// File: @openzeppelin/contracts/token/ERC1155/extensions/IERC1155MetadataURI.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC1155/extensions/IERC1155MetadataURI.sol)

pragma solidity ^0.8.0;

import "../IERC1155.sol";

/**
 * @dev Interface of the optional ERC1155MetadataExtension interface, as defined
 * in the https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[EIP].
 *
 * _Available since v3.1._
 */
interface IERC1155MetadataURI is IERC1155 {
    /**
     * @dev Returns the URI for token type `id`.
     *
     * If the `\{id\}` substring is present in the URI, it must be replaced by
     * clients with the actual token type ID.
     */
    function uri(uint256 id) external view returns (string memory);
}

// File: @openzeppelin/contracts/token/ERC1155/IERC1155.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC1155/IERC1155.sol)

pragma solidity ^0.8.0;

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

/**
 * @dev Required interface of an ERC1155 compliant contract, as defined in the
 * https://eips.ethereum.org/EIPS/eip-1155[EIP].
 *
 * _Available since v3.1._
 */
interface IERC1155 is IERC165 {
    /**
     * @dev Emitted when `value` tokens of token type `id` are transferred from `from` to `to` by `operator`.
     */
    event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value);

    /**
     * @dev Equivalent to multiple {TransferSingle} events, where `operator`, `from` and `to` are the same for all
     * transfers.
     */
    event TransferBatch(
        address indexed operator,
        address indexed from,
        address indexed to,
        uint256[] ids,
        uint256[] values
    );

    /**
     * @dev Emitted when `account` grants or revokes permission to `operator` to transfer their tokens, according to
     * `approved`.
     */
    event ApprovalForAll(address indexed account, address indexed operator, bool approved);

    /**
     * @dev Emitted when the URI for token type `id` changes to `value`, if it is a non-programmatic URI.
     *
     * If an {URI} event was emitted for `id`, the standard
     * https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[guarantees] that `value` will equal the value
     * returned by {IERC1155MetadataURI-uri}.
     */
    event URI(string value, uint256 indexed id);

    /**
     * @dev Returns the amount of tokens of token type `id` owned by `account`.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     */
    function balanceOf(address account, uint256 id) external view returns (uint256);

    /**
     * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {balanceOf}.
     *
     * Requirements:
     *
     * - `accounts` and `ids` must have the same length.
     */
    function balanceOfBatch(
        address[] calldata accounts,
        uint256[] calldata ids
    ) external view returns (uint256[] memory);

    /**
     * @dev Grants or revokes permission to `operator` to transfer the caller's tokens, according to `approved`,
     *
     * Emits an {ApprovalForAll} event.
     *
     * Requirements:
     *
     * - `operator` cannot be the caller.
     */
    function setApprovalForAll(address operator, bool approved) external;

    /**
     * @dev Returns true if `operator` is approved to transfer ``account``'s tokens.
     *
     * See {setApprovalForAll}.
     */
    function isApprovedForAll(address account, address operator) external view returns (bool);

    /**
     * @dev Transfers `amount` tokens of token type `id` from `from` to `to`.
     *
     * Emits a {TransferSingle} event.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - If the caller is not `from`, it must have been approved to spend ``from``'s tokens via {setApprovalForAll}.
     * - `from` must have a balance of tokens of type `id` of at least `amount`.
     * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
     * acceptance magic value.
     */
    function safeTransferFrom(address from, address to, uint256 id, uint256 amount, bytes calldata data) external;

    /**
     * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {safeTransferFrom}.
     *
     * Emits a {TransferBatch} event.
     *
     * Requirements:
     *
     * - `ids` and `amounts` must have the same length.
     * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
     * acceptance magic value.
     */
    function safeBatchTransferFrom(
        address from,
        address to,
        uint256[] calldata ids,
        uint256[] calldata amounts,
        bytes calldata data
    ) external;
}

// File: @openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC1155/IERC1155Receiver.sol)

pragma solidity ^0.8.0;

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

/**
 * @dev _Available since v3.1._
 */
interface IERC1155Receiver is IERC165 {
    /**
     * @dev Handles the receipt of a single ERC1155 token type. This function is
     * called at the end of a `safeTransferFrom` after the balance has been updated.
     *
     * NOTE: To accept the transfer, this must return
     * `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
     * (i.e. 0xf23a6e61, or its own function selector).
     *
     * @param operator The address which initiated the transfer (i.e. msg.sender)
     * @param from The address which previously owned the token
     * @param id The ID of the token being transferred
     * @param value The amount of tokens being transferred
     * @param data Additional data with no specified format
     * @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` if transfer is allowed
     */
    function onERC1155Received(
        address operator,
        address from,
        uint256 id,
        uint256 value,
        bytes calldata data
    ) external returns (bytes4);

    /**
     * @dev Handles the receipt of a multiple ERC1155 token types. This function
     * is called at the end of a `safeBatchTransferFrom` after the balances have
     * been updated.
     *
     * NOTE: To accept the transfer(s), this must return
     * `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`
     * (i.e. 0xbc197c81, or its own function selector).
     *
     * @param operator The address which initiated the batch transfer (i.e. msg.sender)
     * @param from The address which previously owned the token
     * @param ids An array containing ids of each token being transferred (order and length must match values array)
     * @param values An array containing amounts of each token being transferred (order and length must match ids array)
     * @param data Additional data with no specified format
     * @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` if transfer is allowed
     */
    function onERC1155BatchReceived(
        address operator,
        address from,
        uint256[] calldata ids,
        uint256[] calldata values,
        bytes calldata data
    ) external returns (bytes4);
}

// File: @openzeppelin/contracts/utils/Address.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)

pragma solidity ^0.8.1;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     *
     * Furthermore, `isContract` will also return true if the target contract within
     * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
     * which only has an effect at the end of a transaction.
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
     *
     * _Available since v4.8._
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        if (success) {
            if (returndata.length == 0) {
                // only check isContract if the call was successful and the return data is empty
                // otherwise we already know that it was a contract
                require(isContract(target), "Address: call to non-contract");
            }
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason or using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    function _revert(bytes memory returndata, string memory errorMessage) private pure {
        // Look for revert reason and bubble it up if present
        if (returndata.length > 0) {
            // The easiest way to bubble the revert reason is using memory via assembly
            /// @solidity memory-safe-assembly
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert(errorMessage);
        }
    }
}

// File: @openzeppelin/contracts/utils/Context.sol



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

pragma solidity ^0.8.0;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }
}

// File: @openzeppelin/contracts/utils/introspection/ERC165.sol



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

pragma solidity ^0.8.0;

import "./IERC165.sol";

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
 * for the additional interface id that will be supported. For example:
 *
 * ```solidity
 * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
 *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
 * }
 * ```
 *
 * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
 */
abstract contract ERC165 is IERC165 {
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IERC165).interfaceId;
    }
}

// File: @openzeppelin/contracts/utils/introspection/IERC165.sol



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

// File: contracts/ISAN.sol



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

import "./SANSoulbindable.sol";

interface ISAN is SANSoulbindable {
    function tokenLevel(uint256 _tokenId)
        external
        view
        returns (SoulboundLevel _level);

    function transferFrom(
        address _from,
        address _to,
        uint256 _tokenId
    ) external;

    function batchTransferFrom(
        address _from,
        address _to,
        uint256[] calldata _tokenIDs
    ) external;

    function ownerOf(uint256 _tokenId) external view returns (address owner);
}

// File: contracts/ISANPASS.sol



//SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

interface ISANPASS {
    struct factionCredits {
        uint8 chi;
        uint8 umi;
        uint8 sora;
        uint8 mecha;
        uint8 none;
    }

    enum Id {
        UNUSED,
        Chi,
        Umi,
        Sora,
        Mecha,
        None,
        VIP,
        Redvoxx
    }

    enum SaleState {
        Paused, // 0
        Open    // 1
    }

    event Sacrifice(
        address indexed sacrificer
    );

    event SaleStateChanged(
        SaleState newSaleState
    );

    error ExceedsMaxRoyaltiesPercentage();
    error SaleStateNotActive();
}

// File: contracts/SAN1155.sol



// SPDX-License-Identifier: MIT
// Based on OpenZeppelin Contracts (last updated v4.9.0) (token/ERC1155/ERC1155.sol)

pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC1155/IERC1155.sol";
import "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol";
import "@openzeppelin/contracts/token/ERC1155/extensions/IERC1155MetadataURI.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import "@openzeppelin/contracts/utils/Context.sol";
import "@openzeppelin/contracts/utils/introspection/ERC165.sol";

/**
 * @dev Implementation of the basic standard multi-token.
 * See https://eips.ethereum.org/EIPS/eip-1155
 * Originally based on code by Enjin: https://github.com/enjin/erc-1155
 *
 * _Available since v3.1._
 */
contract SAN1155 is Context, ERC165, IERC1155, IERC1155MetadataURI {
    using Address for address;

    // Mapping from token ID to account balances
    mapping(uint256 => mapping(address => uint256)) private _balances;

    // Mapping from account to operator approvals
    mapping(address => mapping(address => bool)) private _operatorApprovals;

    // Used as the URI for all token types by relying on ID substitution, e.g. https://token-cdn-domain/{id}.json
    string private _uri;

    /**
     * @dev See {_setURI}.
     */
    constructor(string memory uri_) {
        _setURI(uri_);
    }

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
        return
            interfaceId == type(IERC1155).interfaceId ||
            interfaceId == type(IERC1155MetadataURI).interfaceId ||
            super.supportsInterface(interfaceId);
    }

    /**
     * @dev See {IERC1155MetadataURI-uri}.
     *
     * This implementation returns the same URI for *all* token types. It relies
     * on the token type ID substitution mechanism
     * https://eips.ethereum.org/EIPS/eip-1155#metadata[defined in the EIP].
     *
     * Clients calling this function must replace the `\{id\}` substring with the
     * actual token type ID.
     */
    function uri(uint256) public view virtual override returns (string memory) {
        return _uri;
    }

    /**
     * @dev See {IERC1155-balanceOf}.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     */
    function balanceOf(address account, uint256 id) public view virtual override returns (uint256) {
        require(account != address(0), "ERC1155: address zero is not a valid owner");
        return _balances[id][account];
    }

    /**
     * @dev See {IERC1155-balanceOfBatch}.
     *
     * Requirements:
     *
     * - `accounts` and `ids` must have the same length.
     */
    function balanceOfBatch(
        address[] memory accounts,
        uint256[] memory ids
    ) public view virtual override returns (uint256[] memory) {
        require(accounts.length == ids.length, "ERC1155: accounts and ids length mismatch");

        uint256[] memory batchBalances = new uint256[](accounts.length);

        for (uint256 i = 0; i < accounts.length; ++i) {
            batchBalances[i] = balanceOf(accounts[i], ids[i]);
        }

        return batchBalances;
    }

    /**
     * @dev See {IERC1155-setApprovalForAll}.
     */
    function setApprovalForAll(address operator, bool approved) public virtual override {
        _setApprovalForAll(_msgSender(), operator, approved);
    }

    /**
     * @dev See {IERC1155-isApprovedForAll}.
     */
    function isApprovedForAll(address account, address operator) public view virtual override returns (bool) {
        return _operatorApprovals[account][operator];
    }

    /**
     * @dev See {IERC1155-safeTransferFrom}.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 id,
        uint256 amount,
        bytes memory data
    ) public virtual override {
        require(
            from == _msgSender() || isApprovedForAll(from, _msgSender()),
            "ERC1155: caller is not token owner or approved"
        );
        _safeTransferFrom(from, to, id, amount, data);
    }

    /**
     * @dev See {IERC1155-safeBatchTransferFrom}.
     */
    function safeBatchTransferFrom(
        address from,
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) public virtual override {
        require(
            from == _msgSender() || isApprovedForAll(from, _msgSender()),
            "ERC1155: caller is not token owner or approved"
        );
        _safeBatchTransferFrom(from, to, ids, amounts, data);
    }

    /**
     * @dev Transfers `amount` tokens of token type `id` from `from` to `to`.
     *
     * Emits a {TransferSingle} event.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - `from` must have a balance of tokens of type `id` of at least `amount`.
     * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
     * acceptance magic value.
     */
    function _safeTransferFrom(
        address from,
        address to,
        uint256 id,
        uint256 amount,
        bytes memory data
    ) internal virtual {
        require(to != address(0), "ERC1155: transfer to the zero address");

        address operator = _msgSender();
        uint256[] memory ids = _asSingletonArray(id);
        uint256[] memory amounts = _asSingletonArray(amount);

        _beforeTokenTransfer(operator, from, to, ids, amounts, data);

        uint256 fromBalance = _balances[id][from];
        require(fromBalance >= amount, "ERC1155: insufficient balance for transfer");
        unchecked {
            _balances[id][from] = fromBalance - amount;
        }
        _balances[id][to] += amount;

        emit TransferSingle(operator, from, to, id, amount);

        _afterTokenTransfer(operator, from, to, ids, amounts, data);

        _doSafeTransferAcceptanceCheck(operator, from, to, id, amount, data);
    }

    /**
     * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_safeTransferFrom}.
     *
     * Emits a {TransferBatch} event.
     *
     * Requirements:
     *
     * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
     * acceptance magic value.
     */
    function _safeBatchTransferFrom(
        address from,
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) internal virtual {
        require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");
        require(to != address(0), "ERC1155: transfer to the zero address");

        address operator = _msgSender();

        _beforeTokenTransfer(operator, from, to, ids, amounts, data);

        for (uint256 i = 0; i < ids.length; ++i) {
            uint256 id = ids[i];
            uint256 amount = amounts[i];

            uint256 fromBalance = _balances[id][from];
            require(fromBalance >= amount, "ERC1155: insufficient balance for transfer");
            unchecked {
                _balances[id][from] = fromBalance - amount;
            }
            _balances[id][to] += amount;
        }

        emit TransferBatch(operator, from, to, ids, amounts);

        _afterTokenTransfer(operator, from, to, ids, amounts, data);

        _doSafeBatchTransferAcceptanceCheck(operator, from, to, ids, amounts, data);
    }

    /**
     * @dev Sets a new URI for all token types, by relying on the token type ID
     * substitution mechanism
     * https://eips.ethereum.org/EIPS/eip-1155#metadata[defined in the EIP].
     *
     * By this mechanism, any occurrence of the `\{id\}` substring in either the
     * URI or any of the amounts in the JSON file at said URI will be replaced by
     * clients with the token type ID.
     *
     * For example, the `https://token-cdn-domain/\{id\}.json` URI would be
     * interpreted by clients as
     * `https://token-cdn-domain/000000000000000000000000000000000000000000000000000000000004cce0.json`
     * for token type ID 0x4cce0.
     *
     * See {uri}.
     *
     * Because these URIs cannot be meaningfully represented by the {URI} event,
     * this function emits no events.
     */
    function _setURI(string memory newuri) internal virtual {
        _uri = newuri;
    }

    /**
     * @dev Creates `amount` tokens of token type `id`, and assigns them to `to`.
     *
     * Emits a {TransferSingle} event.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
     * acceptance magic value.
     */
    function _mint(address to, uint256 id, uint256 amount, bytes memory data) internal virtual {
        require(to != address(0), "ERC1155: mint to the zero address");

        address operator = _msgSender();
        uint256[] memory ids = _asSingletonArray(id);
        uint256[] memory amounts = _asSingletonArray(amount);

        _beforeTokenTransfer(operator, address(0), to, ids, amounts, data);

        _balances[id][to] += amount;
        emit TransferSingle(operator, address(0), to, id, amount);

        _afterTokenTransfer(operator, address(0), to, ids, amounts, data);

        _doSafeTransferAcceptanceCheck(operator, address(0), to, id, amount, data);
    }

    /**
     * @dev Creates `amount` tokens of token type `id`, and assigns them to `to`.
     *
     * Emits a {TransferSingle} event.
     */
    function _mintSimple(
        address to,
        uint256 id,
        uint256 amount
    ) internal virtual {
        _balances[id][to] += amount;
        emit TransferSingle(_msgSender(), address(0), to, id, amount);
    }

    /**
     * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_mint}.
     *
     * Emits a {TransferBatch} event.
     *
     * Requirements:
     *
     * - `ids` and `amounts` must have the same length.
     * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
     * acceptance magic value.
     */
    function _mintBatch(
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) internal virtual {
        require(to != address(0), "ERC1155: mint to the zero address");
        require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");

        address operator = _msgSender();

        _beforeTokenTransfer(operator, address(0), to, ids, amounts, data);

        for (uint256 i = 0; i < ids.length; i++) {
            _balances[ids[i]][to] += amounts[i];
        }

        emit TransferBatch(operator, address(0), to, ids, amounts);

        _afterTokenTransfer(operator, address(0), to, ids, amounts, data);

        _doSafeBatchTransferAcceptanceCheck(operator, address(0), to, ids, amounts, data);
    }

    /**
     * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_mint}.
     *
     * Emits a {TransferBatch} event.
     *
     * Requirements:
     *
     * - `ids` and `amounts` must have the same length.
     */
    function _mintBatchSimple(
        address to,
        uint256[] memory ids,
        uint256[] memory amounts
    ) internal virtual {
        for (uint256 i = 0; i < ids.length; ++i) {
            _balances[ids[i]][to] += amounts[i];
        }
        emit TransferBatch(_msgSender(), address(0), to, ids, amounts);
    }

    /**
     * @dev Destroys `amount` tokens of token type `id` from `from`
     *
     * Emits a {TransferSingle} event.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `from` must have at least `amount` tokens of token type `id`.
     */
    function _burn(address from, uint256 id, uint256 amount) internal virtual {
        require(from != address(0), "ERC1155: burn from the zero address");

        address operator = _msgSender();
        uint256[] memory ids = _asSingletonArray(id);
        uint256[] memory amounts = _asSingletonArray(amount);

        _beforeTokenTransfer(operator, from, address(0), ids, amounts, "");

        uint256 fromBalance = _balances[id][from];
        require(fromBalance >= amount, "ERC1155: burn amount exceeds balance");
        unchecked {
            _balances[id][from] = fromBalance - amount;
        }

        emit TransferSingle(operator, from, address(0), id, amount);

        _afterTokenTransfer(operator, from, address(0), ids, amounts, "");
    }

    /**
     * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_burn}.
     *
     * Emits a {TransferBatch} event.
     *
     * Requirements:
     *
     * - `ids` and `amounts` must have the same length.
     */
    function _burnBatch(address from, uint256[] memory ids, uint256[] memory amounts) internal virtual {
        require(from != address(0), "ERC1155: burn from the zero address");
        require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");

        address operator = _msgSender();

        _beforeTokenTransfer(operator, from, address(0), ids, amounts, "");

        for (uint256 i = 0; i < ids.length; i++) {
            uint256 id = ids[i];
            uint256 amount = amounts[i];

            uint256 fromBalance = _balances[id][from];
            require(fromBalance >= amount, "ERC1155: burn amount exceeds balance");
            unchecked {
                _balances[id][from] = fromBalance - amount;
            }
        }

        emit TransferBatch(operator, from, address(0), ids, amounts);

        _afterTokenTransfer(operator, from, address(0), ids, amounts, "");
    }

    /**
     * @dev Approve `operator` to operate on all of `owner` tokens
     *
     * Emits an {ApprovalForAll} event.
     */
    function _setApprovalForAll(address owner, address operator, bool approved) internal virtual {
        require(owner != operator, "ERC1155: setting approval status for self");
        _operatorApprovals[owner][operator] = approved;
        emit ApprovalForAll(owner, operator, approved);
    }

    /**
     * @dev Hook that is called before any token transfer. This includes minting
     * and burning, as well as batched variants.
     *
     * The same hook is called on both single and batched variants. For single
     * transfers, the length of the `ids` and `amounts` arrays will be 1.
     *
     * Calling conditions (for each `id` and `amount` pair):
     *
     * - When `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * of token type `id` will be  transferred to `to`.
     * - When `from` is zero, `amount` tokens of token type `id` will be minted
     * for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens of token type `id`
     * will be burned.
     * - `from` and `to` are never both zero.
     * - `ids` and `amounts` have the same, non-zero length.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _beforeTokenTransfer(
        address operator,
        address from,
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) internal virtual {}

    /**
     * @dev Hook that is called after any token transfer. This includes minting
     * and burning, as well as batched variants.
     *
     * The same hook is called on both single and batched variants. For single
     * transfers, the length of the `id` and `amount` arrays will be 1.
     *
     * Calling conditions (for each `id` and `amount` pair):
     *
     * - When `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * of token type `id` will be  transferred to `to`.
     * - When `from` is zero, `amount` tokens of token type `id` will be minted
     * for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens of token type `id`
     * will be burned.
     * - `from` and `to` are never both zero.
     * - `ids` and `amounts` have the same, non-zero length.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _afterTokenTransfer(
        address operator,
        address from,
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) internal virtual {}

    function _doSafeTransferAcceptanceCheck(
        address operator,
        address from,
        address to,
        uint256 id,
        uint256 amount,
        bytes memory data
    ) private {
        if (to.isContract()) {
            try IERC1155Receiver(to).onERC1155Received(operator, from, id, amount, data) returns (bytes4 response) {
                if (response != IERC1155Receiver.onERC1155Received.selector) {
                    revert("ERC1155: ERC1155Receiver rejected tokens");
                }
            } catch Error(string memory reason) {
                revert(reason);
            } catch {
                revert("ERC1155: transfer to non-ERC1155Receiver implementer");
            }
        }
    }

    function _doSafeBatchTransferAcceptanceCheck(
        address operator,
        address from,
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) private {
        if (to.isContract()) {
            try IERC1155Receiver(to).onERC1155BatchReceived(operator, from, ids, amounts, data) returns (
                bytes4 response
            ) {
                if (response != IERC1155Receiver.onERC1155BatchReceived.selector) {
                    revert("ERC1155: ERC1155Receiver rejected tokens");
                }
            } catch Error(string memory reason) {
                revert(reason);
            } catch {
                revert("ERC1155: transfer to non-ERC1155Receiver implementer");
            }
        }
    }

    function _asSingletonArray(uint256 element) private pure returns (uint256[] memory) {
        uint256[] memory array = new uint256[](1);
        array[0] = element;

        return array;
    }
}

// File: contracts/SAN1155Burnable.sol



// SPDX-License-Identifier: MIT
// Based on OpenZeppelin Contracts (last updated v4.9.0) (token/ERC1155/extensions/ERC1155Burnable.sol)

pragma solidity ^0.8.0;

import "./SAN1155.sol";

/**
 * @dev Extension of {ERC1155} that allows token holders to destroy both their
 * own tokens and those that they have been approved to use.
 *
 * _Available since v3.1._
 */
abstract contract SAN1155Burnable is SAN1155 {
    function burn(address account, uint256 id, uint256 value) public virtual {
        require(
            account == _msgSender() || isApprovedForAll(account, _msgSender()),
            "ERC1155: caller is not token owner or approved"
        );

        _burn(account, id, value);
    }

    function burnBatch(address account, uint256[] memory ids, uint256[] memory values) public virtual {
        require(
            account == _msgSender() || isApprovedForAll(account, _msgSender()),
            "ERC1155: caller is not token owner or approved"
        );

        _burnBatch(account, ids, values);
    }
}

// File: contracts/SANMetadata.sol



//SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

abstract contract SANMetadata {
    uint256[157] internal sanFactions = [
        8172118231391141287902840013113466638914952054539639771171260499640339993648,
        29430496281837262956462947599698477680114015847728529588968404981256659936273,
        25393357345252657687578348685524716277208190960890993650346451664004748415256,
        29633808514264345144814183736505347293089797980234916103205276024459315196450,
        30997794508970546932695339362737748626051520564294775271332235480990298149908,
        11088851274543046612534596707831127883620304102107090617791341461734175281432,
        8177327568511719525707267028046949619428579739711613370657212349738512499784,
        15947811451703475418972253741887238191481624435199846522869281518231147856945,
        7732193023099147176953953278980406430491563238064738280913434807607180661825,
        9279612382574686681127402412493149950275823556413062368439613207619681993091,
        22658516974125200663226834582367883215142918725578025433782700808299578135570,
        8206179891065982827427272001249707722201712208641054826683030142483053420835,
        7922661334880222342750658733499771493946070122641381176098344693785380001812,
        59936872091004013524380705450449207157309738970033980571097119443151256555907,
        59763727181727881726613243756862669986082819893133680704279900791580211118372,
        16313423641394527132297668070425006155613871900757179163005487788583621370920,
        32807710136091177259795693570944504261451327778937887755711694487830293202049,
        11086090568948432251399591338827340045964156117020709285395413555789900821521,
        32694677702357318909697182374101968047166310143660976387555175633629225423891,
        59741537974660048614959151045796085506410578882840771801801223085464541729042,
        30420697163066095140688690768034017312874453646077987860331771285596214018337,
        59317501176545182987983055501064816389561841843570449424820562612165178564930,
        18128080585176152444226794394735506793010239035707070698456805004017219474466,
        59820369748313283159604229631108345940410280779207928885706028620957823275521,
        9105032894229945979105849675996708476204746294421801896081683924030073348370,
        7724773588395726504537392581594837412617259706143598140956725063426284720776,
        9081712998843592744859719028837281468254819189086029483406152032589141133377,
        7719480896013179689619835903790787133686105740596302572778228651474042496017,
        29628424614224332540965429694860414685338676695152231766033395904753520097298,
        54511214746616474442643623328358672632929331478507643079965251961821340373640,
        22234453054840859553098624345198537305934422425002756478823506271941147509267,
        29520819839049765751463059447351288650979519592335532661942191210406976176420,
        32681867548114573527903866670953285211493413185659946403004534634388373186632,
        61550798859513052226319193038385183550975087594270352771153016437819051153474,
        15381343686555675198625837724496474335967578587758905414302559721898038010132,
        9135069567159577094657816353681206751158404281086690641252458890717686929537,
        9081871737332460493573653020044347955401455119847851222293132688375540757272,
        61550002222680535540868527270329878311101564686932243461766571283725338034979,
        7776011754646192775870497704013965991376921292084685393843908121745900577923,
        14956481243320019057128777023884867582768003464929639005082027454609225814305,
        29460857037130715708472147241790461336840822459574758422587740808722246801537,
        7804662192125122262488463525395677988217972393957152748229385215400958236737,
        16345571975134151524723856104597707298006513969748379071222127886997191201809,
        1930290801649354003037154680512703862422469877399048408699838160540681913409,
        9161552479798262294808714797135922551717114425339838233718338657275939459394,
        9163367650652110614221988545811969337436801931252679528384034495531713630529,
        30878310101257591734203548147473781811890367630001158517376791124879226704148,
        14968894461810922424526801024104035218241900763753304260242493607671296963617,
        7781312295208728700266849707569993668990375088979278286567528264856480532514,
        58831207122077829432536220416167288636416861121489561311154467846797017760840,
        8629531363070578469553808093148346916113633350920319245295629738366675993122,
        8184264015679463629736402535750205736372007151659699638958189898106081984836,
        58384150388973897156342146629786450514179565642402337055143132869417973661988,
        30086120942862244076439483773583852382575217475948796333584092944396241343524,
        29515347613248869922164729238111959970930414451867583705273448198710947774754,
        15861448982692692723094945921241832781274338385856927300603350689062250971970,
        58915641364356307619610933434887006328905727001204775738328257909291242047506,
        15439062559552472722262835875647857868931696304181565188403069953132124508436,
        58578168589112190418306744769771666958929054071653652686521159429823599313288,
        22651449666943389159509409546856775370255209959338661747655230479062751527971,
        15053993410547815546402806987002554811281051729197611836865456433392468379665,
        10885676359443946269573432145611928119760386413378449731493144620769641763348,
        58379284745630218013772971135911998638054713042276733123832144651092255605025,
        30081470261092880370672268486716293884403700016492353345477235669139595624737,
        15414471178988014762412703782285721036568728475467899549995815774396217369394,
        61635190852639421837480730153301989699953067529803927690300442917335623353473,
        59381087423755638584105705821127647667242377751690498835763970266719601234241,
        32597462216757577037690148587742963189226978300773147961076896020280702669076,
        7760441468815460772416197749933621753223477978656511026143624144710656987713,
        22647680391443324623972484993232894735679362650193453101335423707478540685587,
        58832639665131307539182567387683123701722501623388796428834748103299496940163,
        9161227342617692961930545348893115867939966098234397662221887383427943203880,
        29436584053262656794074259411899676108226457392972874221666853586799215915592,
        58919299378901958461615980459615630053215430822319726658496977311795323741202,
        9077184600677909997355458142920276892031239216190153190934761701037369856836,
        7922771752086562244729203399317346822523778506575454357856424035780865896996,
        30877662808841169336788975123896264785049837300333434552708488579143763575108,
        18320907613844533292751817027897276660470379544256007352663845968142972227857,
        29458869519200677288635797635943928076874674925272773781010981736119685908612,
        7816700541295837429166699168715118405651409126702046530381457977260871061793,
        8172117642971865382509551405586076598352517867362645145788848291509985350020,
        30884607582636238376688159537160509304780893845202319993972721250883615605800,
        15947686785944546719126033119591537898259928997172150328413734892491378861186,
        29432257111843840413687835110230446472769802505800917752116821178610230576259,
        58463651632201661056879485098512105112808060433249410096150653852887898227730,
        9133219926421052347829775587718240732621081384348791434033732748748164121154,
        14986514605368367630291343514865814426723169944001884025978078478769573888328,
        482815177440433400996510302924393273760071340059003585637486742121610314056,
        18184508400905165243301468164704948518441919840393895691552928340118211930177,
        7919285167096682777950670241738823581537981936347043761782765672151624270913,
        9076542808796807190701330266157019447791005655554515664774253695187794625554,
        11089181279244625167290053576865012756806028323784489085910951524519560417672,
        29430602807404152826132512338038980005611524257525931141525286363670086174754,
        58378511850967688060214862156809051706389268801674103609534274573460433307668,
        15409170638522879074807689126316470651451916816635672889374718809718937190673,
        7919126765520006992943528651317569738154716863411979750993340672372268671880,
        8212549798498744969233131531737736323503099404474953908075346663859304874627,
        30800175448038888869657646541091517237949490798475613948378233743061441202225,
        9104951177532444608725890968358270005567297754048593832611298965239231689860,
        29882913256535250203919092446015988626467535585540679822518815837706581586241,
        10916044048693906167811694920408005573788761400169615202936077671163041485860,
        29432256287670589846708071460951565705925576448437653848610899542031209890836,
        59832647697530342123789279541362402494717011505218214655869355735021545079588,
        8177092993497141878753483302460864944297130675028783581747737501834517419076,
        25360028719254451686922070844937595710971837034614379859117178333859680625681,
        58832716905629090012195868554020235741295226377871085382873782022547202904337,
        59820593246436527130453319037056164810099783636570531345200427056839272960577,
        9110143027612407915852016383999070166905443635773460386263721450519655490632,
        29517887484865341834141870728567969144663309147287764638522578900795306575908,
        7724884541087671635313643720744678003849405834964728681063716849751858287171,
        16354077946152720651098549421202572709864074079658255062534725168023274132001,
        32596728063821849205936785411072974252430948291212970494142995116040175960449,
        14961897358113198031895445545500928056207096895267923620456645746377023045912,
        7804620754703959264998146204200789105849810643720346027788960506044155529252,
        30789194745696896284545943566893102130995184126838261909030091587651733979778,
        7804329587912218274044180418544974872451058204566361903530580090573987844113,
        9108373148950256516445128993655813529536562471291678514171507847612133049106,
        7782092241325276821098579226724105734118240193667139527180479772775497875745,
        59740971600079947932652969114684696832392884803266147373072289599426157363489,
        32602090257247291584668853002254863775771923774410548599107217076355150790978,
        30364178391888833056055093622011911355261467501799516041604220402618874341432,
        52504091577161782951263159698826391888519489110675323940732900688282970689825,
        58468974144010741211088368073001205522620743909184176389097654687386950570305,
        58378851163755707925112702231548333772853514824967149444250921138712964335747,
        15044821921567160847671994771529826739865863916742587353197246753838834197122,
        61633820048253208237780679929353127804637823074835411164885089413827901202705,
        30082566425334100739577578187700861008818009426030746145927348935517654844433,
        29517197310632203257331820737207728388431425704929817372945201758670002853905,
        22843793919025490602889118250176824869815824329767152936366203540161674379537,
        7804662165064853769029125147403363242146447969830960339656245657144793318417,
        7719480386079208864888593181814689760106277073416509806999618945210533446721,
        23154808203569452641614975536642174799422931162952506746663755070477988664353,
        9077184239739900229384440911828367453293098337174049617466251011156143642696,
        7724884879147533867263510158513186707447765415668976955865282993571925082692,
        8624450307431717967847067438041044117314276770575310198945215619729579910161,
        11096251665219770268006938701094322433220939793517266342136515860347251425608,
        18207594204644815656864056905318972256083864277784189705883631433169115826194,
        7721399500515348348661107614326678699946111247309055324239508973234228905000,
        15421980793673256746047779094971433572297843431398115302727707946075653478465,
        22391702789909481066896063426322104521228219453666518491582627633227695735108,
        7719589950563992452580558762830223631705120467838110517193998308157743698193,
        16511656042692248558091511266205678535401041052836629397815776545433734105219,
        15166734326800087547408575599610078473919221186706417353106760413502458892676,
        9286901053021052224098792764795343822791182863527444981694086464759454270530,
        16516626150716496050644186894939428304696816162704363971122239221952139039809,
        18163526914763767899331791030754554122768168368131712130707894892794003818514,
        15025385256384236496728937731960083631951853718428290655414189941119032300561,
        59945725737492287429430450583399234769023828036754090083405292230104584360257,
        9076418494696298363416961832233152239097257296837549280690229793870547747972,
        58380630687368839487974706061173643230902433212080218288050159251844222779672,
        30093078214590859114762106918046306738439385700409745255103886459112651952712,
        8201828847250426851529542860056357012140382567961574638609781982361597253704,
        32596791009940788336351669824869748939752665749581006758247781131787358507283,
        59820590157961876402325001349477133615477905733350932598294127284781752533795,
        32625724888840686694879571288271770915486989677524507531858137882822428596248,
        30990622720003927274502257421688706802332158329987730486666196895749675159844,
        46270568071285715492
    ];
}

// File: contracts/SANPASS.sol



//SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

/**                       ███████╗ █████╗ ███╗   ██╗
 *                        ██╔════╝██╔══██╗████╗  ██║
 *                        ███████╗███████║██╔██╗ ██║
 *                        ╚════██║██╔══██║██║╚██╗██║
 *                        ███████║██║  ██║██║ ╚████║
 *                        ╚══════╝╚═╝  ╚═╝╚═╝  ╚═══╝
 *
 *                              █████████████╗
 *                              ╚════════════╝
 *                               ███████████╗
 *                               ╚══════════╝
 *                            █████████████████╗
 *                            ╚════════════════╝
 *
 *                     ██████╗  █████╗ ███████╗███████╗
 *                     ██╔══██╗██╔══██╗██╔════╝██╔════╝
 *                     ██████╔╝███████║███████╗███████╗
 *                     ██╔═══╝ ██╔══██║╚════██║╚════██║
 *                     ██║     ██║  ██║███████║███████║
 *                     ╚═╝     ╚═╝  ╚═╝╚══════╝╚══════╝
 */

import "./token/ERC2981ContractWideRoyalties.sol";
import "./token/TokenRescuer.sol";
import "./ISAN.sol";
import "./ISANPASS.sol";
import "./SAN1155Burnable.sol";
import "./SANMetadata.sol";
import "./SANSoulbindable.sol";

/**
 * @title SAN Concert Pass
 * @author Aaron Hanson <coffee.becomes.code@gmail.com> @CoffeeConverter
 * @notice https://sansound.io
 */
contract SANPASS is
    ISANPASS,
    SAN1155Burnable,
    ERC2981ContractWideRoyalties,
    TokenRescuer,
    SANMetadata,
    SANSoulbindable
{
    uint256 public constant MAX_ROYALTIES_PCT = 333; // 3.33%

    address public constant BURN_ADDRESS =
        0x000000000000000000000000000000000000dEaD;

    uint256 private constant MaskChi   = 1<<0;
    uint256 private constant MaskUmi   = 1<<1;
    uint256 private constant MaskSora  = 1<<2;
    uint256 private constant MaskMecha = 1<<3;

    ISAN public immutable SAN;

    string public contractURI;

    SaleState public saleState;

    mapping(address => factionCredits) public userFactionCredits;

    constructor(
        string memory _contractURI,
        string memory _baseURI,
        address _royaltiesReceiver,
        uint256 _royaltiesPercent,
        address _sanContract
    )
        SAN1155(_baseURI)
    {
        contractURI = _contractURI;
        setRoyalties(
            _royaltiesReceiver,
            _royaltiesPercent
        );
        SAN = ISAN(_sanContract);
    }

    /**
     * @notice Mints SANPASS tokens by burning SAN Origin tokens.
     * @param _tokenIds The list of SAN Origin tokens to burn.
     */
    function mintPasses(
        uint256[] calldata _tokenIds
    )
        external
    {
        if (saleState != SaleState.Open) revert SaleStateNotActive();

        factionCredits memory credits = userFactionCredits[_msgSender()];

        unchecked {
            for (uint i; i < _tokenIds.length; ++i) {
                uint256 factions = sanTokenFactions(_tokenIds[i]);
                if (factions == 0) ++credits.none;
                else if (0 < factions & MaskMecha) ++credits.mecha;
                else {
                    if (0 < factions & MaskChi) ++credits.chi;
                    if (0 < factions & MaskUmi) ++credits.umi;
                    if (0 < factions & MaskSora) ++credits.sora;
                }
            }
        }

        unchecked {
            uint256 mintChi = credits.chi / 3;
            uint256 mintUmi = credits.umi / 3;
            uint256 mintSora = credits.sora / 3;
            uint256 mintMecha = credits.mecha / 3;
            uint256 mintNone = credits.none / 3;
            uint256 idCount = (mintChi == 0 ? 0 : 1) + (mintUmi == 0 ? 0 : 1) +
                (mintSora == 0 ? 0 : 1) + (mintMecha == 0 ? 0 : 1) +
                (mintNone == 0 ? 0 : 1);
            if (1 == idCount) {
                if (mintChi != 0) {
                    _mintSimple(_msgSender(), uint256(Id.Chi), mintChi);
                    credits.chi = credits.chi % 3;
                }
                else if (mintUmi != 0) {
                    _mintSimple(_msgSender(), uint256(Id.Umi), mintUmi);
                    credits.umi = credits.umi % 3;
                }
                else if (mintSora != 0) {
                    _mintSimple(_msgSender(), uint256(Id.Sora), mintSora);
                    credits.sora = credits.sora % 3;
                }
                else if (mintMecha != 0) {
                    _mintSimple(_msgSender(), uint256(Id.Mecha), mintMecha);
                    credits.mecha = credits.mecha % 3;
                }
                else {
                    _mintSimple(_msgSender(), uint256(Id.None), mintNone);
                    credits.none = credits.none % 3;
                }
            }
            else if (1 < idCount) {
                uint256[] memory ids = new uint256[](idCount);
                uint256[] memory amounts = new uint256[](idCount);
                uint256 curIndex;
                if (mintChi != 0) {
                    ids[curIndex] = uint256(Id.Chi);
                    amounts[curIndex++] = mintChi;
                    credits.chi = credits.chi % 3;
                }
                if (mintUmi != 0) {
                    ids[curIndex] = uint256(Id.Umi);
                    amounts[curIndex++] = mintUmi;
                    credits.umi = credits.umi % 3;
                }
                if (mintSora != 0) {
                    ids[curIndex] = uint256(Id.Sora);
                    amounts[curIndex++] = mintSora;
                    credits.sora = credits.sora % 3;
                }
                if (mintMecha != 0) {
                    ids[curIndex] = uint256(Id.Mecha);
                    amounts[curIndex++] = mintMecha;
                    credits.mecha = credits.mecha % 3;
                }
                if (mintNone != 0) {
                    ids[curIndex] = uint256(Id.None);
                    amounts[curIndex] = mintNone;
                    credits.none = credits.none % 3;
                }
                _mintBatchSimple(_msgSender(), ids, amounts);
            }
        }

        userFactionCredits[_msgSender()] = credits;
        SAN.batchTransferFrom(_msgSender(), BURN_ADDRESS, _tokenIds);
        emit Sacrifice(_msgSender());
    }

    /**
     * @notice (only owner) Mints VIP tokens to a list of recipients.
     * @param _recipients The list of token recipients.
     * @param _amounts The list of token amounts.
     */
    function airdropVIP(
        address[] calldata _recipients,
        uint256[] calldata _amounts
    )
        external
        onlyOwner
    {
        if (_recipients.length != _amounts.length) revert ArrayLengthMismatch();
        unchecked {
            for (uint i; i < _recipients.length; ++i) {
                _mintSimple(
                    _recipients[i],
                    uint256(Id.VIP),
                    _amounts[i]
                );
            }
        }
    }

    /**
     * @notice (only owner) Mints Redvoxx tokens to a list of recipients.
     * @param _recipients The list of token recipients.
     * @param _amounts The list of token amounts.
     */
    function airdropRedvoxx(
        address[] calldata _recipients,
        uint256[] calldata _amounts
    )
        external
        onlyOwner
    {
        if (_recipients.length != _amounts.length) revert ArrayLengthMismatch();
        unchecked {
            for (uint i; i < _recipients.length; ++i) {
                _mintSimple(
                    _recipients[i],
                    uint256(Id.Redvoxx),
                    _amounts[i]
                );
            }
        }
    }

    /**
     * @notice (only owner) Sets the contract URI for contract metadata.
     * @param _newContractURI The new contract URI.
     */
    function setContractURI(
        string calldata _newContractURI
    )
        external
        onlyOwner
    {
        contractURI = _newContractURI;
    }

    /**
     * @notice (only owner) Sets the saleState to `_newSaleState`.
     * @param _newSaleState The new sale state
     * (0=Paused, 1=Open).
     */
    function setSaleState(
        SaleState _newSaleState
    )
        external
        onlyOwner
    {
        saleState = _newSaleState;
        emit SaleStateChanged(_newSaleState);
    }

    /**
     * @notice (only owner) Sets the token URI for token metadata.
     * @param _newURI The new URI.
     */
    function setURI(
        string calldata _newURI
    )
        external
        onlyOwner
    {
        _setURI(_newURI);
    }

    /**
     * @notice (only owner) Sets ERC-2981 royalties recipient and percentage.
     * @param _recipient The address to which to send royalties.
     * @param _value The royalties percentage (two decimals, e.g. 1000 = 10%).
     */
    function setRoyalties(
        address _recipient,
        uint256 _value
    )
        public
        onlyOwner
    {
        if (_value > MAX_ROYALTIES_PCT) revert ExceedsMaxRoyaltiesPercentage();

        _setRoyalties(
            _recipient,
            _value
        );
    }

    /**
     * @notice Looks up all factions for a SAN token ID.
     * @param _sanTokenId The SAN token ID to check.
     * @return factions_ Bitfield of five bits indication the factions.
     */
    function sanTokenFactions(
        uint256 _sanTokenId
    )
        public
        view
        returns (uint256 factions_)
    {
        uint256 bucket = _sanTokenId >> 6;
        factions_ = sanFactions[bucket] >> ((_sanTokenId & 63) * 4) & 0xf;
    }

    /**
     * @inheritdoc ERC165
     */
    function supportsInterface(
        bytes4 _interfaceId
    )
        public
        view
        override (SAN1155, ERC2981Base)
        returns (bool)
    {
        return super.supportsInterface(_interfaceId);
    }

    string[119] public _takeThisEngineerMayItServeYouWell = [
        "201110", "010110", "000110", "012010", "210100", "220110", "120110",
        "001110", "100110", "210100", "220110", "020110", "121010", "210100",
        "102010", "200110", "121010", "210100", "002010", "022010", "120110",
        "001110", "100110", "210100", "202010", "001110", "020110", "220110",
        "101000", "010110", "112010", "210100", "120110", "100110", "212010",
        "220110", "111110", "212010", "020110", "210100", "202010", "020110",
        "202010", "212010", "201110", "210100", "102010", "202010", "002010",
        "022010", "012010", "022010", "020110", "002010", "121010", "120110",
        "101000", "100110", "020110", "121010", "201110", "120110", "210100",
        "002010", "022010", "220110", "110110", "111110", "020110", "002010",
        "210100", "121010", "210100", "202010", "200110", "022010", "201110",
        "220110", "200110", "202010", "210100", "120110", "202010", "220110",
        "010110", "200110", "101000", "100110", "020110", "010110", "012010",
        "120110", "200110", "121010", "020110", "220110", "210100", "120110",
        "200110", "010110", "022010", "220110", "002010", "121010", "012010",
        "210100", "000110", "000110", "121010", "210100", "012010", "010110",
        "210100", "120110", "220110", "120110", "121010", "202010", "221010"
    ];
}

// File: contracts/SANSoulbindable.sol



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

interface SANSoulbindable {
    enum SoulboundLevel { Unbound, One, Two, Three, Four }
}

// File: contracts/token/ERC2981Base.sol



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

import "@openzeppelin/contracts/utils/introspection/ERC165.sol";
import "./IERC2981Royalties.sol";

/// @dev This is a contract used to add ERC2981 support to ERC721 and 1155
abstract contract ERC2981Base is ERC165, IERC2981Royalties {
	struct RoyaltyInfo {
		address recipient;
		uint24 amount;
	}

	/// @inheritdoc	ERC165
	function supportsInterface(bytes4 interfaceId)
		public
		view
		virtual
		override
		returns (bool)
	{
		return
			interfaceId == type(IERC2981Royalties).interfaceId ||
			super.supportsInterface(interfaceId);
	}
}
// File: contracts/token/ERC2981ContractWideRoyalties.sol



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

import "@openzeppelin/contracts/utils/introspection/ERC165.sol";

import "./ERC2981Base.sol";

/// @dev This is a contract used to add ERC2981 support to ERC721 and 1155
/// @dev This implementation has the same royalties for each and every tokens
abstract contract ERC2981ContractWideRoyalties is ERC2981Base {
	RoyaltyInfo private _royalties;

	/// @dev Sets token royalties
	/// @param _recipient recipient of the royalties
	/// @param _value percentage (using 2 decimals - 10000 = 100, 0 = 0)
	function _setRoyalties(
		address _recipient,
		uint256 _value
	)
		internal
	{
		// unneeded since the derived contract has a lower _value limit
		// require(_value <= 10000, "ERC2981Royalties: Too high");
		_royalties = RoyaltyInfo(_recipient, uint24(_value));
	}

	/// @inheritdoc	IERC2981Royalties
	function royaltyInfo(
		uint256,
		uint256 _value
	)
		external
		view
		override
		returns (address receiver, uint256 royaltyAmount)
	{
		RoyaltyInfo memory royalties = _royalties;
		receiver = royalties.recipient;
		royaltyAmount = (_value * royalties.amount) / 10000;
	}
}
// File: contracts/token/IERC2981Royalties.sol



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

/// @title IERC2981Royalties
/// @dev Interface for the ERC2981 - Token Royalty standard
interface IERC2981Royalties {
	/// @notice Called with the sale price to determine how much royalty
	///         is owed and to whom.
	/// @param _tokenId - the NFT asset queried for royalty information
	/// @param _value - the sale price of the NFT asset specified by _tokenId
	/// @return _receiver - address of who should be sent the royalty payment
	/// @return _royaltyAmount - the royalty payment amount for value sale price
	function royaltyInfo(uint256 _tokenId, uint256 _value)
		external
		view
		returns (address _receiver, uint256 _royaltyAmount);
}

// File: contracts/token/IStuckTokens.sol



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

interface IStuckERC20 {
    function transfer(
        address to,
        uint256 amount
    ) external returns (bool);
}

interface IStuckERC721 {
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId
    ) external;
}
// File: contracts/token/SafeERC20.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.0;

import "./IStuckTokens.sol";
import "@openzeppelin/contracts/utils/Address.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    using Address for address;

    function safeTransfer(
        IStuckERC20 token,
        address to,
        uint256 value
    ) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    function _callOptionalReturn(IStuckERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.

        bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
        if (returndata.length > 0) {
            // Return data is optional
            require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
        }
    }
}

// File: contracts/token/TokenRescuer.sol



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

import "./IStuckTokens.sol";
import "./SafeERC20.sol";
import "../utils/Ownable.sol";

error ArrayLengthMismatch();

contract TokenRescuer is Ownable {
    using SafeERC20 for IStuckERC20;

    function rescueBatchERC20(
        address _token,
        address[] calldata _receivers,
        uint256[] calldata _amounts
    )
        external
        onlyOwner
    {
        if (_receivers.length != _amounts.length) revert ArrayLengthMismatch();
        unchecked {
            for (uint i; i < _receivers.length; i += 1) {
                _rescueERC20(_token, _receivers[i], _amounts[i]);
            }
        }
    }

    function rescueERC20(
        address _token,
        address _receiver,
        uint256 _amount
    )
        external
        onlyOwner
    {
        _rescueERC20(_token, _receiver, _amount);
    }

    function rescueBatchERC721(
        address _token,
        address[] calldata _receivers,
        uint256[][] calldata _tokenIDs
    )
        external
        onlyOwner
    {
        if (_receivers.length != _tokenIDs.length) revert ArrayLengthMismatch();
        unchecked {
            for (uint i; i < _receivers.length; i += 1) {
                uint256[] memory tokenIDs = _tokenIDs[i];
                for (uint j; j < tokenIDs.length; j += 1) {
                    _rescueERC721(_token, _receivers[i], tokenIDs[j]);
                }
            }
        }
    }

    function rescueERC721(
        address _token,
        address _receiver,
        uint256 _tokenID
    )
        external
        onlyOwner
    {
        _rescueERC721(_token, _receiver, _tokenID);
    }

    function _rescueERC20(
        address _token,
        address _receiver,
        uint256 _amount
    )
        private
    {
        IStuckERC20(_token).safeTransfer(_receiver, _amount);
    }

    function _rescueERC721(
        address _token,
        address _receiver,
        uint256 _tokenID
    )
        private
    {
        IStuckERC721(_token).safeTransferFrom(
            address(this),
            _receiver,
            _tokenID
        );
    }
}
// File: contracts/utils/Ownable.sol



// SPDX-License-Identifier: MIT
// Based on OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)
// With renounceOwnership() removed

pragma solidity ^0.8.12;

import "@openzeppelin/contracts/utils/Context.sol";

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor() {
        _transferOwnership(_msgSender());
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
        _;
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}