ETH Price: $2,065.61 (+0.72%)
 

Overview

ETH Balance

0 ETH

ETH Value

$0.00

Token Holdings

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Block
From
To
Create Sound And...1128032772023-11-28 21:02:11834 days ago1701205331IN
0x5bCF5773...a80b63c55
0 ETH0.0012151011961.50626713
Create Sound And...1128024612023-11-28 20:34:59834 days ago1701203699IN
0x5bCF5773...a80b63c55
0 ETH0.0013232624791.50842601
Create Sound And...1128001862023-11-28 19:19:09834 days ago1701199149IN
0x5bCF5773...a80b63c55
0 ETH0.0012802651231.50745249
Create Sound And...1106770072023-10-10 15:46:31883 days ago1696952791IN
0x5bCF5773...a80b63c55
0 ETH0.000095763010.03540815
Create Sound And...1106651762023-10-10 9:12:09884 days ago1696929129IN
0x5bCF5773...a80b63c55
0 ETH0.0001476145890.11746657
Create Sound And...1106606022023-10-10 6:39:41884 days ago1696919981IN
0x5bCF5773...a80b63c55
0 ETH0.0000528694630.02104022
Create Sound And...1106515162023-10-10 1:36:49884 days ago1696901809IN
0x5bCF5773...a80b63c55
0 ETH0.000073550540.01976836
Create Sound And...1106410282023-10-09 19:47:13884 days ago1696880833IN
0x5bCF5773...a80b63c55
0 ETH0.000070832950.02609887
Create Sound And...1106404512023-10-09 19:27:59884 days ago1696879679IN
0x5bCF5773...a80b63c55
0 ETH0.0000802351890.02186784
Create Sound And...1106381232023-10-09 18:10:23884 days ago1696875023IN
0x5bCF5773...a80b63c55
0 ETH0.0001368108470.02351241
Create Sound And...1106366492023-10-09 17:21:15884 days ago1696872075IN
0x5bCF5773...a80b63c55
0 ETH0.000142497330.02460506
Create Sound And...1106248412023-10-09 10:47:39885 days ago1696848459IN
0x5bCF5773...a80b63c55
0 ETH0.0000707888080.02250917
Create Sound And...1106222892023-10-09 9:22:35885 days ago1696843355IN
0x5bCF5773...a80b63c55
0 ETH0.0000687124760.01988032
Create Sound And...1106201062023-10-09 8:09:49885 days ago1696838989IN
0x5bCF5773...a80b63c55
0 ETH0.0001219390240.15146251
Create Sound And...1106137772023-10-09 4:38:51885 days ago1696826331IN
0x5bCF5773...a80b63c55
0 ETH0.0000774606590.08079332
Create Sound And...1106053932023-10-08 23:59:23885 days ago1696809563IN
0x5bCF5773...a80b63c55
0 ETH0.0000717868140.01898688
Create Sound And...1105923452023-10-08 16:44:27885 days ago1696783467IN
0x5bCF5773...a80b63c55
0 ETH0.0000759710370.01927965
Create Sound And...1105846532023-10-08 12:28:03886 days ago1696768083IN
0x5bCF5773...a80b63c55
0 ETH0.0000833509240.02228973
Create Sound And...1105817492023-10-08 10:51:15886 days ago1696762275IN
0x5bCF5773...a80b63c55
0 ETH0.0000807978560.05245488
Create Sound And...1105540312023-10-07 19:27:19886 days ago1696706839IN
0x5bCF5773...a80b63c55
0 ETH0.0000587868360.02534031
Create Sound And...1105526662023-10-07 18:41:49886 days ago1696704109IN
0x5bCF5773...a80b63c55
0 ETH0.000053676670.01872451
Create Sound And...1105504222023-10-07 17:27:01886 days ago1696699621IN
0x5bCF5773...a80b63c55
0 ETH0.0000679668710.02544506
Create Sound And...1105486992023-10-07 16:29:35886 days ago1696696175IN
0x5bCF5773...a80b63c55
0 ETH0.0000532806420.02723438
Create Sound And...1105459992023-10-07 14:59:35886 days ago1696690775IN
0x5bCF5773...a80b63c55
0 ETH0.0000874976630.01684825
Create Sound And...1105457742023-10-07 14:52:05886 days ago1696690325IN
0x5bCF5773...a80b63c55
0 ETH0.0001551962690.12273271
View all transactions

Latest 25 internal transactions (View All)

Advanced mode:
Parent Transaction Hash Block From To
1128032772023-11-28 21:02:11834 days ago1701205331
0x5bCF5773...a80b63c55
 Contract Creation0 ETH
1128024612023-11-28 20:34:59834 days ago1701203699
0x5bCF5773...a80b63c55
 Contract Creation0 ETH
1128001862023-11-28 19:19:09834 days ago1701199149
0x5bCF5773...a80b63c55
 Contract Creation0 ETH
1106770072023-10-10 15:46:31883 days ago1696952791
0x5bCF5773...a80b63c55
 Contract Creation0 ETH
1106651762023-10-10 9:12:09884 days ago1696929129
0x5bCF5773...a80b63c55
 Contract Creation0 ETH
1106606022023-10-10 6:39:41884 days ago1696919981
0x5bCF5773...a80b63c55
 Contract Creation0 ETH
1106515162023-10-10 1:36:49884 days ago1696901809
0x5bCF5773...a80b63c55
 Contract Creation0 ETH
1106410282023-10-09 19:47:13884 days ago1696880833
0x5bCF5773...a80b63c55
 Contract Creation0 ETH
1106404512023-10-09 19:27:59884 days ago1696879679
0x5bCF5773...a80b63c55
 Contract Creation0 ETH
1106381232023-10-09 18:10:23884 days ago1696875023
0x5bCF5773...a80b63c55
 Contract Creation0 ETH
1106366492023-10-09 17:21:15884 days ago1696872075
0x5bCF5773...a80b63c55
 Contract Creation0 ETH
1106248412023-10-09 10:47:39885 days ago1696848459
0x5bCF5773...a80b63c55
 Contract Creation0 ETH
1106222892023-10-09 9:22:35885 days ago1696843355
0x5bCF5773...a80b63c55
 Contract Creation0 ETH
1106201062023-10-09 8:09:49885 days ago1696838989
0x5bCF5773...a80b63c55
 Contract Creation0 ETH
1106137772023-10-09 4:38:51885 days ago1696826331
0x5bCF5773...a80b63c55
 Contract Creation0 ETH
1106053932023-10-08 23:59:23885 days ago1696809563
0x5bCF5773...a80b63c55
 Contract Creation0 ETH
1105923452023-10-08 16:44:27885 days ago1696783467
0x5bCF5773...a80b63c55
 Contract Creation0 ETH
1105846532023-10-08 12:28:03886 days ago1696768083
0x5bCF5773...a80b63c55
 Contract Creation0 ETH
1105817492023-10-08 10:51:15886 days ago1696762275
0x5bCF5773...a80b63c55
 Contract Creation0 ETH
1105540312023-10-07 19:27:19886 days ago1696706839
0x5bCF5773...a80b63c55
 Contract Creation0 ETH
1105526662023-10-07 18:41:49886 days ago1696704109
0x5bCF5773...a80b63c55
 Contract Creation0 ETH
1105504222023-10-07 17:27:01886 days ago1696699621
0x5bCF5773...a80b63c55
 Contract Creation0 ETH
1105486992023-10-07 16:29:35886 days ago1696696175
0x5bCF5773...a80b63c55
 Contract Creation0 ETH
1105459992023-10-07 14:59:35886 days ago1696690775
0x5bCF5773...a80b63c55
 Contract Creation0 ETH
1105457742023-10-07 14:52:05886 days ago1696690325
0x5bCF5773...a80b63c55
 Contract Creation0 ETH
View All Internal Transactions

Cross-Chain Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
SoundCreatorV1

Compiler Version
v0.8.19+commit.7dd6d404

Optimization Enabled:
Yes with 1000 runs

Other Settings:
default evmVersion, MIT license
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.16;

/*
                 ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
               ▒███████████████████████████████████████████████████████████
               ▒███████████████████████████████████████████████████████████
 ▒▓▓▓▓▓▓▓▓▓▓▓▓▓████████████████▓▓▓▓▓▓▓▓▓▓▓▓▓▓██████████████████████████████▓▒▒▒▒▒▒▒▒▒▒▒▒▒
 █████████████████████████████▓              ████████████████████████████████████████████
 █████████████████████████████▓              ████████████████████████████████████████████
 █████████████████████████████▓               ▒▒▒▒▒▒▒▒▒▒▒▒▒██████████████████████████████
 █████████████████████████████▓                            ▒█████████████████████████████
 █████████████████████████████▓                             ▒████████████████████████████
 █████████████████████████████████████████████████████████▓
 ███████████████████████████████████████████████████████████
 ███████████████████████████████████████████████████████████▒
                              ███████████████████████████████████████████████████████████▒
                              ▓██████████████████████████████████████████████████████████▒
                               ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓███████████████████████████████▒
 █████████████████████████████                             ▒█████████████████████████████▒
 ██████████████████████████████                            ▒█████████████████████████████▒
 ██████████████████████████████▓▒▒▒▒▒▒▒▒▒▒▒▒▒              ▒█████████████████████████████▒
 ████████████████████████████████████████████▒             ▒█████████████████████████████▒
 ████████████████████████████████████████████▒             ▒█████████████████████████████▒
 ▒▒▒▒▒▒▒▒▒▒▒▒▒▒███████████████████████████████▓▓▓▓▓▓▓▓▓▓▓▓▓███████████████▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒
               ▓█████████████████████████████████████████████████████████▒
               ▓██████████████████████████████████████████████████████████
*/

import { Clones } from "openzeppelin/proxy/Clones.sol";

import { ISoundCreatorV1 } from "./interfaces/ISoundCreatorV1.sol";
import { ISoundEditionV1_2 } from "./interfaces/ISoundEditionV1_2.sol";
import { IMetadataModule } from "./interfaces/IMetadataModule.sol";

import { OwnableRoles } from "solady/auth/OwnableRoles.sol";

/**
 * @title SoundCreatorV1
 * @notice A factory that deploys minimal proxies of `SoundEditionV1_2.sol`.
 * @dev The proxies are OpenZeppelin's Clones implementation of https://eips.ethereum.org/EIPS/eip-1167
 */
contract SoundCreatorV1 is ISoundCreatorV1, OwnableRoles {
    // =============================================================
    //                            STORAGE
    // =============================================================

    /**
     * @dev The implementation contract delegated to by Sound edition proxies.
     */
    address public soundEditionImplementation;

    // =============================================================
    //                          CONSTRUCTOR
    // =============================================================

    constructor(address _soundEditionImplementation) implementationNotZero(_soundEditionImplementation) {
        soundEditionImplementation = _soundEditionImplementation;
        _initializeOwner(msg.sender);
    }

    // =============================================================
    //               PUBLIC / EXTERNAL WRITE FUNCTIONS
    // =============================================================

    /**
     * @inheritdoc ISoundCreatorV1
     */
    function createSoundAndMints(
        bytes32 salt,
        bytes calldata initData,
        address[] calldata contracts,
        bytes[] calldata data
    ) external returns (address soundEdition, bytes[] memory results) {
        // Create Sound Edition proxy.
        soundEdition = payable(Clones.cloneDeterministic(soundEditionImplementation, _saltedSalt(msg.sender, salt)));

        // Initialize proxy.
        assembly {
            // Grab the free memory pointer.
            let m := mload(0x40)
            // Copy the `initData` to the free memory.
            calldatacopy(m, initData.offset, initData.length)
            // Call the initializer, and revert if the call fails.
            if iszero(
                call(
                    gas(), // Gas remaining.
                    soundEdition, // Address of the edition.
                    0, // `msg.value` of the call: 0 ETH.
                    m, // Start of input.
                    initData.length, // Length of input.
                    0x00, // Start of output. Not used.
                    0x00 // Size of output. Not used.
                )
            ) {
                // Bubble up the revert if the call reverts.
                returndatacopy(0x00, 0x00, returndatasize())
                revert(0x00, returndatasize())
            }
        }

        results = _callContracts(contracts, data);

        OwnableRoles(soundEdition).transferOwnership(msg.sender);

        emit SoundEditionCreated(soundEdition, msg.sender, initData, contracts, data, results);
    }

    /**
     * @inheritdoc ISoundCreatorV1
     */
    function setEditionImplementation(address newImplementation)
        external
        onlyOwner
        implementationNotZero(newImplementation)
    {
        soundEditionImplementation = newImplementation;

        emit SoundEditionImplementationSet(soundEditionImplementation);
    }

    // =============================================================
    //               PUBLIC / EXTERNAL VIEW FUNCTIONS
    // =============================================================

    /**
     * @inheritdoc ISoundCreatorV1
     */
    function soundEditionAddress(address by, bytes32 salt) external view returns (address addr, bool exists) {
        addr = Clones.predictDeterministicAddress(soundEditionImplementation, _saltedSalt(by, salt), address(this));
        exists = addr.code.length > 0;
    }

    // =============================================================
    //                  INTERNAL / PRIVATE HELPERS
    // =============================================================

    /**
     * @dev Call the `contracts` in order with `data`.
     * @param contracts The addresses of the contracts.
     * @param data      The `abi.encodeWithSelector` calldata for each of the contracts.
     * @return results The results of calling the contracts.
     */
    function _callContracts(address[] calldata contracts, bytes[] calldata data)
        internal
        returns (bytes[] memory results)
    {
        if (contracts.length != data.length) revert ArrayLengthsMismatch();

        assembly {
            // Grab the free memory pointer.
            // We will use the free memory to construct the `results` array,
            // and also as a temporary space for the calldata.
            results := mload(0x40)
            // Set `results.length` to be equal to `data.length`.
            mstore(results, data.length)
            // Skip the first word, which is used to store the length
            let resultsOffsets := add(results, 0x20)
            // Compute the location of the last calldata offset in `data`.
            // `shl(5, n)` is a gas-saving shorthand for `mul(0x20, n)`.
            let dataOffsetsEnd := add(data.offset, shl(5, data.length))
            // This is the start of the unused free memory.
            // We use it to temporarily store the calldata to call the contracts.
            let m := add(resultsOffsets, shl(5, data.length))

            // Loop through `contacts` and `data` together.
            // prettier-ignore
            for { let i := data.offset } iszero(eq(i, dataOffsetsEnd)) { i := add(i, 0x20) } {
                // Location of `bytes[i]` in calldata.
                let o := add(data.offset, calldataload(i))
                // Copy `bytes[i]` from calldata to the free memory.
                calldatacopy(
                    m, // Start of the unused free memory.
                    add(o, 0x20), // Location of starting byte of `data[i]` in calldata.
                    calldataload(o) // The length of the `bytes[i]`.
                )
                // Grab `contracts[i]` from the calldata.
                // As `contracts` is the same length as `data`,
                // `sub(i, data.offset)` gives the relative offset to apply to
                // `contracts.offset` for `contracts[i]` to match `data[i]`.
                let c := calldataload(add(contracts.offset, sub(i, data.offset)))
                // Call the contract, and revert if the call fails.
                if iszero(
                    call(
                        gas(), // Gas remaining.
                        c, // `contracts[i]`.
                        0, // `msg.value` of the call: 0 ETH.
                        m, // Start of the copy of `bytes[i]` in memory.
                        calldataload(o), // The length of the `bytes[i]`.
                        0x00, // Start of output. Not used.
                        0x00 // Size of output. Not used.
                    )
                ) {
                    // Bubble up the revert if the call reverts.
                    returndatacopy(0x00, 0x00, returndatasize())
                    revert(0x00, returndatasize())
                }
                // Append the current `m` into `resultsOffsets`.
                mstore(resultsOffsets, m)
                resultsOffsets := add(resultsOffsets, 0x20)

                // Append the `returndatasize()` to `results`.
                mstore(m, returndatasize())
                // Append the return data to `results`.
                returndatacopy(add(m, 0x20), 0x00, returndatasize())
                // Advance `m` by `returndatasize() + 0x20`,
                // rounded up to the next multiple of 32.
                // `0x3f = 32 + 31`. The mask is `type(uint64).max & ~31`,
                // which is big enough for all purposes (see memory expansion costs).
                m := and(add(add(m, returndatasize()), 0x3f), 0xffffffffffffffe0)
            }
            // Allocate the memory for `results` by updating the free memory pointer.
            mstore(0x40, m)
        }
    }

    /**
     * @dev Returns the salted salt.
     *      To prevent griefing and accidental collisions from clients that don't
     *      generate their salt properly.
     * @param by   The caller of the {createSoundAndMints} function.
     * @param salt The salt, generated on the client side.
     * @return result The computed value.
     */
    function _saltedSalt(address by, bytes32 salt) internal pure returns (bytes32 result) {
        assembly {
            // Store the variables into the scratch space.
            mstore(0x00, by)
            mstore(0x20, salt)
            // Equivalent to `keccak256(abi.encode(by, salt))`.
            result := keccak256(0x00, 0x40)
        }
    }

    /**
     * @dev Reverts if the given implementation address is zero.
     * @param implementation The address of the implementation.
     */
    modifier implementationNotZero(address implementation) {
        if (implementation == address(0)) {
            revert ImplementationAddressCantBeZero();
        }
        _;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (proxy/Clones.sol)

pragma solidity ^0.8.0;

/**
 * @dev https://eips.ethereum.org/EIPS/eip-1167[EIP 1167] is a standard for
 * deploying minimal proxy contracts, also known as "clones".
 *
 * > To simply and cheaply clone contract functionality in an immutable way, this standard specifies
 * > a minimal bytecode implementation that delegates all calls to a known, fixed address.
 *
 * The library includes functions to deploy a proxy using either `create` (traditional deployment) or `create2`
 * (salted deterministic deployment). It also includes functions to predict the addresses of clones deployed using the
 * deterministic method.
 *
 * _Available since v3.4._
 */
library Clones {
    /**
     * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
     *
     * This function uses the create opcode, which should never revert.
     */
    function clone(address implementation) internal returns (address instance) {
        /// @solidity memory-safe-assembly
        assembly {
            let ptr := mload(0x40)
            mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
            mstore(add(ptr, 0x14), shl(0x60, implementation))
            mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)
            instance := create(0, ptr, 0x37)
        }
        require(instance != address(0), "ERC1167: create failed");
    }

    /**
     * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
     *
     * This function uses the create2 opcode and a `salt` to deterministically deploy
     * the clone. Using the same `implementation` and `salt` multiple time will revert, since
     * the clones cannot be deployed twice at the same address.
     */
    function cloneDeterministic(address implementation, bytes32 salt) internal returns (address instance) {
        /// @solidity memory-safe-assembly
        assembly {
            let ptr := mload(0x40)
            mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
            mstore(add(ptr, 0x14), shl(0x60, implementation))
            mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)
            instance := create2(0, ptr, 0x37, salt)
        }
        require(instance != address(0), "ERC1167: create2 failed");
    }

    /**
     * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
     */
    function predictDeterministicAddress(
        address implementation,
        bytes32 salt,
        address deployer
    ) internal pure returns (address predicted) {
        /// @solidity memory-safe-assembly
        assembly {
            let ptr := mload(0x40)
            mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
            mstore(add(ptr, 0x14), shl(0x60, implementation))
            mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf3ff00000000000000000000000000000000)
            mstore(add(ptr, 0x38), shl(0x60, deployer))
            mstore(add(ptr, 0x4c), salt)
            mstore(add(ptr, 0x6c), keccak256(ptr, 0x37))
            predicted := keccak256(add(ptr, 0x37), 0x55)
        }
    }

    /**
     * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
     */
    function predictDeterministicAddress(address implementation, bytes32 salt)
        internal
        view
        returns (address predicted)
    {
        return predictDeterministicAddress(implementation, salt, address(this));
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.16;

import { IMetadataModule } from "./IMetadataModule.sol";

/**
 * @title ISoundCreatorV1
 * @notice The interface for the Sound edition factory.
 */
interface ISoundCreatorV1 {
    // =============================================================
    //                            EVENTS
    // =============================================================

    /**
     * @dev Emitted when an edition is created.
     * @param soundEdition The address of the edition.
     * @param deployer     The address of the deployer.
     * @param initData     The calldata to initialize SoundEdition via `abi.encodeWithSelector`.
     * @param contracts    The list of contracts called.
     * @param data         The list of calldata created via `abi.encodeWithSelector`
     * @param results      The results of calling the contracts. Use `abi.decode` to decode them.
     */
    event SoundEditionCreated(
        address indexed soundEdition,
        address indexed deployer,
        bytes initData,
        address[] contracts,
        bytes[] data,
        bytes[] results
    );

    /**
     * @dev Emitted when the edition implementation address is set.
     * @param newImplementation The new implementation address to be set.
     */
    event SoundEditionImplementationSet(address newImplementation);

    // =============================================================
    //                            ERRORS
    // =============================================================

    /**
     * @dev Thrown if the implementation address is zero.
     */
    error ImplementationAddressCantBeZero();

    /**
     * @dev Thrown if the lengths of the input arrays are not equal.
     */
    error ArrayLengthsMismatch();

    // =============================================================
    //               PUBLIC / EXTERNAL WRITE FUNCTIONS
    // =============================================================

    /**
     * @dev Creates a Sound Edition proxy, initializes it,
     *      and creates mint configurations on a given set of minter addresses.
     * @param salt      The salt used for the CREATE2 to deploy the clone to a
     *                  deterministic address.
     * @param initData  The calldata to initialize SoundEdition via
     *                  `abi.encodeWithSelector`.
     * @param contracts A list of contracts to call.
     * @param data      A list of calldata created via `abi.encodeWithSelector`
     *                  This must contain the same number of entries as `contracts`.
     * @return soundEdition Returns the address of the created contract.
     * @return results      The results of calling the contracts.
     *                      Use `abi.decode` to decode them.
     */
    function createSoundAndMints(
        bytes32 salt,
        bytes calldata initData,
        address[] calldata contracts,
        bytes[] calldata data
    ) external returns (address soundEdition, bytes[] memory results);

    /**
     * @dev Changes the SoundEdition implementation contract address.
     *
     * Calling conditions:
     * - The caller must be the owner of the contract.
     *
     * @param newImplementation The new implementation address to be set.
     */
    function setEditionImplementation(address newImplementation) external;

    // =============================================================
    //               PUBLIC / EXTERNAL VIEW FUNCTIONS
    // =============================================================

    /**
     * @dev The address of the sound edition implementation.
     * @return The configured value.
     */
    function soundEditionImplementation() external returns (address);

    /**
     * @dev Returns the deterministic address for the sound edition clone.
     * @param by   The caller of the {createSoundAndMints} function.
     * @param salt The salt, generated on the client side.
     * @return addr The computed address.
     * @return exists Whether the contract exists.
     */
    function soundEditionAddress(address by, bytes32 salt) external view returns (address addr, bool exists);
}

File 4 of 10 : ISoundEditionV1_2.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.16;

import { IERC721AUpgradeable } from "chiru-labs/ERC721A-Upgradeable/IERC721AUpgradeable.sol";
import { IERC2981Upgradeable } from "openzeppelin-upgradeable/interfaces/IERC2981Upgradeable.sol";
import { IERC165Upgradeable } from "openzeppelin-upgradeable/utils/introspection/IERC165Upgradeable.sol";

import { IMetadataModule } from "./IMetadataModule.sol";

/**
 * @dev The information pertaining to this edition.
 */
struct EditionInfo {
    // Base URI for the tokenId.
    string baseURI;
    // Contract URI for OpenSea storefront.
    string contractURI;
    // Name of the collection.
    string name;
    // Symbol of the collection.
    string symbol;
    // Address that receives primary and secondary royalties.
    address fundingRecipient;
    // The current max mintable amount;
    uint32 editionMaxMintable;
    // The lower limit of the maximum number of tokens that can be minted.
    uint32 editionMaxMintableUpper;
    // The upper limit of the maximum number of tokens that can be minted.
    uint32 editionMaxMintableLower;
    // The timestamp (in seconds since unix epoch) after which the
    // max amount of tokens mintable will drop from
    // `maxMintableUpper` to `maxMintableLower`.
    uint32 editionCutoffTime;
    // Address of metadata module, address(0x00) if not used.
    address metadataModule;
    // The current mint randomness value.
    uint256 mintRandomness;
    // The royalty BPS (basis points).
    uint16 royaltyBPS;
    // Whether the mint randomness is enabled.
    bool mintRandomnessEnabled;
    // Whether the mint has concluded.
    bool mintConcluded;
    // Whether the metadata has been frozen.
    bool isMetadataFrozen;
    // Next token ID to be minted.
    uint256 nextTokenId;
    // Total number of tokens burned.
    uint256 totalBurned;
    // Total number of tokens minted.
    uint256 totalMinted;
    // Total number of tokens currently in existence.
    uint256 totalSupply;
}

/**
 * @title ISoundEditionV1_2
 * @notice The interface for Sound edition contracts.
 */
interface ISoundEditionV1_2 is IERC721AUpgradeable, IERC2981Upgradeable {
    // =============================================================
    //                            EVENTS
    // =============================================================

    /**
     * @dev Emitted when the metadata module is set.
     * @param metadataModule the address of the metadata module.
     */
    event MetadataModuleSet(address metadataModule);

    /**
     * @dev Emitted when the `baseURI` is set.
     * @param baseURI the base URI of the edition.
     */
    event BaseURISet(string baseURI);

    /**
     * @dev Emitted when the `contractURI` is set.
     * @param contractURI The contract URI of the edition.
     */
    event ContractURISet(string contractURI);

    /**
     * @dev Emitted when the metadata is frozen (e.g.: `baseURI` can no longer be changed).
     * @param metadataModule The address of the metadata module.
     * @param baseURI        The base URI of the edition.
     * @param contractURI    The contract URI of the edition.
     */
    event MetadataFrozen(address metadataModule, string baseURI, string contractURI);

    /**
     * @dev Emitted when the `fundingRecipient` is set.
     * @param fundingRecipient The address of the funding recipient.
     */
    event FundingRecipientSet(address fundingRecipient);

    /**
     * @dev Emitted when the `royaltyBPS` is set.
     * @param bps The new royalty, measured in basis points.
     */
    event RoyaltySet(uint16 bps);

    /**
     * @dev Emitted when the edition's maximum mintable token quantity range is set.
     * @param editionMaxMintableLower_ The lower limit of the maximum number of tokens that can be minted.
     * @param editionMaxMintableUpper_ The upper limit of the maximum number of tokens that can be minted.
     */
    event EditionMaxMintableRangeSet(uint32 editionMaxMintableLower_, uint32 editionMaxMintableUpper_);

    /**
     * @dev Emitted when the edition's cutoff time set.
     * @param editionCutoffTime_ The timestamp.
     */
    event EditionCutoffTimeSet(uint32 editionCutoffTime_);

    /**
     * @dev Emitted when the `mintRandomnessEnabled` is set.
     * @param mintRandomnessEnabled_ The boolean value.
     */
    event MintRandomnessEnabledSet(bool mintRandomnessEnabled_);

    /**
     * @dev Emitted when the `operatorFilteringEnabled` is set.
     * @param operatorFilteringEnabled_ The boolean value.
     */
    event OperatorFilteringEnablededSet(bool operatorFilteringEnabled_);

    /**
     * @dev Emitted upon initialization.
     * @param edition_                 The address of the edition.
     * @param name_                    Name of the collection.
     * @param symbol_                  Symbol of the collection.
     * @param metadataModule_          Address of metadata module, address(0x00) if not used.
     * @param baseURI_                 Base URI.
     * @param contractURI_             Contract URI for OpenSea storefront.
     * @param fundingRecipient_        Address that receives primary and secondary royalties.
     * @param royaltyBPS_              Royalty amount in bps (basis points).
     * @param editionMaxMintableLower_ The lower bound of the max mintable quantity for the edition.
     * @param editionMaxMintableUpper_ The upper bound of the max mintable quantity for the edition.
     * @param editionCutoffTime_       The timestamp after which `editionMaxMintable` drops from
     *                                 `editionMaxMintableUpper` to
     *                                 `max(_totalMinted(), editionMaxMintableLower)`.
     * @param flags_                   The bitwise OR result of the initialization flags.
     *                                 See: {METADATA_IS_FROZEN_FLAG}
     *                                 See: {MINT_RANDOMNESS_ENABLED_FLAG}
     */
    event SoundEditionInitialized(
        address indexed edition_,
        string name_,
        string symbol_,
        address metadataModule_,
        string baseURI_,
        string contractURI_,
        address fundingRecipient_,
        uint16 royaltyBPS_,
        uint32 editionMaxMintableLower_,
        uint32 editionMaxMintableUpper_,
        uint32 editionCutoffTime_,
        uint8 flags_
    );

    /**
     * @dev Emitted upon ETH withdrawal.
     * @param recipient The recipient of the withdrawal.
     * @param amount    The amount withdrawn.
     * @param caller    The account that initiated the withdrawal.
     */
    event ETHWithdrawn(address recipient, uint256 amount, address caller);

    /**
     * @dev Emitted upon ERC20 withdrawal.
     * @param recipient The recipient of the withdrawal.
     * @param tokens    The addresses of the ERC20 tokens.
     * @param amounts   The amount of each token withdrawn.
     * @param caller    The account that initiated the withdrawal.
     */
    event ERC20Withdrawn(address recipient, address[] tokens, uint256[] amounts, address caller);

    /**
     * @dev Emitted upon a mint.
     * @param to          The address to mint to.
     * @param quantity    The number of minted.
     * @param fromTokenId The first token ID minted.
     */
    event Minted(address to, uint256 quantity, uint256 fromTokenId);

    /**
     * @dev Emitted upon an airdrop.
     * @param to          The recipients of the airdrop.
     * @param quantity    The number of tokens airdropped to each address in `to`.
     * @param fromTokenId The first token ID minted to the first address in `to`.
     */
    event Airdropped(address[] to, uint256 quantity, uint256 fromTokenId);

    /**
     * @dev EIP-4906 event to signal marketplaces to refresh the metadata.
     * @param fromTokenId The starting token ID.
     * @param toTokenId   The ending token ID.
     */
    event BatchMetadataUpdate(uint256 fromTokenId, uint256 toTokenId);

    /**
     * @dev Emiited when the Sound Automated Market (i.e. bonding curve minter) is set.
     * @param sam_ The Sound Automated Market.
     */
    event SAMSet(address sam_);

    // =============================================================
    //                            ERRORS
    // =============================================================

    /**
     * @dev The edition's metadata is frozen (e.g.: `baseURI` can no longer be changed).
     */
    error MetadataIsFrozen();

    /**
     * @dev The given `royaltyBPS` is invalid.
     */
    error InvalidRoyaltyBPS();

    /**
     * @dev The given `randomnessLockedAfterMinted` value is invalid.
     */
    error InvalidRandomnessLock();

    /**
     * @dev The requested quantity exceeds the edition's remaining mintable token quantity.
     * @param available The number of tokens remaining available for mint.
     */
    error ExceedsEditionAvailableSupply(uint32 available);

    /**
     * @dev The given amount is invalid.
     */
    error InvalidAmount();

    /**
     * @dev The given `fundingRecipient` address is invalid.
     */
    error InvalidFundingRecipient();

    /**
     * @dev The `editionMaxMintableLower` must not be greater than `editionMaxMintableUpper`.
     */
    error InvalidEditionMaxMintableRange();

    /**
     * @dev The `editionMaxMintable` has already been reached.
     */
    error MaximumHasAlreadyBeenReached();

    /**
     * @dev The mint `quantity` cannot exceed `ADDRESS_BATCH_MINT_LIMIT` tokens.
     */
    error ExceedsAddressBatchMintLimit();

    /**
     * @dev The mint randomness has already been revealed.
     */
    error MintRandomnessAlreadyRevealed();

    /**
     * @dev No addresses to airdrop.
     */
    error NoAddressesToAirdrop();

    /**
     * @dev The mint has already concluded.
     */
    error MintHasConcluded();

    /**
     * @dev The mint has not concluded.
     */
    error MintNotConcluded();

    /**
     * @dev Cannot perform the operation after a token has been minted.
     */
    error MintsAlreadyExist();

    /**
     * @dev The token IDs must be in strictly ascending order.
     */
    error TokenIdsNotStrictlyAscending();

    /**
     * @dev Please wait for a while before you burn.
     */
    error CannotBurnImmediately();

    // =============================================================
    //               PUBLIC / EXTERNAL WRITE FUNCTIONS
    // =============================================================

    /**
     * @dev Initializes the contract.
     * @param name_                    Name of the collection.
     * @param symbol_                  Symbol of the collection.
     * @param metadataModule_          Address of metadata module, address(0x00) if not used.
     * @param baseURI_                 Base URI.
     * @param contractURI_             Contract URI for OpenSea storefront.
     * @param fundingRecipient_        Address that receives primary and secondary royalties.
     * @param royaltyBPS_              Royalty amount in bps (basis points).
     * @param editionMaxMintableLower_ The lower bound of the max mintable quantity for the edition.
     * @param editionMaxMintableUpper_ The upper bound of the max mintable quantity for the edition.
     * @param editionCutoffTime_       The timestamp after which `editionMaxMintable` drops from
     *                                 `editionMaxMintableUpper` to
     *                                 `max(_totalMinted(), editionMaxMintableLower)`.
     * @param flags_                   The bitwise OR result of the initialization flags.
     *                                 See: {METADATA_IS_FROZEN_FLAG}
     *                                 See: {MINT_RANDOMNESS_ENABLED_FLAG}
     */
    function initialize(
        string memory name_,
        string memory symbol_,
        address metadataModule_,
        string memory baseURI_,
        string memory contractURI_,
        address fundingRecipient_,
        uint16 royaltyBPS_,
        uint32 editionMaxMintableLower_,
        uint32 editionMaxMintableUpper_,
        uint32 editionCutoffTime_,
        uint8 flags_
    ) external;

    /**
     * @dev Mints `quantity` tokens to addrress `to`
     *      Each token will be assigned a token ID that is consecutively increasing.
     *
     * Calling conditions:
     * - The caller must be the owner of the contract, or have either the
     *   `ADMIN_ROLE`, `MINTER_ROLE`, which can be granted via {grantRole}.
     *   Multiple minters, such as different minter contracts,
     *   can be authorized simultaneously.
     *
     * @param to       Address to mint to.
     * @param quantity Number of tokens to mint.
     * @return fromTokenId The first token ID minted.
     */
    function mint(address to, uint256 quantity) external payable returns (uint256 fromTokenId);

    /**
     * @dev Mints `quantity` tokens to each of the addresses in `to`.
     *
     * Calling conditions:
     * - The caller must be the owner of the contract, or have the
     *   `ADMIN_ROLE`, which can be granted via {grantRole}.
     *
     * @param to           Address to mint to.
     * @param quantity     Number of tokens to mint.
     * @return fromTokenId The first token ID minted.
     */
    function airdrop(address[] calldata to, uint256 quantity) external returns (uint256 fromTokenId);

    /**
     * @dev Mints `quantity` tokens to addrress `to`
     *      Each token will be assigned a token ID that is consecutively increasing.
     *
     * Calling conditions:
     * - The caller must be the bonding curve contract.
     *
     * @param to       Address to mint to.
     * @param quantity Number of tokens to mint.
     * @return fromTokenId The first token ID minted.
     */
    function samMint(address to, uint256 quantity) external payable returns (uint256 fromTokenId);

    /**
     * @dev Burns the `tokenIds`.
     *
     * Calling conditions:
     * - The caller must be the bonding curve contract.
     *
     * @param burner   The initiator of the burn.
     * @param tokenIds The list of token IDs to burn.
     */
    function samBurn(address burner, uint256[] calldata tokenIds) external;

    /**
     * @dev Withdraws collected ETH royalties to the fundingRecipient.
     */
    function withdrawETH() external;

    /**
     * @dev Withdraws collected ERC20 royalties to the fundingRecipient.
     * @param tokens array of ERC20 tokens to withdraw
     */
    function withdrawERC20(address[] calldata tokens) external;

    /**
     * @dev Sets metadata module.
     *
     * Calling conditions:
     * - The caller must be the owner of the contract, or have the `ADMIN_ROLE`.
     *
     * @param metadataModule Address of metadata module.
     */
    function setMetadataModule(address metadataModule) external;

    /**
     * @dev Sets global base URI.
     *
     * Calling conditions:
     * - The caller must be the owner of the contract, or have the `ADMIN_ROLE`.
     *
     * @param baseURI The base URI to be set.
     */
    function setBaseURI(string memory baseURI) external;

    /**
     * @dev Sets contract URI.
     *
     * Calling conditions:
     * - The caller must be the owner of the contract, or have the `ADMIN_ROLE`.
     *
     * @param contractURI The contract URI to be set.
     */
    function setContractURI(string memory contractURI) external;

    /**
     * @dev Freezes metadata by preventing any more changes to base URI.
     *
     * Calling conditions:
     * - The caller must be the owner of the contract, or have the `ADMIN_ROLE`.
     */
    function freezeMetadata() external;

    /**
     * @dev Sets funding recipient address.
     *
     * Calling conditions:
     * - The caller must be the owner of the contract, or have the `ADMIN_ROLE`.
     *
     * @param fundingRecipient Address to be set as the new funding recipient.
     */
    function setFundingRecipient(address fundingRecipient) external;

    /**
     * @dev Sets royalty amount in bps (basis points).
     *
     * Calling conditions:
     * - The caller must be the owner of the contract, or have the `ADMIN_ROLE`.
     *
     * @param bps The new royalty basis points to be set.
     */
    function setRoyalty(uint16 bps) external;

    /**
     * @dev Sets the edition max mintable range.
     *
     * Calling conditions:
     * - The caller must be the owner of the contract, or have the `ADMIN_ROLE`.
     *
     * @param editionMaxMintableLower_ The lower limit of the maximum number of tokens that can be minted.
     * @param editionMaxMintableUpper_ The upper limit of the maximum number of tokens that can be minted.
     */
    function setEditionMaxMintableRange(uint32 editionMaxMintableLower_, uint32 editionMaxMintableUpper_) external;

    /**
     * @dev Sets the timestamp after which, the `editionMaxMintable` drops
     *      from `editionMaxMintableUpper` to `editionMaxMintableLower.
     *
     * Calling conditions:
     * - The caller must be the owner of the contract, or have the `ADMIN_ROLE`.
     *
     * @param editionCutoffTime_ The timestamp.
     */
    function setEditionCutoffTime(uint32 editionCutoffTime_) external;

    /**
     * @dev Sets whether the `mintRandomness` is enabled.
     *
     * Calling conditions:
     * - The caller must be the owner of the contract, or have the `ADMIN_ROLE`.
     *
     * @param mintRandomnessEnabled_ The boolean value.
     */
    function setMintRandomnessEnabled(bool mintRandomnessEnabled_) external;

    /**
     * @dev Sets whether OpenSea operator filtering is enabled.
     *
     * Calling conditions:
     * - The caller must be the owner of the contract, or have the `ADMIN_ROLE`.
     *
     * @param operatorFilteringEnabled_ The boolean value.
     */
    function setOperatorFilteringEnabled(bool operatorFilteringEnabled_) external;

    /**
     * @dev Emits an event to signal to marketplaces to refresh all the metadata.
     */
    function emitAllMetadataUpdate() external;

    /**
     * @dev Sets the Sound Automated Market (i.e. bonding curve minter).
     *
     * Calling conditions:
     * - The caller must be the owner of the contract, or have the `ADMIN_ROLE`.
     *
     * @param sam_ The Sound Automated Market.
     */
    function setSAM(address sam_) external;

    // =============================================================
    //               PUBLIC / EXTERNAL VIEW FUNCTIONS
    // =============================================================

    /**
     * @dev Returns the edition info.
     * @return editionInfo The latest value.
     */
    function editionInfo() external view returns (EditionInfo memory editionInfo);

    /**
     * @dev Returns the minter role flag.
     * @return The constant value.
     */
    function MINTER_ROLE() external view returns (uint256);

    /**
     * @dev Returns the admin role flag.
     * @return The constant value.
     */
    function ADMIN_ROLE() external view returns (uint256);

    /**
     * @dev Returns the bit flag to freeze the metadata on initialization.
     * @return The constant value.
     */
    function METADATA_IS_FROZEN_FLAG() external pure returns (uint8);

    /**
     * @dev Returns the bit flag to enable the mint randomness feature on initialization.
     * @return The constant value.
     */
    function MINT_RANDOMNESS_ENABLED_FLAG() external pure returns (uint8);

    /**
     * @dev Returns the bit flag to enable OpenSea operator filtering.
     * @return The constant value.
     */
    function OPERATOR_FILTERING_ENABLED_FLAG() external pure returns (uint8);

    /**
     * @dev Returns the base token URI for the collection.
     * @return The configured value.
     */
    function baseURI() external view returns (string memory);

    /**
     * @dev Returns the contract URI to be used by Opensea.
     *      See: https://docs.opensea.io/docs/contract-level-metadata
     * @return The configured value.
     */
    function contractURI() external view returns (string memory);

    /**
     * @dev Returns the address of the funding recipient.
     * @return The configured value.
     */
    function fundingRecipient() external view returns (address);

    /**
     * @dev Returns the maximum amount of tokens mintable for this edition.
     * @return The configured value.
     */
    function editionMaxMintable() external view returns (uint32);

    /**
     * @dev Returns the upper bound for the maximum tokens that can be minted for this edition.
     * @return The configured value.
     */
    function editionMaxMintableUpper() external view returns (uint32);

    /**
     * @dev Returns the lower bound for the maximum tokens that can be minted for this edition.
     * @return The configured value.
     */
    function editionMaxMintableLower() external view returns (uint32);

    /**
     * @dev Returns the timestamp after which `editionMaxMintable` drops from
     *      `editionMaxMintableUpper` to `editionMaxMintableLower`.
     * @return The configured value.
     */
    function editionCutoffTime() external view returns (uint32);

    /**
     * @dev Returns the address of the metadata module.
     * @return The configured value.
     */
    function metadataModule() external view returns (address);

    /**
     * @dev Returns the randomness based on latest block hash, which is stored upon each mint.
     *      unless {mintConcluded} is true.
     *      Used for game mechanics like the Sound Golden Egg.
     *      Returns 0 before revealed.
     *      WARNING: This value should NOT be used for any reward of significant monetary
     *      value, due to it being computed via a purely on-chain psuedorandom mechanism.
     * @return The latest value.
     */
    function mintRandomness() external view returns (uint256);

    /**
     * @dev Returns whether the `mintRandomness` has been enabled.
     * @return The configured value.
     */
    function mintRandomnessEnabled() external view returns (bool);

    /**
     * @dev Returns whether the `operatorFilteringEnabled` has been enabled.
     * @return The configured value.
     */
    function operatorFilteringEnabled() external view returns (bool);

    /**
     * @dev Returns whether the mint has been concluded.
     * @return The latest value.
     */
    function mintConcluded() external view returns (bool);

    /**
     * @dev Returns the royalty basis points.
     * @return The configured value.
     */
    function royaltyBPS() external view returns (uint16);

    /**
     * @dev Returns whether the metadata module is frozen.
     * @return The configured value.
     */
    function isMetadataFrozen() external view returns (bool);

    /**
     * @dev Returns the sound automated market, if any.
     * @return The configured value.
     */
    function sam() external view returns (address);

    /**
     * @dev Returns the next token ID to be minted.
     * @return The latest value.
     */
    function nextTokenId() external view returns (uint256);

    /**
     * @dev Returns the number of tokens minted by `owner`.
     * @param owner Address to query for number minted.
     * @return The latest value.
     */
    function numberMinted(address owner) external view returns (uint256);

    /**
     * @dev Returns the number of tokens burned by `owner`.
     * @param owner Address to query for number burned.
     * @return The latest value.
     */
    function numberBurned(address owner) external view returns (uint256);

    /**
     * @dev Returns the total amount of tokens minted.
     * @return The latest value.
     */
    function totalMinted() external view returns (uint256);

    /**
     * @dev Returns the total amount of tokens burned.
     * @return The latest value.
     */
    function totalBurned() external view returns (uint256);

    /**
     * @dev Informs other contracts which interfaces this contract supports.
     *      Required by https://eips.ethereum.org/EIPS/eip-165
     * @param interfaceId The interface id to check.
     * @return Whether the `interfaceId` is supported.
     */
    function supportsInterface(bytes4 interfaceId)
        external
        view
        override(IERC721AUpgradeable, IERC165Upgradeable)
        returns (bool);
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.16;

/**
 * @title IMetadataModule
 * @notice The interface for custom metadata modules.
 */
interface IMetadataModule {
    /**
     * @dev When implemented, SoundEdition's `tokenURI` redirects execution to this `tokenURI`.
     * @param tokenId The token ID to retrieve the token URI for.
     * @return The token URI string.
     */
    function tokenURI(uint256 tokenId) external view returns (string memory);
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import "./Ownable.sol";

/// @notice Simple single owner and multiroles authorization mixin.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/auth/Ownable.sol)
/// @dev While the ownable portion follows [EIP-173](https://eips.ethereum.org/EIPS/eip-173)
/// for compatibility, the nomenclature for the 2-step ownership handover and roles
/// may be unique to this codebase.
abstract contract OwnableRoles is Ownable {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev `bytes4(keccak256(bytes("Unauthorized()")))`.
    uint256 private constant _UNAUTHORIZED_ERROR_SELECTOR = 0x82b42900;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                           EVENTS                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The `user`'s roles is updated to `roles`.
    /// Each bit of `roles` represents whether the role is set.
    event RolesUpdated(address indexed user, uint256 indexed roles);

    /// @dev `keccak256(bytes("RolesUpdated(address,uint256)"))`.
    uint256 private constant _ROLES_UPDATED_EVENT_SIGNATURE =
        0x715ad5ce61fc9595c7b415289d59cf203f23a94fa06f04af7e489a0a76e1fe26;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                          STORAGE                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The role slot of `user` is given by:
    /// ```
    ///     mstore(0x00, or(shl(96, user), _ROLE_SLOT_SEED))
    ///     let roleSlot := keccak256(0x00, 0x20)
    /// ```
    /// This automatically ignores the upper bits of the `user` in case
    /// they are not clean, as well as keep the `keccak256` under 32-bytes.
    ///
    /// Note: This is equal to `_OWNER_SLOT_NOT` in for gas efficiency.
    uint256 private constant _ROLE_SLOT_SEED = 0x8b78c6d8;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                     INTERNAL FUNCTIONS                     */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Grants the roles directly without authorization guard.
    /// Each bit of `roles` represents the role to turn on.
    function _grantRoles(address user, uint256 roles) internal virtual {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute the role slot.
            mstore(0x0c, _ROLE_SLOT_SEED)
            mstore(0x00, user)
            let roleSlot := keccak256(0x0c, 0x20)
            // Load the current value and `or` it with `roles`.
            roles := or(sload(roleSlot), roles)
            // Store the new value.
            sstore(roleSlot, roles)
            // Emit the {RolesUpdated} event.
            log3(0, 0, _ROLES_UPDATED_EVENT_SIGNATURE, shr(96, mload(0x0c)), roles)
        }
    }

    /// @dev Removes the roles directly without authorization guard.
    /// Each bit of `roles` represents the role to turn off.
    function _removeRoles(address user, uint256 roles) internal virtual {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute the role slot.
            mstore(0x0c, _ROLE_SLOT_SEED)
            mstore(0x00, user)
            let roleSlot := keccak256(0x0c, 0x20)
            // Load the current value.
            let currentRoles := sload(roleSlot)
            // Use `and` to compute the intersection of `currentRoles` and `roles`,
            // `xor` it with `currentRoles` to flip the bits in the intersection.
            roles := xor(currentRoles, and(currentRoles, roles))
            // Then, store the new value.
            sstore(roleSlot, roles)
            // Emit the {RolesUpdated} event.
            log3(0, 0, _ROLES_UPDATED_EVENT_SIGNATURE, shr(96, mload(0x0c)), roles)
        }
    }

    /// @dev Throws if the sender does not have any of the `roles`.
    function _checkRoles(uint256 roles) internal view virtual {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute the role slot.
            mstore(0x0c, _ROLE_SLOT_SEED)
            mstore(0x00, caller())
            // Load the stored value, and if the `and` intersection
            // of the value and `roles` is zero, revert.
            if iszero(and(sload(keccak256(0x0c, 0x20)), roles)) {
                mstore(0x00, _UNAUTHORIZED_ERROR_SELECTOR)
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Throws if the sender is not the owner,
    /// and does not have any of the `roles`.
    /// Checks for ownership first, then lazily checks for roles.
    function _checkOwnerOrRoles(uint256 roles) internal view virtual {
        /// @solidity memory-safe-assembly
        assembly {
            // If the caller is not the stored owner.
            // Note: `_ROLE_SLOT_SEED` is equal to `_OWNER_SLOT_NOT`.
            if iszero(eq(caller(), sload(not(_ROLE_SLOT_SEED)))) {
                // Compute the role slot.
                mstore(0x0c, _ROLE_SLOT_SEED)
                mstore(0x00, caller())
                // Load the stored value, and if the `and` intersection
                // of the value and `roles` is zero, revert.
                if iszero(and(sload(keccak256(0x0c, 0x20)), roles)) {
                    mstore(0x00, _UNAUTHORIZED_ERROR_SELECTOR)
                    revert(0x1c, 0x04)
                }
            }
        }
    }

    /// @dev Throws if the sender does not have any of the `roles`,
    /// and is not the owner.
    /// Checks for roles first, then lazily checks for ownership.
    function _checkRolesOrOwner(uint256 roles) internal view virtual {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute the role slot.
            mstore(0x0c, _ROLE_SLOT_SEED)
            mstore(0x00, caller())
            // Load the stored value, and if the `and` intersection
            // of the value and `roles` is zero, revert.
            if iszero(and(sload(keccak256(0x0c, 0x20)), roles)) {
                // If the caller is not the stored owner.
                // Note: `_ROLE_SLOT_SEED` is equal to `_OWNER_SLOT_NOT`.
                if iszero(eq(caller(), sload(not(_ROLE_SLOT_SEED)))) {
                    mstore(0x00, _UNAUTHORIZED_ERROR_SELECTOR)
                    revert(0x1c, 0x04)
                }
            }
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                  PUBLIC UPDATE FUNCTIONS                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Allows the owner to grant `user` `roles`.
    /// If the `user` already has a role, then it will be an no-op for the role.
    function grantRoles(address user, uint256 roles) public payable virtual onlyOwner {
        _grantRoles(user, roles);
    }

    /// @dev Allows the owner to remove `user` `roles`.
    /// If the `user` does not have a role, then it will be an no-op for the role.
    function revokeRoles(address user, uint256 roles) public payable virtual onlyOwner {
        _removeRoles(user, roles);
    }

    /// @dev Allow the caller to remove their own roles.
    /// If the caller does not have a role, then it will be an no-op for the role.
    function renounceRoles(uint256 roles) public payable virtual {
        _removeRoles(msg.sender, roles);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                   PUBLIC READ FUNCTIONS                    */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns whether `user` has any of `roles`.
    function hasAnyRole(address user, uint256 roles) public view virtual returns (bool result) {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute the role slot.
            mstore(0x0c, _ROLE_SLOT_SEED)
            mstore(0x00, user)
            // Load the stored value, and set the result to whether the
            // `and` intersection of the value and `roles` is not zero.
            result := iszero(iszero(and(sload(keccak256(0x0c, 0x20)), roles)))
        }
    }

    /// @dev Returns whether `user` has all of `roles`.
    function hasAllRoles(address user, uint256 roles) public view virtual returns (bool result) {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute the role slot.
            mstore(0x0c, _ROLE_SLOT_SEED)
            mstore(0x00, user)
            // Whether the stored value is contains all the set bits in `roles`.
            result := eq(and(sload(keccak256(0x0c, 0x20)), roles), roles)
        }
    }

    /// @dev Returns the roles of `user`.
    function rolesOf(address user) public view virtual returns (uint256 roles) {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute the role slot.
            mstore(0x0c, _ROLE_SLOT_SEED)
            mstore(0x00, user)
            // Load the stored value.
            roles := sload(keccak256(0x0c, 0x20))
        }
    }

    /// @dev Convenience function to return a `roles` bitmap from an array of `ordinals`.
    /// This is meant for frontends like Etherscan, and is therefore not fully optimized.
    /// Not recommended to be called on-chain.
    function rolesFromOrdinals(uint8[] memory ordinals) public pure returns (uint256 roles) {
        /// @solidity memory-safe-assembly
        assembly {
            for { let i := shl(5, mload(ordinals)) } i { i := sub(i, 0x20) } {
                // We don't need to mask the values of `ordinals`, as Solidity
                // cleans dirty upper bits when storing variables into memory.
                roles := or(shl(mload(add(ordinals, i)), 1), roles)
            }
        }
    }

    /// @dev Convenience function to return an array of `ordinals` from the `roles` bitmap.
    /// This is meant for frontends like Etherscan, and is therefore not fully optimized.
    /// Not recommended to be called on-chain.
    function ordinalsFromRoles(uint256 roles) public pure returns (uint8[] memory ordinals) {
        /// @solidity memory-safe-assembly
        assembly {
            // Grab the pointer to the free memory.
            ordinals := mload(0x40)
            let ptr := add(ordinals, 0x20)
            let o := 0
            // The absence of lookup tables, De Bruijn, etc., here is intentional for
            // smaller bytecode, as this function is not meant to be called on-chain.
            for { let t := roles } 1 {} {
                mstore(ptr, o)
                // `shr` 5 is equivalent to multiplying by 0x20.
                // Push back into the ordinals array if the bit is set.
                ptr := add(ptr, shl(5, and(t, 1)))
                o := add(o, 1)
                t := shr(o, roles)
                if iszero(t) { break }
            }
            // Store the length of `ordinals`.
            mstore(ordinals, shr(5, sub(ptr, add(ordinals, 0x20))))
            // Allocate the memory.
            mstore(0x40, ptr)
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         MODIFIERS                          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Marks a function as only callable by an account with `roles`.
    modifier onlyRoles(uint256 roles) virtual {
        _checkRoles(roles);
        _;
    }

    /// @dev Marks a function as only callable by the owner or by an account
    /// with `roles`. Checks for ownership first, then lazily checks for roles.
    modifier onlyOwnerOrRoles(uint256 roles) virtual {
        _checkOwnerOrRoles(roles);
        _;
    }

    /// @dev Marks a function as only callable by an account with `roles`
    /// or the owner. Checks for roles first, then lazily checks for ownership.
    modifier onlyRolesOrOwner(uint256 roles) virtual {
        _checkRolesOrOwner(roles);
        _;
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       ROLE CONSTANTS                       */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    // IYKYK

    uint256 internal constant _ROLE_0 = 1 << 0;
    uint256 internal constant _ROLE_1 = 1 << 1;
    uint256 internal constant _ROLE_2 = 1 << 2;
    uint256 internal constant _ROLE_3 = 1 << 3;
    uint256 internal constant _ROLE_4 = 1 << 4;
    uint256 internal constant _ROLE_5 = 1 << 5;
    uint256 internal constant _ROLE_6 = 1 << 6;
    uint256 internal constant _ROLE_7 = 1 << 7;
    uint256 internal constant _ROLE_8 = 1 << 8;
    uint256 internal constant _ROLE_9 = 1 << 9;
    uint256 internal constant _ROLE_10 = 1 << 10;
    uint256 internal constant _ROLE_11 = 1 << 11;
    uint256 internal constant _ROLE_12 = 1 << 12;
    uint256 internal constant _ROLE_13 = 1 << 13;
    uint256 internal constant _ROLE_14 = 1 << 14;
    uint256 internal constant _ROLE_15 = 1 << 15;
    uint256 internal constant _ROLE_16 = 1 << 16;
    uint256 internal constant _ROLE_17 = 1 << 17;
    uint256 internal constant _ROLE_18 = 1 << 18;
    uint256 internal constant _ROLE_19 = 1 << 19;
    uint256 internal constant _ROLE_20 = 1 << 20;
    uint256 internal constant _ROLE_21 = 1 << 21;
    uint256 internal constant _ROLE_22 = 1 << 22;
    uint256 internal constant _ROLE_23 = 1 << 23;
    uint256 internal constant _ROLE_24 = 1 << 24;
    uint256 internal constant _ROLE_25 = 1 << 25;
    uint256 internal constant _ROLE_26 = 1 << 26;
    uint256 internal constant _ROLE_27 = 1 << 27;
    uint256 internal constant _ROLE_28 = 1 << 28;
    uint256 internal constant _ROLE_29 = 1 << 29;
    uint256 internal constant _ROLE_30 = 1 << 30;
    uint256 internal constant _ROLE_31 = 1 << 31;
    uint256 internal constant _ROLE_32 = 1 << 32;
    uint256 internal constant _ROLE_33 = 1 << 33;
    uint256 internal constant _ROLE_34 = 1 << 34;
    uint256 internal constant _ROLE_35 = 1 << 35;
    uint256 internal constant _ROLE_36 = 1 << 36;
    uint256 internal constant _ROLE_37 = 1 << 37;
    uint256 internal constant _ROLE_38 = 1 << 38;
    uint256 internal constant _ROLE_39 = 1 << 39;
    uint256 internal constant _ROLE_40 = 1 << 40;
    uint256 internal constant _ROLE_41 = 1 << 41;
    uint256 internal constant _ROLE_42 = 1 << 42;
    uint256 internal constant _ROLE_43 = 1 << 43;
    uint256 internal constant _ROLE_44 = 1 << 44;
    uint256 internal constant _ROLE_45 = 1 << 45;
    uint256 internal constant _ROLE_46 = 1 << 46;
    uint256 internal constant _ROLE_47 = 1 << 47;
    uint256 internal constant _ROLE_48 = 1 << 48;
    uint256 internal constant _ROLE_49 = 1 << 49;
    uint256 internal constant _ROLE_50 = 1 << 50;
    uint256 internal constant _ROLE_51 = 1 << 51;
    uint256 internal constant _ROLE_52 = 1 << 52;
    uint256 internal constant _ROLE_53 = 1 << 53;
    uint256 internal constant _ROLE_54 = 1 << 54;
    uint256 internal constant _ROLE_55 = 1 << 55;
    uint256 internal constant _ROLE_56 = 1 << 56;
    uint256 internal constant _ROLE_57 = 1 << 57;
    uint256 internal constant _ROLE_58 = 1 << 58;
    uint256 internal constant _ROLE_59 = 1 << 59;
    uint256 internal constant _ROLE_60 = 1 << 60;
    uint256 internal constant _ROLE_61 = 1 << 61;
    uint256 internal constant _ROLE_62 = 1 << 62;
    uint256 internal constant _ROLE_63 = 1 << 63;
    uint256 internal constant _ROLE_64 = 1 << 64;
    uint256 internal constant _ROLE_65 = 1 << 65;
    uint256 internal constant _ROLE_66 = 1 << 66;
    uint256 internal constant _ROLE_67 = 1 << 67;
    uint256 internal constant _ROLE_68 = 1 << 68;
    uint256 internal constant _ROLE_69 = 1 << 69;
    uint256 internal constant _ROLE_70 = 1 << 70;
    uint256 internal constant _ROLE_71 = 1 << 71;
    uint256 internal constant _ROLE_72 = 1 << 72;
    uint256 internal constant _ROLE_73 = 1 << 73;
    uint256 internal constant _ROLE_74 = 1 << 74;
    uint256 internal constant _ROLE_75 = 1 << 75;
    uint256 internal constant _ROLE_76 = 1 << 76;
    uint256 internal constant _ROLE_77 = 1 << 77;
    uint256 internal constant _ROLE_78 = 1 << 78;
    uint256 internal constant _ROLE_79 = 1 << 79;
    uint256 internal constant _ROLE_80 = 1 << 80;
    uint256 internal constant _ROLE_81 = 1 << 81;
    uint256 internal constant _ROLE_82 = 1 << 82;
    uint256 internal constant _ROLE_83 = 1 << 83;
    uint256 internal constant _ROLE_84 = 1 << 84;
    uint256 internal constant _ROLE_85 = 1 << 85;
    uint256 internal constant _ROLE_86 = 1 << 86;
    uint256 internal constant _ROLE_87 = 1 << 87;
    uint256 internal constant _ROLE_88 = 1 << 88;
    uint256 internal constant _ROLE_89 = 1 << 89;
    uint256 internal constant _ROLE_90 = 1 << 90;
    uint256 internal constant _ROLE_91 = 1 << 91;
    uint256 internal constant _ROLE_92 = 1 << 92;
    uint256 internal constant _ROLE_93 = 1 << 93;
    uint256 internal constant _ROLE_94 = 1 << 94;
    uint256 internal constant _ROLE_95 = 1 << 95;
    uint256 internal constant _ROLE_96 = 1 << 96;
    uint256 internal constant _ROLE_97 = 1 << 97;
    uint256 internal constant _ROLE_98 = 1 << 98;
    uint256 internal constant _ROLE_99 = 1 << 99;
    uint256 internal constant _ROLE_100 = 1 << 100;
    uint256 internal constant _ROLE_101 = 1 << 101;
    uint256 internal constant _ROLE_102 = 1 << 102;
    uint256 internal constant _ROLE_103 = 1 << 103;
    uint256 internal constant _ROLE_104 = 1 << 104;
    uint256 internal constant _ROLE_105 = 1 << 105;
    uint256 internal constant _ROLE_106 = 1 << 106;
    uint256 internal constant _ROLE_107 = 1 << 107;
    uint256 internal constant _ROLE_108 = 1 << 108;
    uint256 internal constant _ROLE_109 = 1 << 109;
    uint256 internal constant _ROLE_110 = 1 << 110;
    uint256 internal constant _ROLE_111 = 1 << 111;
    uint256 internal constant _ROLE_112 = 1 << 112;
    uint256 internal constant _ROLE_113 = 1 << 113;
    uint256 internal constant _ROLE_114 = 1 << 114;
    uint256 internal constant _ROLE_115 = 1 << 115;
    uint256 internal constant _ROLE_116 = 1 << 116;
    uint256 internal constant _ROLE_117 = 1 << 117;
    uint256 internal constant _ROLE_118 = 1 << 118;
    uint256 internal constant _ROLE_119 = 1 << 119;
    uint256 internal constant _ROLE_120 = 1 << 120;
    uint256 internal constant _ROLE_121 = 1 << 121;
    uint256 internal constant _ROLE_122 = 1 << 122;
    uint256 internal constant _ROLE_123 = 1 << 123;
    uint256 internal constant _ROLE_124 = 1 << 124;
    uint256 internal constant _ROLE_125 = 1 << 125;
    uint256 internal constant _ROLE_126 = 1 << 126;
    uint256 internal constant _ROLE_127 = 1 << 127;
    uint256 internal constant _ROLE_128 = 1 << 128;
    uint256 internal constant _ROLE_129 = 1 << 129;
    uint256 internal constant _ROLE_130 = 1 << 130;
    uint256 internal constant _ROLE_131 = 1 << 131;
    uint256 internal constant _ROLE_132 = 1 << 132;
    uint256 internal constant _ROLE_133 = 1 << 133;
    uint256 internal constant _ROLE_134 = 1 << 134;
    uint256 internal constant _ROLE_135 = 1 << 135;
    uint256 internal constant _ROLE_136 = 1 << 136;
    uint256 internal constant _ROLE_137 = 1 << 137;
    uint256 internal constant _ROLE_138 = 1 << 138;
    uint256 internal constant _ROLE_139 = 1 << 139;
    uint256 internal constant _ROLE_140 = 1 << 140;
    uint256 internal constant _ROLE_141 = 1 << 141;
    uint256 internal constant _ROLE_142 = 1 << 142;
    uint256 internal constant _ROLE_143 = 1 << 143;
    uint256 internal constant _ROLE_144 = 1 << 144;
    uint256 internal constant _ROLE_145 = 1 << 145;
    uint256 internal constant _ROLE_146 = 1 << 146;
    uint256 internal constant _ROLE_147 = 1 << 147;
    uint256 internal constant _ROLE_148 = 1 << 148;
    uint256 internal constant _ROLE_149 = 1 << 149;
    uint256 internal constant _ROLE_150 = 1 << 150;
    uint256 internal constant _ROLE_151 = 1 << 151;
    uint256 internal constant _ROLE_152 = 1 << 152;
    uint256 internal constant _ROLE_153 = 1 << 153;
    uint256 internal constant _ROLE_154 = 1 << 154;
    uint256 internal constant _ROLE_155 = 1 << 155;
    uint256 internal constant _ROLE_156 = 1 << 156;
    uint256 internal constant _ROLE_157 = 1 << 157;
    uint256 internal constant _ROLE_158 = 1 << 158;
    uint256 internal constant _ROLE_159 = 1 << 159;
    uint256 internal constant _ROLE_160 = 1 << 160;
    uint256 internal constant _ROLE_161 = 1 << 161;
    uint256 internal constant _ROLE_162 = 1 << 162;
    uint256 internal constant _ROLE_163 = 1 << 163;
    uint256 internal constant _ROLE_164 = 1 << 164;
    uint256 internal constant _ROLE_165 = 1 << 165;
    uint256 internal constant _ROLE_166 = 1 << 166;
    uint256 internal constant _ROLE_167 = 1 << 167;
    uint256 internal constant _ROLE_168 = 1 << 168;
    uint256 internal constant _ROLE_169 = 1 << 169;
    uint256 internal constant _ROLE_170 = 1 << 170;
    uint256 internal constant _ROLE_171 = 1 << 171;
    uint256 internal constant _ROLE_172 = 1 << 172;
    uint256 internal constant _ROLE_173 = 1 << 173;
    uint256 internal constant _ROLE_174 = 1 << 174;
    uint256 internal constant _ROLE_175 = 1 << 175;
    uint256 internal constant _ROLE_176 = 1 << 176;
    uint256 internal constant _ROLE_177 = 1 << 177;
    uint256 internal constant _ROLE_178 = 1 << 178;
    uint256 internal constant _ROLE_179 = 1 << 179;
    uint256 internal constant _ROLE_180 = 1 << 180;
    uint256 internal constant _ROLE_181 = 1 << 181;
    uint256 internal constant _ROLE_182 = 1 << 182;
    uint256 internal constant _ROLE_183 = 1 << 183;
    uint256 internal constant _ROLE_184 = 1 << 184;
    uint256 internal constant _ROLE_185 = 1 << 185;
    uint256 internal constant _ROLE_186 = 1 << 186;
    uint256 internal constant _ROLE_187 = 1 << 187;
    uint256 internal constant _ROLE_188 = 1 << 188;
    uint256 internal constant _ROLE_189 = 1 << 189;
    uint256 internal constant _ROLE_190 = 1 << 190;
    uint256 internal constant _ROLE_191 = 1 << 191;
    uint256 internal constant _ROLE_192 = 1 << 192;
    uint256 internal constant _ROLE_193 = 1 << 193;
    uint256 internal constant _ROLE_194 = 1 << 194;
    uint256 internal constant _ROLE_195 = 1 << 195;
    uint256 internal constant _ROLE_196 = 1 << 196;
    uint256 internal constant _ROLE_197 = 1 << 197;
    uint256 internal constant _ROLE_198 = 1 << 198;
    uint256 internal constant _ROLE_199 = 1 << 199;
    uint256 internal constant _ROLE_200 = 1 << 200;
    uint256 internal constant _ROLE_201 = 1 << 201;
    uint256 internal constant _ROLE_202 = 1 << 202;
    uint256 internal constant _ROLE_203 = 1 << 203;
    uint256 internal constant _ROLE_204 = 1 << 204;
    uint256 internal constant _ROLE_205 = 1 << 205;
    uint256 internal constant _ROLE_206 = 1 << 206;
    uint256 internal constant _ROLE_207 = 1 << 207;
    uint256 internal constant _ROLE_208 = 1 << 208;
    uint256 internal constant _ROLE_209 = 1 << 209;
    uint256 internal constant _ROLE_210 = 1 << 210;
    uint256 internal constant _ROLE_211 = 1 << 211;
    uint256 internal constant _ROLE_212 = 1 << 212;
    uint256 internal constant _ROLE_213 = 1 << 213;
    uint256 internal constant _ROLE_214 = 1 << 214;
    uint256 internal constant _ROLE_215 = 1 << 215;
    uint256 internal constant _ROLE_216 = 1 << 216;
    uint256 internal constant _ROLE_217 = 1 << 217;
    uint256 internal constant _ROLE_218 = 1 << 218;
    uint256 internal constant _ROLE_219 = 1 << 219;
    uint256 internal constant _ROLE_220 = 1 << 220;
    uint256 internal constant _ROLE_221 = 1 << 221;
    uint256 internal constant _ROLE_222 = 1 << 222;
    uint256 internal constant _ROLE_223 = 1 << 223;
    uint256 internal constant _ROLE_224 = 1 << 224;
    uint256 internal constant _ROLE_225 = 1 << 225;
    uint256 internal constant _ROLE_226 = 1 << 226;
    uint256 internal constant _ROLE_227 = 1 << 227;
    uint256 internal constant _ROLE_228 = 1 << 228;
    uint256 internal constant _ROLE_229 = 1 << 229;
    uint256 internal constant _ROLE_230 = 1 << 230;
    uint256 internal constant _ROLE_231 = 1 << 231;
    uint256 internal constant _ROLE_232 = 1 << 232;
    uint256 internal constant _ROLE_233 = 1 << 233;
    uint256 internal constant _ROLE_234 = 1 << 234;
    uint256 internal constant _ROLE_235 = 1 << 235;
    uint256 internal constant _ROLE_236 = 1 << 236;
    uint256 internal constant _ROLE_237 = 1 << 237;
    uint256 internal constant _ROLE_238 = 1 << 238;
    uint256 internal constant _ROLE_239 = 1 << 239;
    uint256 internal constant _ROLE_240 = 1 << 240;
    uint256 internal constant _ROLE_241 = 1 << 241;
    uint256 internal constant _ROLE_242 = 1 << 242;
    uint256 internal constant _ROLE_243 = 1 << 243;
    uint256 internal constant _ROLE_244 = 1 << 244;
    uint256 internal constant _ROLE_245 = 1 << 245;
    uint256 internal constant _ROLE_246 = 1 << 246;
    uint256 internal constant _ROLE_247 = 1 << 247;
    uint256 internal constant _ROLE_248 = 1 << 248;
    uint256 internal constant _ROLE_249 = 1 << 249;
    uint256 internal constant _ROLE_250 = 1 << 250;
    uint256 internal constant _ROLE_251 = 1 << 251;
    uint256 internal constant _ROLE_252 = 1 << 252;
    uint256 internal constant _ROLE_253 = 1 << 253;
    uint256 internal constant _ROLE_254 = 1 << 254;
    uint256 internal constant _ROLE_255 = 1 << 255;
}

// SPDX-License-Identifier: MIT
// ERC721A Contracts v4.2.3
// Creator: Chiru Labs

pragma solidity ^0.8.4;

/**
 * @dev Interface of ERC721A.
 */
interface IERC721AUpgradeable {
    /**
     * The caller must own the token or be an approved operator.
     */
    error ApprovalCallerNotOwnerNorApproved();

    /**
     * The token does not exist.
     */
    error ApprovalQueryForNonexistentToken();

    /**
     * Cannot query the balance for the zero address.
     */
    error BalanceQueryForZeroAddress();

    /**
     * Cannot mint to the zero address.
     */
    error MintToZeroAddress();

    /**
     * The quantity of tokens minted must be more than zero.
     */
    error MintZeroQuantity();

    /**
     * The token does not exist.
     */
    error OwnerQueryForNonexistentToken();

    /**
     * The caller must own the token or be an approved operator.
     */
    error TransferCallerNotOwnerNorApproved();

    /**
     * The token must be owned by `from`.
     */
    error TransferFromIncorrectOwner();

    /**
     * Cannot safely transfer to a contract that does not implement the
     * ERC721Receiver interface.
     */
    error TransferToNonERC721ReceiverImplementer();

    /**
     * Cannot transfer to the zero address.
     */
    error TransferToZeroAddress();

    /**
     * The token does not exist.
     */
    error URIQueryForNonexistentToken();

    /**
     * The `quantity` minted with ERC2309 exceeds the safety limit.
     */
    error MintERC2309QuantityExceedsLimit();

    /**
     * The `extraData` cannot be set on an unintialized ownership slot.
     */
    error OwnershipNotInitializedForExtraData();

    // =============================================================
    //                            STRUCTS
    // =============================================================

    struct TokenOwnership {
        // The address of the owner.
        address addr;
        // Stores the start time of ownership with minimal overhead for tokenomics.
        uint64 startTimestamp;
        // Whether the token has been burned.
        bool burned;
        // Arbitrary data similar to `startTimestamp` that can be set via {_extraData}.
        uint24 extraData;
    }

    // =============================================================
    //                         TOKEN COUNTERS
    // =============================================================

    /**
     * @dev Returns the total number of tokens in existence.
     * Burned tokens will reduce the count.
     * To get the total number of tokens minted, please see {_totalMinted}.
     */
    function totalSupply() external view returns (uint256);

    // =============================================================
    //                            IERC165
    // =============================================================

    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * [EIP section](https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified)
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);

    // =============================================================
    //                            IERC721
    // =============================================================

    /**
     * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
     */
    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
     */
    event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables or disables
     * (`approved`) `operator` to manage all of its assets.
     */
    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

    /**
     * @dev Returns the number of tokens in `owner`'s account.
     */
    function balanceOf(address owner) external view returns (uint256 balance);

    /**
     * @dev Returns the owner of the `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function ownerOf(uint256 tokenId) external view returns (address owner);

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`,
     * checking first that contract recipients are aware of the ERC721 protocol
     * to prevent tokens from being forever locked.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must be have been allowed to move
     * this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement
     * {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId,
        bytes calldata data
    ) external payable;

    /**
     * @dev Equivalent to `safeTransferFrom(from, to, tokenId, '')`.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId
    ) external payable;

    /**
     * @dev Transfers `tokenId` from `from` to `to`.
     *
     * WARNING: Usage of this method is discouraged, use {safeTransferFrom}
     * whenever possible.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token
     * by either {approve} or {setApprovalForAll}.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(
        address from,
        address to,
        uint256 tokenId
    ) external payable;

    /**
     * @dev Gives permission to `to` to transfer `tokenId` token to another account.
     * The approval is cleared when the token is transferred.
     *
     * Only a single account can be approved at a time, so approving the
     * zero address clears previous approvals.
     *
     * Requirements:
     *
     * - The caller must own the token or be an approved operator.
     * - `tokenId` must exist.
     *
     * Emits an {Approval} event.
     */
    function approve(address to, uint256 tokenId) external payable;

    /**
     * @dev Approve or remove `operator` as an operator for the caller.
     * Operators can call {transferFrom} or {safeTransferFrom}
     * for any token owned by the caller.
     *
     * Requirements:
     *
     * - The `operator` cannot be the caller.
     *
     * Emits an {ApprovalForAll} event.
     */
    function setApprovalForAll(address operator, bool _approved) external;

    /**
     * @dev Returns the account approved for `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function getApproved(uint256 tokenId) external view returns (address operator);

    /**
     * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
     *
     * See {setApprovalForAll}.
     */
    function isApprovedForAll(address owner, address operator) external view returns (bool);

    // =============================================================
    //                        IERC721Metadata
    // =============================================================

    /**
     * @dev Returns the token collection name.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the token collection symbol.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
     */
    function tokenURI(uint256 tokenId) external view returns (string memory);

    // =============================================================
    //                           IERC2309
    // =============================================================

    /**
     * @dev Emitted when tokens in `fromTokenId` to `toTokenId`
     * (inclusive) is transferred from `from` to `to`, as defined in the
     * [ERC2309](https://eips.ethereum.org/EIPS/eip-2309) standard.
     *
     * See {_mintERC2309} for more details.
     */
    event ConsecutiveTransfer(uint256 indexed fromTokenId, uint256 toTokenId, address indexed from, address indexed to);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (interfaces/IERC2981.sol)

pragma solidity ^0.8.0;

import "../utils/introspection/IERC165Upgradeable.sol";

/**
 * @dev Interface for the NFT Royalty Standard.
 *
 * A standardized way to retrieve royalty payment information for non-fungible tokens (NFTs) to enable universal
 * support for royalty payments across all NFT marketplaces and ecosystem participants.
 *
 * _Available since v4.5._
 */
interface IERC2981Upgradeable is IERC165Upgradeable {
    /**
     * @dev Returns how much royalty is owed and to whom, based on a sale price that may be denominated in any unit of
     * exchange. The royalty amount is denominated and should be paid in that same unit of exchange.
     */
    function royaltyInfo(uint256 tokenId, uint256 salePrice)
        external
        view
        returns (address receiver, uint256 royaltyAmount);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)

pragma solidity ^0.8.0;

/**
 * @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 IERC165Upgradeable {
    /**
     * @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);
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Simple single owner authorization mixin.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/auth/Ownable.sol)
/// @dev While the ownable portion follows [EIP-173](https://eips.ethereum.org/EIPS/eip-173)
/// for compatibility, the nomenclature for the 2-step ownership handover
/// may be unique to this codebase.
abstract contract Ownable {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The caller is not authorized to call the function.
    error Unauthorized();

    /// @dev The `newOwner` cannot be the zero address.
    error NewOwnerIsZeroAddress();

    /// @dev The `pendingOwner` does not have a valid handover request.
    error NoHandoverRequest();

    /// @dev `bytes4(keccak256(bytes("Unauthorized()")))`.
    uint256 private constant _UNAUTHORIZED_ERROR_SELECTOR = 0x82b42900;

    /// @dev `bytes4(keccak256(bytes("NewOwnerIsZeroAddress()")))`.
    uint256 private constant _NEW_OWNER_IS_ZERO_ADDRESS_ERROR_SELECTOR = 0x7448fbae;

    /// @dev `bytes4(keccak256(bytes("NoHandoverRequest()")))`.
    uint256 private constant _NO_HANDOVER_REQUEST_ERROR_SELECTOR = 0x6f5e8818;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                           EVENTS                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The ownership is transferred from `oldOwner` to `newOwner`.
    /// This event is intentionally kept the same as OpenZeppelin's Ownable to be
    /// compatible with indexers and [EIP-173](https://eips.ethereum.org/EIPS/eip-173),
    /// despite it not being as lightweight as a single argument event.
    event OwnershipTransferred(address indexed oldOwner, address indexed newOwner);

    /// @dev An ownership handover to `pendingOwner` has been requested.
    event OwnershipHandoverRequested(address indexed pendingOwner);

    /// @dev The ownership handover to `pendingOwner` has been canceled.
    event OwnershipHandoverCanceled(address indexed pendingOwner);

    /// @dev `keccak256(bytes("OwnershipTransferred(address,address)"))`.
    uint256 private constant _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE =
        0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0;

    /// @dev `keccak256(bytes("OwnershipHandoverRequested(address)"))`.
    uint256 private constant _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE =
        0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d;

    /// @dev `keccak256(bytes("OwnershipHandoverCanceled(address)"))`.
    uint256 private constant _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE =
        0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                          STORAGE                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The owner slot is given by: `not(_OWNER_SLOT_NOT)`.
    /// It is intentionally choosen to be a high value
    /// to avoid collision with lower slots.
    /// The choice of manual storage layout is to enable compatibility
    /// with both regular and upgradeable contracts.
    uint256 private constant _OWNER_SLOT_NOT = 0x8b78c6d8;

    /// The ownership handover slot of `newOwner` is given by:
    /// ```
    ///     mstore(0x00, or(shl(96, user), _HANDOVER_SLOT_SEED))
    ///     let handoverSlot := keccak256(0x00, 0x20)
    /// ```
    /// It stores the expiry timestamp of the two-step ownership handover.
    uint256 private constant _HANDOVER_SLOT_SEED = 0x389a75e1;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                     INTERNAL FUNCTIONS                     */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Initializes the owner directly without authorization guard.
    /// This function must be called upon initialization,
    /// regardless of whether the contract is upgradeable or not.
    /// This is to enable generalization to both regular and upgradeable contracts,
    /// and to save gas in case the initial owner is not the caller.
    /// For performance reasons, this function will not check if there
    /// is an existing owner.
    function _initializeOwner(address newOwner) internal virtual {
        /// @solidity memory-safe-assembly
        assembly {
            // Clean the upper 96 bits.
            newOwner := shr(96, shl(96, newOwner))
            // Store the new value.
            sstore(not(_OWNER_SLOT_NOT), newOwner)
            // Emit the {OwnershipTransferred} event.
            log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)
        }
    }

    /// @dev Sets the owner directly without authorization guard.
    function _setOwner(address newOwner) internal virtual {
        /// @solidity memory-safe-assembly
        assembly {
            let ownerSlot := not(_OWNER_SLOT_NOT)
            // Clean the upper 96 bits.
            newOwner := shr(96, shl(96, newOwner))
            // Emit the {OwnershipTransferred} event.
            log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)
            // Store the new value.
            sstore(ownerSlot, newOwner)
        }
    }

    /// @dev Throws if the sender is not the owner.
    function _checkOwner() internal view virtual {
        /// @solidity memory-safe-assembly
        assembly {
            // If the caller is not the stored owner, revert.
            if iszero(eq(caller(), sload(not(_OWNER_SLOT_NOT)))) {
                mstore(0x00, _UNAUTHORIZED_ERROR_SELECTOR)
                revert(0x1c, 0x04)
            }
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                  PUBLIC UPDATE FUNCTIONS                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Allows the owner to transfer the ownership to `newOwner`.
    function transferOwnership(address newOwner) public payable virtual onlyOwner {
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(shl(96, newOwner)) {
                mstore(0x00, _NEW_OWNER_IS_ZERO_ADDRESS_ERROR_SELECTOR)
                revert(0x1c, 0x04)
            }
        }
        _setOwner(newOwner);
    }

    /// @dev Allows the owner to renounce their ownership.
    function renounceOwnership() public payable virtual onlyOwner {
        _setOwner(address(0));
    }

    /// @dev Request a two-step ownership handover to the caller.
    /// The request will be automatically expire in 48 hours (172800 seconds) by default.
    function requestOwnershipHandover() public payable virtual {
        unchecked {
            uint256 expires = block.timestamp + ownershipHandoverValidFor();
            /// @solidity memory-safe-assembly
            assembly {
                // Compute and set the handover slot to `expires`.
                mstore(0x0c, _HANDOVER_SLOT_SEED)
                mstore(0x00, caller())
                sstore(keccak256(0x0c, 0x20), expires)
                // Emit the {OwnershipHandoverRequested} event.
                log2(0, 0, _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE, caller())
            }
        }
    }

    /// @dev Cancels the two-step ownership handover to the caller, if any.
    function cancelOwnershipHandover() public payable virtual {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute and set the handover slot to 0.
            mstore(0x0c, _HANDOVER_SLOT_SEED)
            mstore(0x00, caller())
            sstore(keccak256(0x0c, 0x20), 0)
            // Emit the {OwnershipHandoverCanceled} event.
            log2(0, 0, _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE, caller())
        }
    }

    /// @dev Allows the owner to complete the two-step ownership handover to `pendingOwner`.
    /// Reverts if there is no existing ownership handover requested by `pendingOwner`.
    function completeOwnershipHandover(address pendingOwner) public payable virtual onlyOwner {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute and set the handover slot to 0.
            mstore(0x0c, _HANDOVER_SLOT_SEED)
            mstore(0x00, pendingOwner)
            let handoverSlot := keccak256(0x0c, 0x20)
            // If the handover does not exist, or has expired.
            if gt(timestamp(), sload(handoverSlot)) {
                mstore(0x00, _NO_HANDOVER_REQUEST_ERROR_SELECTOR)
                revert(0x1c, 0x04)
            }
            // Set the handover slot to 0.
            sstore(handoverSlot, 0)
        }
        _setOwner(pendingOwner);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                   PUBLIC READ FUNCTIONS                    */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns the owner of the contract.
    function owner() public view virtual returns (address result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := sload(not(_OWNER_SLOT_NOT))
        }
    }

    /// @dev Returns the expiry timestamp for the two-step ownership handover to `pendingOwner`.
    function ownershipHandoverExpiresAt(address pendingOwner)
        public
        view
        virtual
        returns (uint256 result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute the handover slot.
            mstore(0x0c, _HANDOVER_SLOT_SEED)
            mstore(0x00, pendingOwner)
            // Load the handover slot.
            result := sload(keccak256(0x0c, 0x20))
        }
    }

    /// @dev Returns how long a two-step ownership handover is valid for in seconds.
    function ownershipHandoverValidFor() public view virtual returns (uint64) {
        return 48 * 3600;
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         MODIFIERS                          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Marks a function as only callable by the owner.
    modifier onlyOwner() virtual {
        _checkOwner();
        _;
    }
}

Settings
{
  "remappings": [
    "@core/=contracts/core/",
    "@modules/=contracts/modules/",
    "ERC721A-Upgradeable/=lib/ERC721A-Upgradeable/contracts/",
    "chiru-labs/ERC721A-Upgradeable/=lib/ERC721A-Upgradeable/contracts/",
    "closedsea/=lib/closedsea/src/",
    "ds-test/=lib/forge-std/lib/ds-test/src/",
    "erc4626-tests/=lib/closedsea/lib/openzeppelin-contracts/lib/erc4626-tests/",
    "erc721a-upgradeable/=lib/closedsea/lib/erc721a-upgradeable/contracts/",
    "erc721a/=lib/closedsea/lib/erc721a/contracts/",
    "forge-std/=lib/forge-std/src/",
    "multicaller/=lib/multicaller/src/",
    "murky/=lib/murky/src/",
    "openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/",
    "openzeppelin-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
    "openzeppelin/=lib/openzeppelin-contracts/contracts/",
    "operator-filter-registry/=lib/closedsea/lib/operator-filter-registry/",
    "preapprove/=lib/preapprove/src/",
    "solady/=lib/solady/src/",
    "solmate/=lib/solady/lib/solmate/src/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 1000
  },
  "metadata": {
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "abi"
      ]
    }
  },
  "evmVersion": "paris",
  "libraries": {}
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"address","name":"_soundEditionImplementation","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ArrayLengthsMismatch","type":"error"},{"inputs":[],"name":"ImplementationAddressCantBeZero","type":"error"},{"inputs":[],"name":"NewOwnerIsZeroAddress","type":"error"},{"inputs":[],"name":"NoHandoverRequest","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"OwnershipHandoverCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"OwnershipHandoverRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"uint256","name":"roles","type":"uint256"}],"name":"RolesUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"soundEdition","type":"address"},{"indexed":true,"internalType":"address","name":"deployer","type":"address"},{"indexed":false,"internalType":"bytes","name":"initData","type":"bytes"},{"indexed":false,"internalType":"address[]","name":"contracts","type":"address[]"},{"indexed":false,"internalType":"bytes[]","name":"data","type":"bytes[]"},{"indexed":false,"internalType":"bytes[]","name":"results","type":"bytes[]"}],"name":"SoundEditionCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newImplementation","type":"address"}],"name":"SoundEditionImplementationSet","type":"event"},{"inputs":[],"name":"cancelOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"completeOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"salt","type":"bytes32"},{"internalType":"bytes","name":"initData","type":"bytes"},{"internalType":"address[]","name":"contracts","type":"address[]"},{"internalType":"bytes[]","name":"data","type":"bytes[]"}],"name":"createSoundAndMints","outputs":[{"internalType":"address","name":"soundEdition","type":"address"},{"internalType":"bytes[]","name":"results","type":"bytes[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"roles","type":"uint256"}],"name":"grantRoles","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"roles","type":"uint256"}],"name":"hasAllRoles","outputs":[{"internalType":"bool","name":"result","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"roles","type":"uint256"}],"name":"hasAnyRole","outputs":[{"internalType":"bool","name":"result","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"roles","type":"uint256"}],"name":"ordinalsFromRoles","outputs":[{"internalType":"uint8[]","name":"ordinals","type":"uint8[]"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"result","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"ownershipHandoverExpiresAt","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ownershipHandoverValidFor","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"roles","type":"uint256"}],"name":"renounceRoles","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"requestOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"roles","type":"uint256"}],"name":"revokeRoles","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint8[]","name":"ordinals","type":"uint8[]"}],"name":"rolesFromOrdinals","outputs":[{"internalType":"uint256","name":"roles","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"rolesOf","outputs":[{"internalType":"uint256","name":"roles","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newImplementation","type":"address"}],"name":"setEditionImplementation","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"by","type":"address"},{"internalType":"bytes32","name":"salt","type":"bytes32"}],"name":"soundEditionAddress","outputs":[{"internalType":"address","name":"addr","type":"address"},{"internalType":"bool","name":"exists","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"soundEditionImplementation","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"payable","type":"function"}]

608060405234801561001057600080fd5b506040516110e33803806110e383398101604081905261002f916100be565b806001600160a01b03811661005757604051639841ec5160e01b815260040160405180910390fd5b600080546001600160a01b0319166001600160a01b03841617905561007b33610082565b50506100ee565b6001600160a01b0316638b78c6d8198190558060007f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08180a350565b6000602082840312156100d057600080fd5b81516001600160a01b03811681146100e757600080fd5b9392505050565b610fe6806100fd6000396000f3fe60806040526004361061015f5760003560e01c806354d1f13d116100c0578063a77e0b9811610074578063f04e283e11610059578063f04e283e146103c0578063f2fde38b146103d3578063fee81cf4146103e657600080fd5b8063a77e0b9814610382578063d7533f02146103a257600080fd5b806371537cdd116100a557806371537cdd146103085780637359e41f146103285780638da5cb5b1461035557600080fd5b806354d1f13d146102f8578063715018a61461030057600080fd5b80632de94807116101175780634a4ee7b1116100fc5780634a4ee7b11461026f5780635004b7a014610282578063514e62fc146102c157600080fd5b80632de948071461020e5780633d1d406a1461024157600080fd5b80631c10893f116101485780631c10893f146101ac5780631cd64df4146101bf578063256929621461020657600080fd5b806313a661ed14610164578063183a4f6e14610197575b600080fd5b34801561017057600080fd5b5061018461017f366004610b29565b610419565b6040519081526020015b60405180910390f35b6101aa6101a5366004610bee565b610442565b005b6101aa6101ba366004610c1e565b61044f565b3480156101cb57600080fd5b506101f66101da366004610c1e565b638b78c6d8600c90815260009290925260209091205481161490565b604051901515815260200161018e565b6101aa610465565b34801561021a57600080fd5b50610184610229366004610c48565b638b78c6d8600c908152600091909152602090205490565b34801561024d57600080fd5b5061026161025c366004610cb6565b6104b5565b60405161018e929190610e0d565b6101aa61027d366004610c1e565b6105e8565b34801561028e57600080fd5b506102a261029d366004610c1e565b6105fa565b604080516001600160a01b03909316835290151560208301520161018e565b3480156102cd57600080fd5b506101f66102dc366004610c1e565b638b78c6d8600c90815260009290925260209091205416151590565b6101aa6106ac565b6101aa6106e8565b34801561031457600080fd5b506101aa610323366004610c48565b6106fc565b34801561033457600080fd5b50610348610343366004610bee565b6107b2565b60405161018e9190610e37565b34801561036157600080fd5b50638b78c6d819545b6040516001600160a01b03909116815260200161018e565b34801561038e57600080fd5b5060005461036a906001600160a01b031681565b3480156103ae57600080fd5b506040516202a300815260200161018e565b6101aa6103ce366004610c48565b6107eb565b6101aa6103e1366004610c48565b610828565b3480156103f257600080fd5b50610184610401366004610c48565b63389a75e1600c908152600091909152602090205490565b6000815160051b5b801561043c57828101516001901b90911790601f1901610421565b50919050565b61044c338261084f565b50565b61045761089e565b61046182826108b9565b5050565b60006202a30067ffffffffffffffff164201905063389a75e1600c5233600052806020600c2055337fdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d600080a250565b600080546060906104e2906001600160a01b03166104dd338c60009182526020526040902090565b610904565b91506040518789823760008089836000875af1610503573d6000803e3d6000fd5b50610510868686866109df565b6040517ff2fde38b0000000000000000000000000000000000000000000000000000000081523360048201529091506001600160a01b0383169063f2fde38b90602401600060405180830381600087803b15801561056d57600080fd5b505af1158015610581573d6000803e3d6000fd5b50505050336001600160a01b0316826001600160a01b03167f405098db99342b699216d8150e930dbbf2f686f5a43485aed1e69219dafd49358a8a8a8a8a8a896040516105d49796959493929190610ea7565b60405180910390a397509795505050505050565b6105f061089e565b610461828261084f565b600080548190610696906001600160a01b0316610621868660009182526020526040902090565b306040517f3d602d80600a3d3981f3363d3d373d3d3d363d730000000000000000000000008152606093841b60148201527f5af43d82803e903d91602b57fd5bf3ff000000000000000000000000000000006028820152921b6038830152604c8201526037808220606c830152605591012090565b946001600160a01b0386163b1515945092505050565b63389a75e1600c523360005260006020600c2055337ffa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92600080a2565b6106f061089e565b6106fa6000610aa6565b565b61070461089e565b806001600160a01b038116610745576040517f9841ec5100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0384169081179091556040519081527f6474a145358de2983a1f98097b7806fd7071e8ca712d3fa4f91df709a99a9c109060200160405180910390a15050565b604051602081016000835b81835260051b6020169091019060010183811c806107bd575050601f198282030160051c8252604052919050565b6107f361089e565b63389a75e1600c52806000526020600c20805442111561081b57636f5e88186000526004601cfd5b6000905561044c81610aa6565b61083061089e565b8060601b61084657637448fbae6000526004601cfd5b61044c81610aa6565b638b78c6d8600c52816000526020600c20805482811681189250508181555080600c5160601c7f715ad5ce61fc9595c7b415289d59cf203f23a94fa06f04af7e489a0a76e1fe26600080a35050565b638b78c6d8195433146106fa576382b429006000526004601cfd5b638b78c6d8600c52816000526020600c208181541791508181555080600c5160601c7f715ad5ce61fc9595c7b415289d59cf203f23a94fa06f04af7e489a0a76e1fe26600080a35050565b60006040517f3d602d80600a3d3981f3363d3d373d3d3d363d7300000000000000000000000081528360601b60148201527f5af43d82803e903d91602b57fd5bf300000000000000000000000000000000006028820152826037826000f59150506001600160a01b0381166109d9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f455243313136373a2063726561746532206661696c6564000000000000000000604482015260640160405180910390fd5b92915050565b6060838214610a1a576040517f3b800a4600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040519050818152602081018260051b84018360051b8201855b828114610a97578035870180356020820184378782038a01356000808335866000855af1610a66573d6000803e3d6000fd5b50508184526020840193503d82523d6000602084013e3d91909101603f0167ffffffffffffffe01690602001610a34565b50604052509095945050505050565b638b78c6d81980546001600160a01b039092169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a355565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b803560ff81168114610b2457600080fd5b919050565b60006020808385031215610b3c57600080fd5b823567ffffffffffffffff80821115610b5457600080fd5b818501915085601f830112610b6857600080fd5b813581811115610b7a57610b7a610ae4565b8060051b604051601f19603f83011681018181108582111715610b9f57610b9f610ae4565b604052918252848201925083810185019188831115610bbd57600080fd5b938501935b82851015610be257610bd385610b13565b84529385019392850192610bc2565b98975050505050505050565b600060208284031215610c0057600080fd5b5035919050565b80356001600160a01b0381168114610b2457600080fd5b60008060408385031215610c3157600080fd5b610c3a83610c07565b946020939093013593505050565b600060208284031215610c5a57600080fd5b610c6382610c07565b9392505050565b60008083601f840112610c7c57600080fd5b50813567ffffffffffffffff811115610c9457600080fd5b6020830191508360208260051b8501011115610caf57600080fd5b9250929050565b60008060008060008060006080888a031215610cd157600080fd5b87359650602088013567ffffffffffffffff80821115610cf057600080fd5b818a0191508a601f830112610d0457600080fd5b813581811115610d1357600080fd5b8b6020828501011115610d2557600080fd5b6020830198508097505060408a0135915080821115610d4357600080fd5b610d4f8b838c01610c6a565b909650945060608a0135915080821115610d6857600080fd5b50610d758a828b01610c6a565b989b979a50959850939692959293505050565b600081518084526020808501808196508360051b810191508286016000805b86811015610dff578385038a5282518051808752835b81811015610dd8578281018901518882018a01528801610dbd565b5086810188018490529a87019a601f01601f19169095018601945091850191600101610da7565b509298975050505050505050565b6001600160a01b0383168152604060208201526000610e2f6040830184610d88565b949350505050565b6020808252825182820181905260009190848201906040850190845b81811015610e7257835160ff1683529284019291840191600101610e53565b50909695505050505050565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b608081526000610ebb60808301898b610e7e565b8281036020848101919091528782528891810160005b89811015610efd576001600160a01b03610eea85610c07565b1682529282019290820190600101610ed1565b5084810360408601528681528181019250600587901b810182018860005b89811015610f8b57838303601f190186528135368c9003601e19018112610f4157600080fd5b8b01858101903567ffffffffffffffff811115610f5d57600080fd5b803603821315610f6c57600080fd5b610f77858284610e7e565b978701979450505090840190600101610f1b565b50508581036060870152610f9f8188610d88565b9d9c5050505050505050505050505056fea26469706673582212202902e8074694b93e609980d72ba92fee62906fc0199af1cdd0381427cc7b242164736f6c6343000813003300000000000000000000000000000000ee37e4f29070cab8c6a73cb43f2dd73d

Deployed Bytecode

0x60806040526004361061015f5760003560e01c806354d1f13d116100c0578063a77e0b9811610074578063f04e283e11610059578063f04e283e146103c0578063f2fde38b146103d3578063fee81cf4146103e657600080fd5b8063a77e0b9814610382578063d7533f02146103a257600080fd5b806371537cdd116100a557806371537cdd146103085780637359e41f146103285780638da5cb5b1461035557600080fd5b806354d1f13d146102f8578063715018a61461030057600080fd5b80632de94807116101175780634a4ee7b1116100fc5780634a4ee7b11461026f5780635004b7a014610282578063514e62fc146102c157600080fd5b80632de948071461020e5780633d1d406a1461024157600080fd5b80631c10893f116101485780631c10893f146101ac5780631cd64df4146101bf578063256929621461020657600080fd5b806313a661ed14610164578063183a4f6e14610197575b600080fd5b34801561017057600080fd5b5061018461017f366004610b29565b610419565b6040519081526020015b60405180910390f35b6101aa6101a5366004610bee565b610442565b005b6101aa6101ba366004610c1e565b61044f565b3480156101cb57600080fd5b506101f66101da366004610c1e565b638b78c6d8600c90815260009290925260209091205481161490565b604051901515815260200161018e565b6101aa610465565b34801561021a57600080fd5b50610184610229366004610c48565b638b78c6d8600c908152600091909152602090205490565b34801561024d57600080fd5b5061026161025c366004610cb6565b6104b5565b60405161018e929190610e0d565b6101aa61027d366004610c1e565b6105e8565b34801561028e57600080fd5b506102a261029d366004610c1e565b6105fa565b604080516001600160a01b03909316835290151560208301520161018e565b3480156102cd57600080fd5b506101f66102dc366004610c1e565b638b78c6d8600c90815260009290925260209091205416151590565b6101aa6106ac565b6101aa6106e8565b34801561031457600080fd5b506101aa610323366004610c48565b6106fc565b34801561033457600080fd5b50610348610343366004610bee565b6107b2565b60405161018e9190610e37565b34801561036157600080fd5b50638b78c6d819545b6040516001600160a01b03909116815260200161018e565b34801561038e57600080fd5b5060005461036a906001600160a01b031681565b3480156103ae57600080fd5b506040516202a300815260200161018e565b6101aa6103ce366004610c48565b6107eb565b6101aa6103e1366004610c48565b610828565b3480156103f257600080fd5b50610184610401366004610c48565b63389a75e1600c908152600091909152602090205490565b6000815160051b5b801561043c57828101516001901b90911790601f1901610421565b50919050565b61044c338261084f565b50565b61045761089e565b61046182826108b9565b5050565b60006202a30067ffffffffffffffff164201905063389a75e1600c5233600052806020600c2055337fdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d600080a250565b600080546060906104e2906001600160a01b03166104dd338c60009182526020526040902090565b610904565b91506040518789823760008089836000875af1610503573d6000803e3d6000fd5b50610510868686866109df565b6040517ff2fde38b0000000000000000000000000000000000000000000000000000000081523360048201529091506001600160a01b0383169063f2fde38b90602401600060405180830381600087803b15801561056d57600080fd5b505af1158015610581573d6000803e3d6000fd5b50505050336001600160a01b0316826001600160a01b03167f405098db99342b699216d8150e930dbbf2f686f5a43485aed1e69219dafd49358a8a8a8a8a8a896040516105d49796959493929190610ea7565b60405180910390a397509795505050505050565b6105f061089e565b610461828261084f565b600080548190610696906001600160a01b0316610621868660009182526020526040902090565b306040517f3d602d80600a3d3981f3363d3d373d3d3d363d730000000000000000000000008152606093841b60148201527f5af43d82803e903d91602b57fd5bf3ff000000000000000000000000000000006028820152921b6038830152604c8201526037808220606c830152605591012090565b946001600160a01b0386163b1515945092505050565b63389a75e1600c523360005260006020600c2055337ffa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92600080a2565b6106f061089e565b6106fa6000610aa6565b565b61070461089e565b806001600160a01b038116610745576040517f9841ec5100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0384169081179091556040519081527f6474a145358de2983a1f98097b7806fd7071e8ca712d3fa4f91df709a99a9c109060200160405180910390a15050565b604051602081016000835b81835260051b6020169091019060010183811c806107bd575050601f198282030160051c8252604052919050565b6107f361089e565b63389a75e1600c52806000526020600c20805442111561081b57636f5e88186000526004601cfd5b6000905561044c81610aa6565b61083061089e565b8060601b61084657637448fbae6000526004601cfd5b61044c81610aa6565b638b78c6d8600c52816000526020600c20805482811681189250508181555080600c5160601c7f715ad5ce61fc9595c7b415289d59cf203f23a94fa06f04af7e489a0a76e1fe26600080a35050565b638b78c6d8195433146106fa576382b429006000526004601cfd5b638b78c6d8600c52816000526020600c208181541791508181555080600c5160601c7f715ad5ce61fc9595c7b415289d59cf203f23a94fa06f04af7e489a0a76e1fe26600080a35050565b60006040517f3d602d80600a3d3981f3363d3d373d3d3d363d7300000000000000000000000081528360601b60148201527f5af43d82803e903d91602b57fd5bf300000000000000000000000000000000006028820152826037826000f59150506001600160a01b0381166109d9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f455243313136373a2063726561746532206661696c6564000000000000000000604482015260640160405180910390fd5b92915050565b6060838214610a1a576040517f3b800a4600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040519050818152602081018260051b84018360051b8201855b828114610a97578035870180356020820184378782038a01356000808335866000855af1610a66573d6000803e3d6000fd5b50508184526020840193503d82523d6000602084013e3d91909101603f0167ffffffffffffffe01690602001610a34565b50604052509095945050505050565b638b78c6d81980546001600160a01b039092169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a355565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b803560ff81168114610b2457600080fd5b919050565b60006020808385031215610b3c57600080fd5b823567ffffffffffffffff80821115610b5457600080fd5b818501915085601f830112610b6857600080fd5b813581811115610b7a57610b7a610ae4565b8060051b604051601f19603f83011681018181108582111715610b9f57610b9f610ae4565b604052918252848201925083810185019188831115610bbd57600080fd5b938501935b82851015610be257610bd385610b13565b84529385019392850192610bc2565b98975050505050505050565b600060208284031215610c0057600080fd5b5035919050565b80356001600160a01b0381168114610b2457600080fd5b60008060408385031215610c3157600080fd5b610c3a83610c07565b946020939093013593505050565b600060208284031215610c5a57600080fd5b610c6382610c07565b9392505050565b60008083601f840112610c7c57600080fd5b50813567ffffffffffffffff811115610c9457600080fd5b6020830191508360208260051b8501011115610caf57600080fd5b9250929050565b60008060008060008060006080888a031215610cd157600080fd5b87359650602088013567ffffffffffffffff80821115610cf057600080fd5b818a0191508a601f830112610d0457600080fd5b813581811115610d1357600080fd5b8b6020828501011115610d2557600080fd5b6020830198508097505060408a0135915080821115610d4357600080fd5b610d4f8b838c01610c6a565b909650945060608a0135915080821115610d6857600080fd5b50610d758a828b01610c6a565b989b979a50959850939692959293505050565b600081518084526020808501808196508360051b810191508286016000805b86811015610dff578385038a5282518051808752835b81811015610dd8578281018901518882018a01528801610dbd565b5086810188018490529a87019a601f01601f19169095018601945091850191600101610da7565b509298975050505050505050565b6001600160a01b0383168152604060208201526000610e2f6040830184610d88565b949350505050565b6020808252825182820181905260009190848201906040850190845b81811015610e7257835160ff1683529284019291840191600101610e53565b50909695505050505050565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b608081526000610ebb60808301898b610e7e565b8281036020848101919091528782528891810160005b89811015610efd576001600160a01b03610eea85610c07565b1682529282019290820190600101610ed1565b5084810360408601528681528181019250600587901b810182018860005b89811015610f8b57838303601f190186528135368c9003601e19018112610f4157600080fd5b8b01858101903567ffffffffffffffff811115610f5d57600080fd5b803603821315610f6c57600080fd5b610f77858284610e7e565b978701979450505090840190600101610f1b565b50508581036060870152610f9f8188610d88565b9d9c5050505050505050505050505056fea26469706673582212202902e8074694b93e609980d72ba92fee62906fc0199af1cdd0381427cc7b242164736f6c63430008130033

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

00000000000000000000000000000000ee37e4f29070cab8c6a73cb43f2dd73d

-----Decoded View---------------
Arg [0] : _soundEditionImplementation (address): 0x00000000ee37E4f29070cab8C6a73Cb43f2dD73d

-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 00000000000000000000000000000000ee37e4f29070cab8c6a73cb43f2dd73d


Deployed Bytecode Sourcemap

5555:8755:0:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;9956:486:9;;;;;;;;;;-1:-1:-1;9956:486:9;;;;;:::i;:::-;;:::i;:::-;;;1632:25:10;;;1620:2;1605:18;9956:486:9;;;;;;;;7865:109;;;;;;:::i;:::-;;:::i;:::-;;7326:123;;;;;;:::i;:::-;;:::i;8885:437::-;;;;;;;;;;-1:-1:-1;8885:437:9;;;;;:::i;:::-;9104:15;9098:4;9091:29;;;8964:11;9133:18;;;;9284:4;9268:21;;;9262:28;9258:40;;9255:51;;8885:437;;;;2478:14:10;;2471:22;2453:41;;2441:2;2426:18;8885:437:9;2313:187:10;7253:616:8;;;:::i;9370:353:9:-;;;;;;;;;;-1:-1:-1;9370:353:9;;;;;:::i;:::-;9572:15;9566:4;9559:29;;;9430:13;9601:18;;;;9701:4;9685:21;;9679:28;;9370:353;6587:1559:0;;;;;;;;;;-1:-1:-1;6587:1559:0;;;;;:::i;:::-;;:::i;:::-;;;;;;;;:::i;7594:125:9:-;;;;;;:::i;:::-;;:::i;8738:268:0:-;;;;;;;;;;-1:-1:-1;8738:268:0;;;;;:::i;:::-;;:::i;:::-;;;;-1:-1:-1;;;;;6235:55:10;;;6217:74;;6334:14;;6327:22;6322:2;6307:18;;6300:50;6190:18;8738:268:0;6049:307:10;8319:504:9;;;;;;;;;;-1:-1:-1;8319:504:9;;;;;:::i;:::-;8537:15;8531:4;8524:29;;;8397:11;8566:18;;;;8791:4;8775:21;;;8769:28;8765:40;8758:48;8751:56;;8319:504;7951:456:8;;;:::i;6991:100::-;;;:::i;8203:285:0:-;;;;;;;;;;-1:-1:-1;8203:285:0;;;;;:::i;:::-;;:::i;10677:1054:9:-;;;;;;;;;;-1:-1:-1;10677:1054:9;;;;;:::i;:::-;;:::i;:::-;;;;;;;:::i;9638:191:8:-;;;;;;;;;;-1:-1:-1;;;9786:27:8;9638:191;;;-1:-1:-1;;;;;7169:55:10;;;7151:74;;7139:2;7124:18;9638:191:8;7005:226:10;5894:41:0;;;;;;;;;;-1:-1:-1;5894:41:0;;;;-1:-1:-1;;;;;5894:41:0;;;10458:107:8;;;;;;;;;;-1:-1:-1;10458:107:8;;10549:9;7380:50:10;;7368:2;7353:18;10458:107:8;7236:200:10;8594:707:8;;;;;;:::i;:::-;;:::i;6576:350::-;;;;;;:::i;:::-;;:::i;9932:435::-;;;;;;;;;;-1:-1:-1;9932:435:8;;;;;:::i;:::-;10202:19;10196:4;10189:33;;;10051:14;10235:26;;;;10345:4;10329:21;;10323:28;;9932:435;9956:486:9;10029:13;10148:8;10142:15;10139:1;10135:23;10120:306;10161:1;10120:306;;;10383:16;;;10377:23;10402:1;10373:31;;10370:42;;;;-1:-1:-1;;10170:12:9;10120:306;;;10124:36;9956:486;;;:::o;7865:109::-;7936:31;7949:10;7961:5;7936:12;:31::i;:::-;7865:109;:::o;7326:123::-;10954:13:8;:11;:13::i;:::-;7418:24:9::1;7430:4;7436:5;7418:11;:24::i;:::-;7326:123:::0;;:::o;7253:616:8:-;7346:15;10549:9;7364:45;;:15;:45;7346:63;;7577:19;7571:4;7564:33;7627:8;7621:4;7614:22;7683:7;7676:4;7670;7660:21;7653:38;7830:8;7783:45;7780:1;7777;7772:67;7479:374;7253:616::o;6587:1559:0:-;6764:20;6908:26;;6786:22;;6882:84;;-1:-1:-1;;;;;6908:26:0;6936:29;6948:10;6960:4;13697:14;13805:16;;;13841:4;13834:18;13955:4;13939:21;;;13627:349;6936:29;6882:25;:84::i;:::-;6859:108;;7090:4;7084:11;7196:15;7179;7176:1;7163:49;7664:4;7608;7551:15;7509:1;7451;7390:12;7345:5;7319:396;7292:622;;7836:16;7830:4;7824;7809:44;7883:16;7877:4;7870:30;7292:622;;7944:31;7959:9;;7970:4;;7944:14;:31::i;:::-;7986:56;;;;;8031:10;7986:56;;;7151:74:10;7934:41:0;;-1:-1:-1;;;;;;7986:44:0;;;;;7124:18:10;;7986:56:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8092:10;-1:-1:-1;;;;;8058:81:0;8078:12;-1:-1:-1;;;;;8058:81:0;;8104:8;;8114:9;;8125:4;;8131:7;8058:81;;;;;;;;;;;;:::i;:::-;;;;;;;;6587:1559;;;;;;;;;;:::o;7594:125:9:-;10954:13:8;:11;:13::i;:::-;7687:25:9::1;7700:4;7706:5;7687:12;:25::i;8738:268:0:-:0;8816:12;8895:26;;8816:12;;8860:100;;-1:-1:-1;;;;;8895:26:0;8923:21;8935:2;8939:4;13697:14;13805:16;;;13841:4;13834:18;13955:4;13939:21;;;13627:349;8923:21;8954:4;2867::7;2861:11;2897:66;2885:79;;3004:4;3000:25;;;2993:4;2984:14;;2977:49;3062:66;3055:4;3046:14;;3039:90;3165:19;;3158:4;3149:14;;3142:43;3214:4;3205:14;;3198:28;3277:4;3262:20;;;3255:4;3246:14;;3239:44;3335:4;3319:14;;3309:31;;2609:747;8860:100:0;8853:107;-1:-1:-1;;;;;8979:16:0;;;:20;;;-1:-1:-1;8738:268:0;-1:-1:-1;;;8738:268:0:o;7951:456:8:-;8153:19;8147:4;8140:33;8199:8;8193:4;8186:22;8251:1;8244:4;8238;8228:21;8221:32;8382:8;8336:44;8333:1;8330;8325:66;7951:456::o;6991:100::-;10954:13;:11;:13::i;:::-;7063:21:::1;7081:1;7063:9;:21::i;:::-;6991:100::o:0;8203:285:0:-;10954:13:8;:11;:13::i;:::-;8329:17:0;-1:-1:-1;;;;;14196:28:0;::::1;14192:99;;14247:33;;;;;;;;;;;;;;14192:99;8362:26:::2;:46:::0;;;::::2;-1:-1:-1::0;;;;;8362:46:0;::::2;::::0;;::::2;::::0;;;8424:57:::2;::::0;7151:74:10;;;8424:57:0::2;::::0;7139:2:10;7124:18;8424:57:0::2;;;;;;;10977:1:8::1;8203:285:0::0;:::o;10677:1054:9:-;10911:4;10905:11;10954:4;10940:19;;10981:1;11182:5;11167:367;11213:14;;;11401:1;11397:17;;;11388:27;;;;11411:1;11437:9;11468:13;;;;11167:367;11498:22;-1:-1:-1;;;;11618:29:9;;;;11615:1;11611:37;11594:55;;11705:4;11698:17;11631:8;10677:1054;-1:-1:-1;10677:1054:9:o;8594:707:8:-;10954:13;:11;:13::i;:::-;8828:19:::1;8822:4;8815:33;8874:12;8868:4;8861:26;8936:4;8930;8920:21;9042:12;9036:19;9023:11;9020:36;9017:156;;;9088:35;9082:4;9075:49;9154:4;9148;9141:18;9017:156;9250:1;9229:23:::0;;9271::::1;9281:12:::0;9271:9:::1;:23::i;6576:350::-:0;10954:13;:11;:13::i;:::-;6748:8:::1;6744:2;6740:17;6730:151;;6790:41;6784:4;6777:55;6862:4;6856;6849:18;6730:151;6900:19;6910:8;6900:9;:19::i;3516:834:9:-:0;3711:15;3705:4;3698:29;3753:4;3747;3740:18;3803:4;3797;3787:21;3886:8;3880:15;4119:5;4105:12;4101:24;4087:12;4083:43;4074:52;;;4198:5;4188:8;4181:23;;4328:5;4320:4;4314:11;4310:2;4306:20;4274:30;4271:1;4268;4263:71;3516:834;;:::o;5855:361:8:-;-1:-1:-1;;6061:27:8;6051:8;6048:41;6038:162;;6122:28;6116:4;6109:42;6181:4;6175;6168:18;2759:621:9;2953:15;2947:4;2940:29;2995:4;2989;2982:18;3045:4;3039;3029:21;3156:5;3145:8;3139:15;3136:26;3127:35;;3228:5;3218:8;3211:23;;3358:5;3350:4;3344:11;3340:2;3336:20;3304:30;3301:1;3298;3293:71;2759:621;;:::o;1906:593:7:-;1990:16;2101:4;2095:11;2131:66;2126:3;2119:79;2244:14;2238:4;2234:25;2227:4;2222:3;2218:14;2211:49;2296:66;2289:4;2284:3;2280:14;2273:90;2410:4;2404;2399:3;2396:1;2388:27;2376:39;-1:-1:-1;;;;;;;2442:22:7;;2434:58;;;;;;;10018:2:10;2434:58:7;;;10000:21:10;10057:2;10037:18;;;10030:30;10096:25;10076:18;;;10069:53;10139:18;;2434:58:7;;;;;;;;1906:593;;;;:::o;9479:3795:0:-;9590:22;9632:31;;;9628:66;;9672:22;;;;;;;;;;;;;;9628:66;9930:4;9924:11;9913:22;;10030:11;10021:7;10014:28;10160:4;10151:7;10147:18;10372:11;10369:1;10365:19;10352:11;10348:37;10576:11;10573:1;10569:19;10553:14;10549:40;10709:11;10694:2450;10736:14;10733:1;10730:21;10694:2450;;10887:1;10874:15;10861:11;10857:33;11171:1;11158:15;11076:4;11073:1;11069:12;11010:1;10976:248;11570:11;11567:1;11563:19;11545:16;11541:42;11528:56;12094:4;12034;11973:1;11960:15;11887:1;11825;11779;11730:5;11700:449;11669:699;;12282:16;12276:4;12270;12255:44;12333:16;12327:4;12320:30;11669:699;;;12473:1;12457:14;12450:25;12530:4;12514:14;12510:25;12492:43;;12626:16;12623:1;12616:27;12751:16;12745:4;12738;12735:1;12731:12;12716:52;13085:16;13078:24;;;;13104:4;13074:35;13111:18;13070:60;;10767:4;10760:12;10694:2450;;;-1:-1:-1;13250:4:0;13243:15;-1:-1:-1;9479:3795:0;;;-1:-1:-1;;;;;9479:3795:0:o;5302:495:8:-;-1:-1:-1;;5678:16:8;;-1:-1:-1;;;;;5534:26:8;;;;;;5638:38;5635:1;;5627:78;5754:27;5302:495::o;14:184:10:-;66:77;63:1;56:88;163:4;160:1;153:15;187:4;184:1;177:15;203:156;269:20;;329:4;318:16;;308:27;;298:55;;349:1;346;339:12;298:55;203:156;;;:::o;364:1117::-;446:6;477:2;520;508:9;499:7;495:23;491:32;488:52;;;536:1;533;526:12;488:52;576:9;563:23;605:18;646:2;638:6;635:14;632:34;;;662:1;659;652:12;632:34;700:6;689:9;685:22;675:32;;745:7;738:4;734:2;730:13;726:27;716:55;;767:1;764;757:12;716:55;803:2;790:16;825:2;821;818:10;815:36;;;831:18;;:::i;:::-;877:2;874:1;870:10;909:2;903:9;972:2;968:7;963:2;959;955:11;951:25;943:6;939:38;1027:6;1015:10;1012:22;1007:2;995:10;992:18;989:46;986:72;;;1038:18;;:::i;:::-;1074:2;1067:22;1124:18;;;1158:15;;;;-1:-1:-1;1200:11:10;;;1196:20;;;1228:19;;;1225:39;;;1260:1;1257;1250:12;1225:39;1284:11;;;;1304:146;1320:6;1315:3;1312:15;1304:146;;;1386:21;1403:3;1386:21;:::i;:::-;1374:34;;1337:12;;;;1428;;;;1304:146;;;1469:6;364:1117;-1:-1:-1;;;;;;;;364:1117:10:o;1668:180::-;1727:6;1780:2;1768:9;1759:7;1755:23;1751:32;1748:52;;;1796:1;1793;1786:12;1748:52;-1:-1:-1;1819:23:10;;1668:180;-1:-1:-1;1668:180:10:o;1853:196::-;1921:20;;-1:-1:-1;;;;;1970:54:10;;1960:65;;1950:93;;2039:1;2036;2029:12;2054:254;2122:6;2130;2183:2;2171:9;2162:7;2158:23;2154:32;2151:52;;;2199:1;2196;2189:12;2151:52;2222:29;2241:9;2222:29;:::i;:::-;2212:39;2298:2;2283:18;;;;2270:32;;-1:-1:-1;;;2054:254:10:o;2505:186::-;2564:6;2617:2;2605:9;2596:7;2592:23;2588:32;2585:52;;;2633:1;2630;2623:12;2585:52;2656:29;2675:9;2656:29;:::i;:::-;2646:39;2505:186;-1:-1:-1;;;2505:186:10:o;2696:367::-;2759:8;2769:6;2823:3;2816:4;2808:6;2804:17;2800:27;2790:55;;2841:1;2838;2831:12;2790:55;-1:-1:-1;2864:20:10;;2907:18;2896:30;;2893:50;;;2939:1;2936;2929:12;2893:50;2976:4;2968:6;2964:17;2952:29;;3036:3;3029:4;3019:6;3016:1;3012:14;3004:6;3000:27;2996:38;2993:47;2990:67;;;3053:1;3050;3043:12;2990:67;2696:367;;;;;:::o;3068:1301::-;3230:6;3238;3246;3254;3262;3270;3278;3331:3;3319:9;3310:7;3306:23;3302:33;3299:53;;;3348:1;3345;3338:12;3299:53;3384:9;3371:23;3361:33;;3445:2;3434:9;3430:18;3417:32;3468:18;3509:2;3501:6;3498:14;3495:34;;;3525:1;3522;3515:12;3495:34;3563:6;3552:9;3548:22;3538:32;;3608:7;3601:4;3597:2;3593:13;3589:27;3579:55;;3630:1;3627;3620:12;3579:55;3670:2;3657:16;3696:2;3688:6;3685:14;3682:34;;;3712:1;3709;3702:12;3682:34;3757:7;3752:2;3743:6;3739:2;3735:15;3731:24;3728:37;3725:57;;;3778:1;3775;3768:12;3725:57;3809:2;3805;3801:11;3791:21;;3831:6;3821:16;;;3890:2;3879:9;3875:18;3862:32;3846:48;;3919:2;3909:8;3906:16;3903:36;;;3935:1;3932;3925:12;3903:36;3974:72;4038:7;4027:8;4016:9;4012:24;3974:72;:::i;:::-;4065:8;;-1:-1:-1;3948:98:10;-1:-1:-1;4153:2:10;4138:18;;4125:32;;-1:-1:-1;4169:16:10;;;4166:36;;;4198:1;4195;4188:12;4166:36;;4237:72;4301:7;4290:8;4279:9;4275:24;4237:72;:::i;:::-;3068:1301;;;;-1:-1:-1;3068:1301:10;;-1:-1:-1;3068:1301:10;;;;4211:98;;-1:-1:-1;;;3068:1301:10:o;4374:1009::-;4425:3;4463:5;4457:12;4490:6;4485:3;4478:19;4516:4;4557:2;4552:3;4548:12;4582:11;4609;4602:18;;4659:6;4656:1;4652:14;4645:5;4641:26;4629:38;;4701:2;4694:5;4690:14;4722:1;4743;4753:604;4769:6;4764:3;4761:15;4753:604;;;4844:5;4838:4;4834:16;4829:3;4822:29;4880:6;4874:13;4922:2;4916:9;4951:8;4945:4;4938:22;4984:1;4998:155;5014:8;5009:3;5006:17;4998:155;;;5120:12;;;5116:21;;5110:28;5089:14;;;5085:23;;5078:61;5033:12;;4998:155;;;-1:-1:-1;5177:19:10;;;5173:28;;5166:39;;;5335:12;;;;5267:2;5244:17;-1:-1:-1;;5240:31:10;5230:42;;;5226:51;;;-1:-1:-1;5300:15:10;;;;4795:1;4786:11;4753:604;;;-1:-1:-1;5373:4:10;;4374:1009;-1:-1:-1;;;;;;;;4374:1009:10:o;5388:397::-;-1:-1:-1;;;;;5617:6:10;5613:55;5602:9;5595:74;5705:2;5700;5689:9;5685:18;5678:30;5576:4;5725:54;5775:2;5764:9;5760:18;5752:6;5725:54;:::i;:::-;5717:62;5388:397;-1:-1:-1;;;;5388:397:10:o;6361:639::-;6528:2;6580:21;;;6650:13;;6553:18;;;6672:22;;;6499:4;;6528:2;6751:15;;;;6725:2;6710:18;;;6499:4;6794:180;6808:6;6805:1;6802:13;6794:180;;;6873:13;;6888:4;6869:24;6857:37;;6949:15;;;;6914:12;;;;6830:1;6823:9;6794:180;;;-1:-1:-1;6991:3:10;;6361:639;-1:-1:-1;;;;;;6361:639:10:o;7441:266::-;7529:6;7524:3;7517:19;7581:6;7574:5;7567:4;7562:3;7558:14;7545:43;-1:-1:-1;7633:1:10;7608:16;;;7626:4;7604:27;;;7597:38;;;;7689:2;7668:15;;;-1:-1:-1;;7664:29:10;7655:39;;;7651:50;;7441:266::o;7712:2099::-;8161:3;8150:9;8143:22;8124:4;8188:62;8245:3;8234:9;8230:19;8222:6;8214;8188:62;:::i;:::-;8307:22;;;8269:2;8287:18;;;8280:50;;;;8365:22;;;8441:6;;8403:15;;8465:1;8475:231;8489:6;8486:1;8483:13;8475:231;;;-1:-1:-1;;;;;8554:26:10;8573:6;8554:26;:::i;:::-;8550:75;8538:88;;8681:15;;;;8646:12;;;;8511:1;8504:9;8475:231;;;-1:-1:-1;8742:19:10;;;8737:2;8722:18;;8715:47;8796:19;;;8833:12;;;;-1:-1:-1;8885:1:10;8881:14;;;8872:24;;8868:33;;8926:6;8952:1;8962:725;8978:6;8973:3;8970:15;8962:725;;;9049:16;;;-1:-1:-1;;9045:30:10;9031:45;;9115:22;;9192:14;9188:27;;;-1:-1:-1;;9184:41:10;9160:66;;9150:94;;9240:1;9237;9230:12;9150:94;9270:31;;9375:14;;;;9328:19;9416:18;9405:30;;9402:50;;;9448:1;9445;9438:12;9402:50;9501:6;9485:14;9481:27;9472:7;9468:41;9465:61;;;9522:1;9519;9512:12;9465:61;9549:50;9592:6;9584;9575:7;9549:50;:::i;:::-;9663:14;;;;9539:60;-1:-1:-1;;;9624:17:10;;;;9004:1;8995:11;8962:725;;;8966:3;;9735:9;9727:6;9723:22;9718:2;9707:9;9703:18;9696:50;9763:42;9798:6;9790;9763:42;:::i;:::-;9755:50;7712:2099;-1:-1:-1;;;;;;;;;;;;;7712:2099:10:o

Swarm Source

ipfs://2902e8074694b93e609980d72ba92fee62906fc0199af1cdd0381427cc7b2421

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
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.