ETH Price: $3,400.88 (+3.00%)

Contract

0x1271f55f03ee11104E52f19A7bc8EB184B79Fd3B
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Onboard And Purc...187490042023-12-09 13:35:35380 days ago1702128935IN
0x1271f55f...84B79Fd3B
0.105 ETH0.0273531233.8174711
Onboard And Purc...187223762023-12-05 19:58:35384 days ago1701806315IN
0x1271f55f...84B79Fd3B
0.105 ETH0.0484772862.57587863
Onboard And Purc...186917842023-12-01 13:12:59388 days ago1701436379IN
0x1271f55f...84B79Fd3B
0.105 ETH0.0335766341.5117752
Onboard And Purc...186650742023-11-27 19:30:59392 days ago1701113459IN
0x1271f55f...84B79Fd3B
0.105 ETH0.0435093456.16312625
Onboard And Purc...185967642023-11-18 5:50:11402 days ago1700286611IN
0x1271f55f...84B79Fd3B
0.105 ETH0.0141629418.513776
Onboard And Purc...185508952023-11-11 19:53:11408 days ago1699732391IN
0x1271f55f...84B79Fd3B
0.105 ETH0.0208228528.33534648
Onboard And Purc...185469182023-11-11 6:31:23409 days ago1699684283IN
0x1271f55f...84B79Fd3B
0.105 ETH0.0172187421.28803571
Onboard And Purc...185446822023-11-10 22:59:35409 days ago1699657175IN
0x1271f55f...84B79Fd3B
0.105 ETH0.026887634.70730118
Onboard And Purc...185279572023-11-08 14:53:11411 days ago1699455191IN
0x1271f55f...84B79Fd3B
0.105 ETH0.0311853438.55535015
Onboard And Purc...185077532023-11-05 19:01:35414 days ago1699210895IN
0x1271f55f...84B79Fd3B
0.105 ETH0.0246969131.87948685
Onboard And Purc...185058512023-11-05 12:37:11414 days ago1699187831IN
0x1271f55f...84B79Fd3B
0.105 ETH0.0186957523.1141116
Onboard And Purc...184934442023-11-03 18:52:59416 days ago1699037579IN
0x1271f55f...84B79Fd3B
0.105 ETH0.0163039320.15702947
Onboard And Purc...184810552023-11-02 1:12:47418 days ago1698887567IN
0x1271f55f...84B79Fd3B
0.105 ETH0.020420125.55244157
Onboard And Purc...184803252023-11-01 22:45:59418 days ago1698878759IN
0x1271f55f...84B79Fd3B
0.105 ETH0.0282373936.44964099
Onboard And Purc...184769632023-11-01 11:29:59418 days ago1698838199IN
0x1271f55f...84B79Fd3B
0.105 ETH0.0108173613.96336988
Onboard And Purc...184661442023-10-30 23:07:11420 days ago1698707231IN
0x1271f55f...84B79Fd3B
0.105 ETH0.0201708424.93780336
Onboard And Purc...184654192023-10-30 20:40:47420 days ago1698698447IN
0x1271f55f...84B79Fd3B
0.105 ETH0.0232719628.77181151
Onboard And Purc...184628562023-10-30 12:04:11420 days ago1698667451IN
0x1271f55f...84B79Fd3B
0.105 ETH0.0117704314.55213323
Onboard And Purc...184580732023-10-29 19:58:59421 days ago1698609539IN
0x1271f55f...84B79Fd3B
0.105 ETH0.0109251613.50710334
Onboard And Purc...184579332023-10-29 19:30:59421 days ago1698607859IN
0x1271f55f...84B79Fd3B
0.105 ETH0.0100483712.4232862
Onboard And Purc...184577532023-10-29 18:54:59421 days ago1698605699IN
0x1271f55f...84B79Fd3B
0.105 ETH0.0107239514.01833533
Onboard And Purc...184552032023-10-29 10:19:47421 days ago1698574787IN
0x1271f55f...84B79Fd3B
0.105 ETH0.0081065710.0223915
Onboard And Purc...184527892023-10-29 2:13:11422 days ago1698545591IN
0x1271f55f...84B79Fd3B
0.105 ETH0.007658439.46835012
Onboard And Purc...184522452023-10-29 0:23:47422 days ago1698539027IN
0x1271f55f...84B79Fd3B
0.105 ETH0.0085901910.62030559
Onboard And Purc...184517352023-10-28 22:41:23422 days ago1698532883IN
0x1271f55f...84B79Fd3B
0.105 ETH0.0082499510.64927607
View all transactions

Latest 25 internal transactions (View All)

Advanced mode:
Parent Transaction Hash Block
From
To
187490042023-12-09 13:35:35380 days ago1702128935
0x1271f55f...84B79Fd3B
0.045 ETH
187490042023-12-09 13:35:35380 days ago1702128935
0x1271f55f...84B79Fd3B
0.035 ETH
187490042023-12-09 13:35:35380 days ago1702128935
0x1271f55f...84B79Fd3B
0.025 ETH
187223762023-12-05 19:58:35384 days ago1701806315
0x1271f55f...84B79Fd3B
0.045 ETH
187223762023-12-05 19:58:35384 days ago1701806315
0x1271f55f...84B79Fd3B
0.035 ETH
187223762023-12-05 19:58:35384 days ago1701806315
0x1271f55f...84B79Fd3B
0.025 ETH
186917842023-12-01 13:12:59388 days ago1701436379
0x1271f55f...84B79Fd3B
0.045 ETH
186917842023-12-01 13:12:59388 days ago1701436379
0x1271f55f...84B79Fd3B
0.035 ETH
186917842023-12-01 13:12:59388 days ago1701436379
0x1271f55f...84B79Fd3B
0.025 ETH
186650742023-11-27 19:30:59392 days ago1701113459
0x1271f55f...84B79Fd3B
0.045 ETH
186650742023-11-27 19:30:59392 days ago1701113459
0x1271f55f...84B79Fd3B
0.035 ETH
186650742023-11-27 19:30:59392 days ago1701113459
0x1271f55f...84B79Fd3B
0.025 ETH
185967642023-11-18 5:50:11402 days ago1700286611
0x1271f55f...84B79Fd3B
0.045 ETH
185967642023-11-18 5:50:11402 days ago1700286611
0x1271f55f...84B79Fd3B
0.035 ETH
185967642023-11-18 5:50:11402 days ago1700286611
0x1271f55f...84B79Fd3B
0.025 ETH
185508952023-11-11 19:53:11408 days ago1699732391
0x1271f55f...84B79Fd3B
0.045 ETH
185508952023-11-11 19:53:11408 days ago1699732391
0x1271f55f...84B79Fd3B
0.035 ETH
185508952023-11-11 19:53:11408 days ago1699732391
0x1271f55f...84B79Fd3B
0.025 ETH
185469182023-11-11 6:31:23409 days ago1699684283
0x1271f55f...84B79Fd3B
0.045 ETH
185469182023-11-11 6:31:23409 days ago1699684283
0x1271f55f...84B79Fd3B
0.035 ETH
185469182023-11-11 6:31:23409 days ago1699684283
0x1271f55f...84B79Fd3B
0.025 ETH
185446822023-11-10 22:59:35409 days ago1699657175
0x1271f55f...84B79Fd3B
0.045 ETH
185446822023-11-10 22:59:35409 days ago1699657175
0x1271f55f...84B79Fd3B
0.035 ETH
185446822023-11-10 22:59:35409 days ago1699657175
0x1271f55f...84B79Fd3B
0.025 ETH
185279572023-11-08 14:53:11411 days ago1699455191
0x1271f55f...84B79Fd3B
0.045 ETH
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
MOSOnboarding

Compiler Version
v0.8.20+commit.a1b79de6

Optimization Enabled:
Yes with 200 runs

Other Settings:
shanghai EvmVersion
File 1 of 28 : MOSOnboarding.sol
pragma solidity ^0.8.18;

import "openzeppelin/token/ERC721/IERC721.sol";
import "openzeppelin/access/Ownable.sol";
import "TokenGatedAccount/TBARegistry.sol";
import "TokenGatedAccount/TokenGatedAccount.sol";
import "./Interfaces.sol";
import "./ShipStore.sol";
import "./PillStore.sol";
import "./AzimuthOwnerWrapper.sol";

/// @title MOSOnboarding
/// @notice This contract handles onboarding for miladys to the Milady OS system, along with Ship and Pill purchases.
/// @author Logan Brutsche
contract MOSOnboarding is Ownable {
    IERC721 public immutable miladysContract;
    IERC721 public immutable avatarContract;
    AzimuthOwnerWrapper public immutable azimuthOwnerWrapperContract;
    ISoulboundAccessories public immutable soulboundAccessoriesContract;
    TBARegistry public immutable tgaRegistry;
    TokenGatedAccount public immutable tgaAccountImpl;

    ShipStore public immutable shipStoreContract;
    uint public immutable shipStackId;
    
    PillStore public immutable pillStoreContract;
    uint public immutable mosPillSetId;

    uint public onboardingGasForwardAmount;
    
    /// @notice Constructor to initialize the MOSOnboarding contract.
    /// @param _miladysContract Address of the IERC721 contract for Miladys.
    /// @param _avatarContract Address of the IERC721 contract for Avatars.
    /// @param _azimuthOwnerWrapperContract Address of the contract azimuthOwnerWrapperContract.
    /// @param _soulboundAccessoriesContract Address of the SoulboundAccessories contract.
    /// @param _tgaRegistry Address of the TokenGatedAccount registry.
    /// @param _tgaAccountImpl Address of the TokenGatedAccount implementation.
    /// @param _shipStoreContract Address of the ShipStore contract.
    /// @param _shipStackId The stack ID for ships this contract mediates purchasing of.
    /// @param _pillStoreContract Address of the PillStore contract.
    /// @param _mosPillSetId The Pill Set ID for MOS this contract mediates purchasing of.
    /// @param _onboardingGasForwardAmount The amount of gas to capture and forward to a server that uploads crucial metadata.
    /// @dev sets msg.sender to owner.
    constructor(
        IERC721 _miladysContract,
        IERC721 _avatarContract,
        AzimuthOwnerWrapper _azimuthOwnerWrapperContract,
        ISoulboundAccessories _soulboundAccessoriesContract,
        TBARegistry _tgaRegistry,
        TokenGatedAccount _tgaAccountImpl,

        ShipStore _shipStoreContract,
        uint _shipStackId,

        PillStore _pillStoreContract,
        uint _mosPillSetId,

        uint _onboardingGasForwardAmount
    )
        Ownable(msg.sender)
    {
        miladysContract = _miladysContract;
        avatarContract = _avatarContract;
        azimuthOwnerWrapperContract = _azimuthOwnerWrapperContract;
        soulboundAccessoriesContract = _soulboundAccessoriesContract;
        tgaRegistry = _tgaRegistry;
        tgaAccountImpl = _tgaAccountImpl;

        shipStoreContract = _shipStoreContract;
        shipStackId = _shipStackId;

        pillStoreContract = _pillStoreContract;
        mosPillSetId = _mosPillSetId;

        onboardingGasForwardAmount = _onboardingGasForwardAmount;
    }

    /// @notice Sets the amount of gas to forward for onboarding.
    /// @param _amount The amount of gas to forward.
    /// @dev Only callable by the contract owner.
    function setOnboardingGasForwardAmount(uint _amount)
        external
        onlyOwner()
    {
        onboardingGasForwardAmount = _amount;
    }

    /// @notice Emitted when a mint request for Soulbound is made.
    /// @param miladyId The ID of the Milady involved in the Soulbound mint request.
    event SoulboundMintRequested(uint miladyId);
    
    /// @notice Handles internal logic for onboarding a Milady.
    /// @param miladyId The ID of the Milady to onboard.
    /// @dev Creates two TGAs - one for the miladyMaker contract, one for the avatar contract.
    /// @dev Forwards the gas forwarding amount to the miladyAuthority.
    /// @dev Emits a SoulboundMintRequested event, expected to be seen and actioned by the miladyAuthority server.
    function _requestMiladyOnboard(uint miladyId)
        internal
    {
        createTGA(address(miladysContract), miladyId);
        createTGA(address(avatarContract), miladyId);

        payable(soulboundAccessoriesContract.miladyAuthority()).transfer(onboardingGasForwardAmount);

        emit SoulboundMintRequested(miladyId);
    }

    /// @notice Purchases the app package for a given address.
    /// @param _recipientAddress The recipient's address.
    /// @param _shipPrice The price for the ship.
    /// @param _appPrice The price for the app.
    /// @dev Creates a TGA for the ship, puts the pill into that TGA, and creates a TGA for the pill.
    /// @return shipId The ID of the purchased ship.
    /// @return pillId The ID of the purchased pill.
    function _purchaseAppPackage(address _recipientAddress, uint _shipPrice, uint _appPrice)
        internal
        returns(uint32 shipId, uint pillId)
    {
        shipId = shipStoreContract.buyShip{value:_shipPrice}(shipStackId, _recipientAddress);
        address shipTgaAddress = createTGA(address(azimuthOwnerWrapperContract), shipId);

        Pill pillSetContract = pillStoreContract.getPillSetContract(mosPillSetId);
        pillId = pillStoreContract.mintPill{value:_appPrice}(mosPillSetId, shipTgaAddress);
        createTGA(address(pillSetContract), pillId);
    }

    /// @notice Event emitted when a package is bought.
    /// @param topLevelEOA The address that holds the Milady.
    /// @param miladyId The ID of the associated Milady.
    /// @param shipId The ID of the purchased ship.
    /// @param pillId The ID of the purchased pill.
    event PackageBought(address indexed topLevelEOA, uint indexed miladyId, uint32 indexed shipId, uint pillId);

    /// @notice Onboard and purchase a package for a specific Milady.
    /// @param miladyId The ID of the Milady to onboard.
    /// @return shipId The ID of the purchased ship.
    /// @return pillId The ID of the purchased pill.
    /// @dev Charges the user the full amount of ether needed.
    /// @dev Effectively packages together the _requestMiladyOnboard and _purchaseAppPackage functions.
    function onboardAndPurchaseForMilady(uint miladyId)
        external
        payable
        returns(uint32 shipId, uint pillId)
    {
        (,,uint shipPrice,,,) = shipStoreContract.getStackInfo(shipStackId);
        (,,uint appPrice,,,,,,) = pillStoreContract.getPillSetInfo(mosPillSetId);

        uint totalPrice = shipPrice + appPrice + onboardingGasForwardAmount;

        require(msg.value == totalPrice, "incorrect ether amount included");

        _requestMiladyOnboard(miladyId);
        address miladyOwner = miladysContract.ownerOf(miladyId);
        // implicitly returns
        (shipId, pillId) = _purchaseAppPackage(miladyOwner, shipPrice, appPrice);

        emit PackageBought(miladyOwner, miladyId, shipId, pillId);
    }

    /// @notice Get the onboarded Azimuth points for a specific Milady.
    /// @param miladyId The ID of the Milady to check.
    /// @return numOnboardedPoints The number of valid onboarded points.
    /// @return onboardedPoints The list of onboarded points.
    /// @dev onboardedPoints may contain uninitialized elements beyond numOnboardedPoints.
    function getOnboardedAzimuthPointsForMilady(uint miladyId)
        external
        view
        returns (uint numOnboardedPoints, uint32[] memory onboardedPoints)
    {
        address miladyOwnerAddress = miladysContract.ownerOf(miladyId);

        uint32[] memory ownedPoints = azimuthOwnerWrapperContract.azimuthContract().getOwnedPoints(miladyOwnerAddress);

        onboardedPoints = new uint32[](ownedPoints.length);
        for (uint i; i<ownedPoints.length; i++) {
            address tgaAddress = getTGA(address(azimuthOwnerWrapperContract), ownedPoints[i]);
            if (pillStoreContract.getPillSetContract(mosPillSetId).balanceOf(tgaAddress) > 0) {
                onboardedPoints[numOnboardedPoints] = ownedPoints[i];
                numOnboardedPoints ++;
            }
        }
    }

    /// @notice Retrieve a set of values pertinent to the onboarding interface.
    /// @return shipPrice The current price of a ship.
    /// @return numShipsRemaining The number of ships remaining in the underlying shipStack.
    /// @return appPrice The current price of an app.
    /// @return onboardingGasPrice The amount captured and forwarded to miladyAuthority.
    function getMarketInfo()
        public
        view
        returns(uint shipPrice, uint numShipsRemaining, uint appPrice, uint onboardingGasPrice)
    {
        (,,shipPrice,,,numShipsRemaining) = shipStoreContract.getStackInfo(shipStackId);
        (,,appPrice,,,,,,) = pillStoreContract.getPillSetInfo(mosPillSetId);
        onboardingGasPrice = onboardingGasForwardAmount;
    }

    /// @notice Create a new Token Gated Account (TGA) for a given NFT contract and its ID.
    /// @param tokenContractAddress The address of the NFT contract.
    /// @param tokenId The ID of the token.
    /// @return The address of the newly created TGA.
    function createTGA(address tokenContractAddress, uint tokenId)
        internal
        returns(address payable)
    {
        return payable(tgaRegistry.createAccount(
            address(tgaAccountImpl),
            block.chainid, 
            tokenContractAddress,
            tokenId,
            0,
            ""
        ));
    }

    /// @notice Retrieve the address of an existing Token Gated Account (TGA).
    /// @param tokenContractAddress The address of the NFT contract.
    /// @param tokenId The ID of the token.
    /// @return The address of the existing TGA.
    function getTGA(address tokenContractAddress, uint tokenId)
        internal
        view
        returns(address payable)
    {
        return payable(tgaRegistry.account(
            address(tgaAccountImpl),
            block.chainid, 
            tokenContractAddress,
            tokenId,
            0
        ));
    }
}

File 2 of 28 : IERC721.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/IERC721.sol)

pragma solidity ^0.8.20;

import {IERC165} from "../../utils/introspection/IERC165.sol";

/**
 * @dev Required interface of an ERC721 compliant contract.
 */
interface IERC721 is IERC165 {
    /**
     * @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`.
     *
     * 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 approved 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;

    /**
     * @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 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) external;

    /**
     * @dev Transfers `tokenId` token from `from` to `to`.
     *
     * WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721
     * or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
     * understand this adds an external call which potentially creates a reentrancy vulnerability.
     *
     * 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;

    /**
     * @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;

    /**
     * @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 address zero.
     *
     * 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);
}

File 3 of 28 : Ownable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)

pragma solidity ^0.8.20;

import {Context} from "../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.
 *
 * The initial owner is set to the address provided by the deployer. 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;

    /**
     * @dev The caller account is not authorized to perform an operation.
     */
    error OwnableUnauthorizedAccount(address account);

    /**
     * @dev The owner is not a valid owner account. (eg. `address(0)`)
     */
    error OwnableInvalidOwner(address owner);

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

    /**
     * @dev Initializes the contract setting the address provided by the deployer as the initial owner.
     */
    constructor(address initialOwner) {
        if (initialOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(initialOwner);
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

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

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        if (owner() != _msgSender()) {
            revert OwnableUnauthorizedAccount(_msgSender());
        }
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby disabling any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @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 {
        if (newOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _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);
    }
}

File 4 of 28 : TBARegistry.sol
pragma solidity ^0.8.18;

import "openzeppelin/utils/Create2.sol";
import "./IERC6551Registry.sol";

/**
 * @title TBARegistry
 * @dev This contract implements the registry for Token Bound Accounts (TBA) as per the ERC-6551 standard.
 * It facilitates the creation and management of accounts bound to non-fungible tokens (NFTs),
 * enabling each NFT to operate as its own smart contract account.
 * 
 * The contract leverages the Ethereum Create2 opcode for deploying smart contracts at specified addresses,
 * allowing for predictable contract addresses and efficient user onboarding.
 * 
 * Each created account is registered within the contract, mapping the account address to its corresponding
 * NFT's contract address and token ID.
 *
 * The contract allows for the computation of account addresses prior to their actual creation,
 * aiding in the planning and management of TBAs.
 *
 * @author Logan Brutsche
 */
contract TBARegistry is IERC6551Registry {
    error InitializationFailed();

    struct TBA {
        address tokenContract;
        uint tokenId;
    }
    mapping (address => TBA) public registeredAccounts;

    /**
     * @dev Creates a new account with a bound non-fungible token using the provided parameters and returns the created account's address.
     * @param _implementation Address of the TBA account implementation.
     * @param _chainId Chain ID on which the account is created.
     * @param _tokenContract Address of the NFT contract.
     * @param _tokenId ID of the token to be bound to the new account.
     * @param _salt A value to modify the resulting address.
     * @param initData Initialization data to be called on the new account.
     * @return The address of the created account.
     */
    function createAccount(
        address _implementation,
        uint256 _chainId,
        address _tokenContract,
        uint256 _tokenId,
        uint256 _salt,
        bytes calldata initData
    ) external returns (address) {
        bytes memory code = _creationCode(_implementation, _chainId, _tokenContract, _tokenId, _salt);

        address _account = Create2.computeAddress(
            bytes32(_salt),
            keccak256(code)
        );

        if (_account.code.length != 0) return _account;

        _account = Create2.deploy(0, bytes32(_salt), code);

        registeredAccounts[_account].tokenContract = _tokenContract;
        registeredAccounts[_account].tokenId = _tokenId;

        if (initData.length != 0) {
            (bool success, ) = _account.call(initData);
            if (!success) revert InitializationFailed();
        }

        emit AccountCreated(
            _account,
            _implementation,
            _chainId,
            _tokenContract,
            _tokenId,
            _salt
        );

        return _account;
    }

    /**
     * @dev Computes and returns the address of the account with the provided parameters without actually creating the account.
     * @param _implementation Address of the TBA account implementation.
     * @param _chainId Chain ID for which to compute the account address.
     * @param _tokenContract Address of the token contract.
     * @param _tokenId ID of the token for which to compute the account address.
     * @param _salt A value to modify the resulting address.
     * @return The address of the computed account.
     */
    function account(
        address _implementation,
        uint256 _chainId,
        address _tokenContract,
        uint256 _tokenId,
        uint256 _salt
    ) external view returns (address) {
        bytes32 bytecodeHash = keccak256(
            _creationCode(_implementation, _chainId, _tokenContract, _tokenId, _salt)
        );

        return Create2.computeAddress(bytes32(_salt), bytecodeHash);
    }

    /**
     * @dev Generates the creation code for an account with the provided parameters.
     * @param _implementation Address of the TBA account implementation.
     * @param _chainId Chain ID on which the account is created.
     * @param _tokenContract Address of the token contract.
     * @param _tokenId ID of the token to be bound to the new account.
     * @param _salt A value to modify the resulting creation code.
     * @return The creation code for the account.
     */
    function _creationCode(
        address _implementation,
        uint256 _chainId,
        address _tokenContract,
        uint256 _tokenId,
        uint256 _salt
    ) internal pure returns (bytes memory) {
        return
            abi.encodePacked(
                hex"3d60ad80600a3d3981f3363d3d373d3d3d363d73",
                _implementation,
                hex"5af43d82803e903d91602b57fd5bf3",
                abi.encode(_salt, _chainId, _tokenContract, _tokenId)
            );
    }
}

File 5 of 28 : TokenGatedAccount.sol
pragma solidity ^0.8.18;

import "openzeppelin/utils/introspection/IERC165.sol";
import "openzeppelin/token/ERC721/IERC721.sol";
import "openzeppelin/interfaces/IERC1271.sol";
import "openzeppelin/token/ERC1155/IERC1155Receiver.sol";
import "openzeppelin/token/ERC721/IERC721Receiver.sol";
import "openzeppelin/utils/cryptography/SignatureChecker.sol";
import "sstore2/utils/Bytecode.sol";
import "./IERC6551Account.sol";
import "./IERC6551Executable.sol";

/**
 * @title TokenGatedAccount
 * @dev This contract represents a Token Gated Account (TGA) which serves as an interface for interacting with
 * various token standards and executing operations based on the ownership and authorization of tokens.
 * The contract implements multiple interfaces to ensure compatibility and extend functionality.
 *
 * @author Logan Brutsche
 */
contract TokenGatedAccount is IERC165, IERC1271, IERC6551Account, IERC6551Executable, IERC1155Receiver, IERC721Receiver {
    address public bondedAddress;
    address public tokenOwnerAtLastBond;

    /**
     * @dev Ensures that the message sender is either the token owner or the bonded account, 
     * unless the owner has changed since the last bond call.
     */
    modifier onlyAuthorizedMsgSender() {
        require(_isValidSigner(msg.sender), "Unauthorized caller");
        _;
    }
    
    /**
     * @dev Emitted when a new address is bonded to this contract.
     * @param _newBondedAddress The address that was bonded.
     */
    event NewBondedAddress(address indexed _newBondedAddress);

    /**
     * @dev Bonds a specified address to this contract.
     * Note the bonded address can pass this bond on without authorization from owner().
     * @param _addressToBond The address to bond.
     */
    function bond(address _addressToBond) 
        external
        onlyAuthorizedMsgSender()
    {
        bondedAddress = _addressToBond;
        tokenOwnerAtLastBond = owner();

        emit NewBondedAddress(_addressToBond);
    }

    uint public state;

    receive() external payable {}
    fallback() external payable {}

    /**
     * @dev Executes a call on another contract on behalf of the token owner or bonded account.
     * @param _to The contract address to call.
     * @param _value The amount of ether to send.
     * @param _data The call data.
     * @param operation The operation type (only call operations are supported).
     * @return result The result data of the call or its revert message.
     */
    function execute(address _to, uint256 _value, bytes calldata _data, uint operation)
        external
        payable
        onlyAuthorizedMsgSender()
        returns (bytes memory result)
    {
        require(operation == 0, "Only call operations are supported");

        state ++;

        bool success;
        (success, result) = _to.call{value: _value}(_data);

        if (!success) {
            assembly {
                revert(add(result, 32), mload(result))
            }
        }
    }

    /**
     * @dev Returns the token details that this contract is associated with.
     * @return _chainId The chain ID.
     * @return _tokenContract The address of the token contract.
     * @return _tokenId The ID of the token.
     */
    function token()
        external
        view
        returns (
            uint256 _chainId,
            address _tokenContract,
            uint256 _tokenId
        )
    {
        uint256 length = address(this).code.length;
        return
            abi.decode(
                Bytecode.codeAt(address(this), length - 0x60, length),
                (uint256, address, uint256)
            );
    }

    /**
     * @dev Returns the owner of the token associated with this contract from the ERC721 contract.
     * @return The address of the token owner.
     */
    function owner() public view returns (address) {
        (uint256 chainId, address tokenContract, uint256 tokenId) = this.token();
        if (chainId != block.chainid) return address(0);

        return IERC721(tokenContract).ownerOf(tokenId);
    }

    /**
     * @dev Checks if this contract supports a specified interface.
     * @param _interfaceId The ID of the interface to check.
     * @return true if the interface is supported, false otherwise.
     */
    function supportsInterface(bytes4 _interfaceId) public pure returns (bool) {
        return (_interfaceId == type(IERC165).interfaceId ||
            _interfaceId == type(IERC6551Account).interfaceId);
    }

    /**
     * @dev Checks if a specified signer is valid for this contract.
     * @param signer The address of the signer.
     * @return The function selector if the signer is valid, 0 otherwise.
     */
    function isValidSigner(address signer, bytes calldata) external view returns (bytes4) {
        if (_isValidSigner(signer)) {
            return IERC6551Account.isValidSigner.selector;
        }

        return bytes4(0);
    }

    /**
     * @dev Internal function to check if a specified signer is valid.
     * @param signer The address of the signer.
     * @return true if the signer is valid, false otherwise.
     */
    function _isValidSigner(address signer) internal view returns (bool) {
        return signer == owner() || (signer == bondedAddress && tokenOwnerAtLastBond == owner());
    }

    /**
     * @dev Checks if a specified signature is valid for a given hash.
     * @param hash The hash of the data.
     * @param signature The signature to check.
     * @return The function selector if the signature is valid, empty bytes otherwise.
     */
    function isValidSignature(bytes32 hash, bytes memory signature)
        external
        view
        returns (bytes4)
    {
        bool isValid = 
            SignatureChecker.isValidSignatureNow(owner(), hash, signature) ||
            (SignatureChecker.isValidSignatureNow(bondedAddress, hash, signature) && tokenOwnerAtLastBond == owner());

        if (isValid) {
            return IERC1271.isValidSignature.selector;
        }

        return "";
    }

    /**
     * @dev Handles the receipt of a single ERC1155 token.
     * @return The function selector.
     */
    function onERC1155Received(
        address,
        address,
        uint256,
        uint256,
        bytes calldata
    ) external returns (bytes4) {
        return IERC1155Receiver.onERC1155Received.selector;
    }

    /**
     * @dev Handles the receipt of multiple ERC1155 tokens.
     * @return The function selector.
     */
    function onERC1155BatchReceived(
        address,
        address,
        uint256[] calldata,
        uint256[] calldata,
        bytes calldata
    ) external returns (bytes4)
    {
        return IERC1155Receiver.onERC1155BatchReceived.selector;
    }

    /**
     * @dev Handles the receipt of a single ERC721 token.
     * @return The function selector.
     */
    function onERC721Received(
        address,
        address,
        uint256,
        bytes calldata
    ) external returns (bytes4)
    {
        return IERC721Receiver.onERC721Received.selector;
    }
}

File 6 of 28 : Interfaces.sol
pragma solidity ^0.8.18;

import "openzeppelin/token/ERC721/IERC721.sol";

interface IEcliptic is IERC721 {
    function transferPoint(uint32 _point, address _newOwner, bool _reset) external;
    function setTransferProxy(uint32 _point, address _transferProxy) external;
}

interface IAzimuth {
    function getOwner(uint32 _point) external view returns (address);
    function getOwnedPoints(address) external view returns (uint32[] memory);
    function owner() external view returns (address);
    function canTransfer(uint32 _point, address who) external view returns(bool);
}

interface ISoulboundAccessories {
    function miladyAuthority() external view returns(address);
}

File 7 of 28 : ShipStore.sol
pragma solidity ^0.8.18;

import "openzeppelin/token/ERC721/IERC721Receiver.sol";
import "./Interfaces.sol";

/// @title ShipStore contract
/// @notice Allows anyone to create and manage any number of ShipStacks, and for other users to purchase ships from these stacks.
/// @author Logan Brutsche
contract ShipStore {
    IAzimuth public azimuthContract;

    /// @param _azimuthContract The address of the azimuth contract.
    constructor(IAzimuth _azimuthContract) {
        azimuthContract = _azimuthContract;
    }

    ShipStack[] public shipStacks;

    /// @notice Struct to represent a ship stack.
    struct ShipStack {
        address owner; // 0 indicates the stack has not been initialized
        address operator;
        address depositor;
        uint price;
        address exclusiveBuyer; // if not 0x0, this is the only address that can buy ships from this stack
        address payable revenueRecipient;
        bool deployed; // has the operator called deployStack yet?
        uint32[] ships;
    }

    /// @notice Prepare a ShipStack to be deployed later
    /// @param _owner The owner of the stack. Can change operator and revenueRecipient, and transfer ownership
    /// @param _operator The address that will be allowed to deploy and manage the stack.
    /// @param _revenueRecipient The address to which revenue from pill sales will be sent.
    function prepStack(address _owner, address _operator, address _depositor, address payable _revenueRecipient)
        external
        returns (uint stackId)
    {
        require(_operator != address(0), "can't set operator 0x0");

        // implicitly returns
        stackId = shipStacks.length;

        ShipStack memory stack;
        stack.owner = _owner;
        stack.operator = _operator;
        stack.depositor = _depositor;
        stack.revenueRecipient = _revenueRecipient;
        
        shipStacks.push(stack);
    }

    // onlyOwner funcs

    /// @notice Set a new owner for a specific stack.
    /// @param _stackId The ID of the stack.
    /// @param _owner The new owner's address.
    /// @dev The caller must be the current owner.
    function setOwner(uint _stackId, address _owner)
        external
        onlyStackOwner(_stackId)
    {
        _setOwner(_stackId, _owner);
    }
    /// @notice Set a new operator for a specific stack.
    /// @param _stackId The ID of the stack.
    /// @param _operator The new operator's address.
    /// @dev The caller must be the current owner.
    function setOperator(uint _stackId, address _operator)
        external
        onlyStackOwner(_stackId)
    {
        _setOperator(_stackId, _operator);
    }
    /// @notice Sets the revenue recipient for a ship stack at shipStacks[msg.sender][_stackId].
    /// @dev Stack must be initialized.
    /// @param _stackId The identifier of the ship stack.
    /// @param _revenueRecipient The address to receive the revenue.
    function setRevenueRecipient(uint _stackId, address payable _revenueRecipient)
        public
        onlyStackOwner(_stackId)
    {
        _setRevenueRecipient(_stackId, _revenueRecipient);
    }

    // onlyOperator funcs

    /// @notice Sets the depositor for a ship stack.
    /// @dev Stack must be initialized.
    /// @dev Only callable by operator
    /// @param _stackId The identifier of the ship stack.
    /// @param _depositor The address allowed to deposit ships.
    function setDepositor(uint _stackId, address _depositor)
        external
        onlyStackOperator(_stackId)
    {
        _setDepositor(_stackId, _depositor);
    }
    /// @notice Deploy a stack with specified properties.
    /// @param _stackId The ID of the stack.
    /// @param _price The price for the stack.
    /// @param _exclusiveBuyer If set, only this address can buy a ship from this stack.
    /// @dev The caller must be the current operator.
    /// @dev Throws if the stack is already deployed.
    /// @dev Throws if the stack has not been prepped via prepStack.
    function deployStack(uint _stackId, uint _price, address _exclusiveBuyer)
        external
        onlyStackOperator(_stackId)
    {
        ShipStack storage stack = shipStacks[_stackId];
        require(!stack.deployed, "stack already deployed");

        _setPrice(_stackId, _price);
        _setExclusiveBuyer(_stackId, _exclusiveBuyer);
        stack.deployed = true;
    }
    /// @notice Sets the price for a ship stack.
    /// @dev Stack must be initialized.
    /// @dev Only callable by the stack operator.
    /// @param _stackId The identifier of the ship stack.
    /// @param _price The new price for the ship stack.
    function setPrice(uint _stackId, uint _price)
        external
        onlyStackOperator(_stackId)
    {
        _setPrice(_stackId, _price);
    }
    /// @notice Sets exclusive buyer for a ship stack.
    /// @dev Stack must be initialized.
    /// @dev Only callable by the stack operator.
    /// @param _stackId The identifier of the ship stack.
    /// @param _exclusiveBuyer The address of the exclusive buyer.
    function setExclusiveBuyer(uint _stackId, address _exclusiveBuyer)
        public
        onlyStackOperator(_stackId)
    {
        _setExclusiveBuyer(_stackId, _exclusiveBuyer);
    }

    // internal setters

    /// @notice Internal function to set the owner of a stack.
    /// @param _stackId The ID of the stack.
    /// @param _owner The new owner address.
    function _setOwner(uint _stackId, address _owner) internal {
        shipStacks[_stackId].owner = _owner;
    }
    /// @notice Internal function to set the operator of a stack.
    /// @param _stackId The ID of the stack.
    /// @param _operator The new operator address.
    function _setOperator(uint _stackId, address _operator) internal {
        shipStacks[_stackId].operator = _operator;
    }
    /// @notice Internal function to set the depositor of a stack.
    /// @param _stackId The ID of the stack.
    /// @param _depositor The new depositor address.
    function _setDepositor(uint _stackId, address _depositor) internal {
        shipStacks[_stackId].depositor = _depositor;
    }
    /// @notice Internal function to set the revenue recipient of a stack.
    /// @param _stackId The ID of the stack.
    /// @param _revenueRecipient The new revenue recipient address.
    function _setRevenueRecipient(uint _stackId, address payable _revenueRecipient) internal {
        shipStacks[_stackId].revenueRecipient = _revenueRecipient;
    }
    /// @notice Internal function to set the price of a stack.
    /// @param _stackId The ID of the stack.
    /// @param _price The new price.
    function _setPrice(uint _stackId, uint _price) internal {
        shipStacks[_stackId].price = _price;
    }
    /// @notice Internal function to set the exclusive buyer of a stack.
    /// @param _stackId The ID of the stack.
    /// @param _exclusiveBuyer The new value of exclusiveBuyer.
    function _setExclusiveBuyer(uint _stackId, address _exclusiveBuyer) internal {
        shipStacks[_stackId].exclusiveBuyer = _exclusiveBuyer;
    }

    /// @notice Modifier to ensure only the stack owner can call the function.
    /// @param _stackId The ID of the stack.
    modifier onlyStackOwner(uint _stackId) {
        requireValidStackId(_stackId);
        require(msg.sender == shipStacks[_stackId].owner, "msg.sender != owner");
        _;
    }

    /// @notice Modifier to ensure only the stack operator can call the function.
    /// @param _stackId The ID of the stack.
    modifier onlyStackOperator(uint _stackId) {
        requireValidStackId(_stackId);
        require(msg.sender == shipStacks[_stackId].operator, "msg.sender != operator");
        _;
    }

    /// @notice Modifier to ensure only the stack depositor can call the function.
    /// @param _stackId The ID of the stack.
    modifier onlyStackDepositor(uint _stackId) {
        requireValidStackId(_stackId);
        require(msg.sender == shipStacks[_stackId].depositor, "msg.sender != depositor");
        _;
    }

    /// @notice Internal function to validate the given stack ID.
    /// @param _stackId The ID of the stack to validate.
    /// @dev Throws if the stack ID is out of range.
    function requireValidStackId(uint _stackId)
        internal
        view
    {
        require(_stackId < shipStacks.length, "Invalid _stackId");
    }
    
    /// @notice Retrieve information about a specific ship stack.
    /// @param _stackId The ID of the ship stack.
    /// @dev Stack must be initialized.
    /// @return owner The owner of the ship stack.
    /// @return operator The operator authorized for the ship stack.
    /// @return price The price to buy a ship.
    /// @return onlyBuyerIfSet If not the zero address, only this address can buy from this stack.
    /// @return revenueRecipient Address where revenue is forwarded.
    /// @return numShips The number of ships in this stack.
    function getStackInfo(uint _stackId)
        external
        view
        returns(
            address owner,
            address operator,
            uint price,
            address onlyBuyerIfSet,
            address payable revenueRecipient,
            uint numShips
        )
    {
        requireValidStackId(_stackId);

        return(
            shipStacks[_stackId].owner,
            shipStacks[_stackId].operator,
            shipStacks[_stackId].price,
            shipStacks[_stackId].exclusiveBuyer,
            shipStacks[_stackId].revenueRecipient,
            shipStacks[_stackId].ships.length
        );
    }

    /// @notice Deposits ships into a ship stack.
    /// @dev This contract must be approved to send each id (i.e. by calling setApprovalForAll)
    /// @dev msg.sender must be approved to send each id as defined by azimuthContract.canTransfer
    /// @param _stackId The identifier of the ship stack.
    /// @param _ids The array of ship IDs to deposit.
    function depositShips(uint _stackId, uint32[] calldata _ids)
        external
        onlyStackDepositor(_stackId)
    {
        ShipStack storage shipStack = shipStacks[_stackId];

        IEcliptic ecliptic = IEcliptic(azimuthContract.owner());
        for (uint i; i<_ids.length;) {
            require(azimuthContract.canTransfer(_ids[i], msg.sender), "msg.sender can't transfer point");

            ecliptic.transferPoint(_ids[i], address(this), false);
            shipStack.ships.push(_ids[i]);

            unchecked { i ++ ;}
        }
    }

    /// @notice Recalls ships from a ship stack.
    /// @dev Stack must be initialized.
    /// @param _stackId The identifier of the ship stack.
    /// @param _amount The number of ships to recall.
    /// @param _recipient The recipient of the recalled ships.
    /// @param breach Whether to breach the ship.
    function recallShips(uint _stackId, uint _amount, address _recipient, bool breach)
        external
        onlyStackDepositor(_stackId)
    {
        ShipStack storage shipStack = shipStacks[_stackId];

        require(_amount <= shipStack.ships.length, "Not that many ships in that stack");

        IEcliptic ecliptic = IEcliptic(azimuthContract.owner());
        for (uint i = 0; i < _amount;) {
            ecliptic.transferPoint(shipStack.ships[shipStack.ships.length - 1], _recipient, breach);
            shipStack.ships.pop();

            unchecked { i ++ ;}
        }
    }

    /// @notice Event emitted when a ship is bought.
    /// @param stackId The ID of the ship stack.
    /// @param recipient The address receiving the ship.
    event ShipBought(uint indexed stackId, address indexed recipient);

    /// @notice Allows a user to buy a ship from a specific stack.
    /// @dev Checks for sufficient Ether and permissions before executing the purchase.
    /// @param _stackId The ID of the ship stack.
    /// @param _recipient The address to which the bought ship will be sent.
    /// @return shipId The ID of the bought ship.
    function buyShip(uint _stackId, address _recipient)
        external
        payable
        returns(uint32 shipId)
    {
        requireValidStackId(_stackId);
        require(shipStacks[_stackId].deployed, "Ship stack not deployed");

        ShipStack storage shipStack = shipStacks[_stackId];

        require(msg.value == shipStack.price, "Incorrect ether amount included");
        require(shipStack.ships.length > 0, "Stack has no ships");
        
        if (shipStack.exclusiveBuyer != address(0)) {
            require(msg.sender == shipStack.exclusiveBuyer, "buyer not approved");
        }

        shipId = shipStack.ships[shipStack.ships.length - 1];

        IEcliptic ecliptic = IEcliptic(azimuthContract.owner());
        ecliptic.transferPoint(shipId, _recipient, false);
        
        shipStack.ships.pop();

        (bool success,) = shipStack.revenueRecipient.call{value: msg.value}("");
        require(success, "failed to forward revenue");

        emit ShipBought(_stackId, _recipient);
    }
}

File 8 of 28 : PillStore.sol
pragma solidity ^0.8.18;

import "openzeppelin/token/ERC721/ERC721.sol";

/// @title PillStore contract
/// @notice Allows anyone to define a "Pill" NFT set, with various permissions and settings, and allows other users to mint Pills from these sets.
/// @author Logan Brutsche
contract PillStore {
    PillSet[] public pillSets;

    /// @notice struct to represent PillSets
    struct PillSet {
        address owner; // 0 indicates the set has not been initialized
        address operator; // 0 indicates the contract has not been deployed
        uint price; // 0 indicates free
        uint supplyLimit; // 0 indicates no limit
        address exclusiveBuyer; // 0x0 indicates anyone can buy
        address payable revenueRecipient;
        Pill nftContract;
    }

    /// @notice Prepare a PillSet to be deployed later
    /// @param _owner The owner of the set. Can change operator and revenueRecipient, and transfer ownership
    /// @param _operator The address that will be allowed to deploy and manage the pill set.
    /// @param _revenueRecipient The address to which revenue from pill sales will be sent.
    function prepPillSet(address _owner, address _operator, address payable _revenueRecipient)
        external
        returns (uint pillSetId)
    {
        require(_operator != address(0), "can't set operator 0x0");

        // implicitly returns
        pillSetId = pillSets.length;

        PillSet memory pillSet;
        pillSet.owner = _owner;
        pillSet.operator = _operator;
        pillSet.revenueRecipient = _revenueRecipient;

        pillSets.push(pillSet);
    }

    // onlyOwner funcs

    /// @notice Sets the new owner of the stack.
    /// @dev Only callable by the stack owner.
    /// @param _pillSetId id of the set.
    /// @param _owner new owner of the set.
    function setOwner(uint _pillSetId, address _owner)
        external
        onlyPillSetOwner(_pillSetId)
    {
        _setOwner(_pillSetId, _owner);
    }
    /// @notice Sets the operator buyer of a PillSet.
    /// @dev Only callable by the stack owner.
    /// @param _pillSetId id of the set.
    /// @param _operator new operator of the set.
    function setOperator(uint _pillSetId, address _operator)
        external
        onlyPillSetOwner(_pillSetId)
    {
        _setOperator(_pillSetId, _operator);
    }
    /// @notice Sets the exclusive buyer of a PillSet.
    /// @dev Only callable by the stack owner.
    /// @param _pillSetId id of the set.
    /// @param _revenueRecipient new revenue recipient of the set.
    function setRevenueRecipient(uint _pillSetId, address payable _revenueRecipient)
        external
        onlyPillSetOwner(_pillSetId)
    {
        _setRevenueRecipient(_pillSetId, _revenueRecipient);
    }

    // onlyOperator funcs

    /// @notice Initialize a PillSet with full configuration, and deploy a Pill NFT contract.
    /// @param _pillSetId id of the set.
    /// @param _price Price to mint an token from this set. Zero indicates free.
    /// @param _supplyLimit Maximum number of tokens that can be minted. Zero indicates unlimited.
    /// @param _exclusiveBuyerIfSet If not the zero address, only this buyer can mint a token from this set.
    /// @param _appName The name of the Urbit app this NFT represents.
    /// @param _metadataUrl the URL for the NFT metadata.
    /// @return Returns the deployed Pill NFT contract.
    function deployPillSet
    (
        uint _pillSetId,
        uint _price,
        uint _supplyLimit,
        address _exclusiveBuyerIfSet,
        string calldata _appName,
        string calldata _metadataUrl
    )
        external
        onlyPillSetOperator(_pillSetId)
        returns (Pill)
    {
        PillSet storage pillSet = pillSets[_pillSetId];

        // implicitly checks that pillSet.operator != 0x0
        require(address(pillSet.nftContract) == address(0), "pillSet already deployed");

        _setPrice(_pillSetId, _price);
        _setSupplyLimit(_pillSetId, _supplyLimit);
        _setExclusiveBuyer(_pillSetId, _exclusiveBuyerIfSet);
        return _deployContract(_pillSetId, _appName, _metadataUrl);
    }

    /// @notice Sets the price of a PillSet.
    /// @dev Only callable by the set operator.
    /// @param _pillSetId id of the set.
    /// @param _price new price of the set.
    function setPrice(uint _pillSetId, uint _price)
        external
        onlyPillSetOperator(_pillSetId)
    {
        _setPrice(_pillSetId, _price);
    }
    /// @notice Sets the supply limit of a PillSet.
    /// @dev Only callable by the stack operator.
    /// @dev Will revert if the new supplyLimit is less than the current supply of the token.
    /// @param _pillSetId id of the set.
    /// @param _supplyLimit new supply limit of the set.
    function setSupplyLimit(uint _pillSetId, uint _supplyLimit)
        external
        onlyPillSetOperator(_pillSetId)
    {
        _setSupplyLimit(_pillSetId, _supplyLimit);
    }
    /// @notice Sets the exclusive buyer of a PillSet.
    /// @dev Only callable by the stack operator.
    /// @param _pillSetId id of the set.
    /// @param _exclusiveBuyerIfSet new exclusive buyer of the set.
    /// @dev If set to the zero address, anyone can buy pills from the set.
    function setExclusiveBuyer(uint _pillSetId, address _exclusiveBuyerIfSet)
        external
        onlyPillSetOperator(_pillSetId)
    {
        _setExclusiveBuyer(_pillSetId, _exclusiveBuyerIfSet);
    }

    // internal setters

    /// @notice Internal function to set the owner of a specific pill set.
    /// @param _pillSetId The ID of the pill set.
    /// @param _owner The new owner's address.
    /// @dev This is an internal function and can't be called externally.
    function _setOwner(uint _pillSetId, address _owner) internal {
        pillSets[_pillSetId].owner = _owner;
    }
    /// @notice Internal function to set the operator of a specific pill set.
    /// @param _pillSetId The ID of the pill set.
    /// @param _operator The new operator's address.
    /// @dev This is an internal function and can't be called externally.
    function _setOperator(uint _pillSetId, address _operator) internal {
        pillSets[_pillSetId].operator = _operator;
    }
    /// @notice Internal function to set the revenue recipient of a specific pill set.
    /// @param _pillSetId The ID of the pill set.
    /// @param _revenueRecipient The new revenue recipient's address.
    /// @dev This is an internal function and can't be called externally.
    function _setRevenueRecipient(uint _pillSetId, address payable _revenueRecipient) internal {
        pillSets[_pillSetId].revenueRecipient = _revenueRecipient;
    }
    /// @notice Internal function to set the price of a specific pill set.
    /// @param _pillSetId The ID of the pill set.
    /// @param _price The new price.
    /// @dev This is an internal function and can't be called externally.
    function _setPrice(uint _pillSetId, uint _price) internal {
        pillSets[_pillSetId].price = _price;
    }
    /// @notice Internal function to set the supply limit of a specific pill set.
    /// @param _pillSetId The ID of the pill set.
    /// @param _supplyLimit The new supply limit.
    /// @dev Cannot set the supplyLimit to lower than the current supply of the token
    /// @dev 0 is considered infinite supply, and thus _supplyLimit = 0 is always a valid argument.
    /// @dev This is an internal function and can't be called externally. Checks that the new supply limit is valid.
    function _setSupplyLimit(uint _pillSetId, uint _supplyLimit) internal {
        if (address(pillSets[_pillSetId].nftContract) != address(0)) {
            require(_supplyLimit == 0 || _supplyLimit >= pillSets[_pillSetId].nftContract.currentSupply(), "new supply limit < current supply");
        }
        pillSets[_pillSetId].supplyLimit = _supplyLimit;
    }
    /// @notice Internal function to set the exclusive buyer of a specific pill set.
    /// @param _pillSetId The ID of the pill set.
    /// @param _exclusiveBuyer The new value of the stack's exclusiveBuyer.
    /// @dev This is an internal function and can't be called externally.
    function _setExclusiveBuyer(uint _pillSetId, address _exclusiveBuyer) internal {
        pillSets[_pillSetId].exclusiveBuyer = _exclusiveBuyer;
    }

    /// @notice Internal function to deploy a new Pill contract for a specific pill set.
    /// @param _pillSetId The ID of the pill set.
    /// @param _appName The name of the application.
    /// @param _metadataUrl The URL for the metadata.
    /// @return The newly deployed Pill contract.
    /// @dev Throws if the contract is already deployed for the given pill set.
    function _deployContract(uint _pillSetId, string calldata _appName, string calldata _metadataUrl)
        internal
        returns (Pill)
    {
        require(address(pillSets[_pillSetId].nftContract) == address(0), "contract already deployed");

        return pillSets[_pillSetId].nftContract = new Pill(_appName, _metadataUrl);
    }

    /// @notice Modifier to check if the caller is the owner of a specific pill set.
    /// @param _pillSetId The ID of the pill set.
    /// @dev Throws if the caller is not the owner.
    /// @dev Also throws if the _pillSetId is invalid
    modifier onlyPillSetOwner(uint _pillSetId) {
        require(_pillSetId < pillSets.length, "Invalid _pillSetId");
        require(msg.sender == pillSets[_pillSetId].owner, "msg.sender != owner");
        _;
    }

    /// @notice Modifier to check if the caller is the operator of a specific pill set.
    /// @param _pillSetId The ID of the pill set.
    /// @dev Throws if the caller is not the operator.
    /// @dev Also throws if the _pillSetId is invalid
    modifier onlyPillSetOperator(uint _pillSetId) {
        require(_pillSetId < pillSets.length, "Invalid _pillSetId");
        require(msg.sender == pillSets[_pillSetId].operator, "msg.sender != operator");
        _;
    }
    
    /// @notice Emitted when a Pill NFT is sold.
    /// @param pillSetId The ID of the pill set.
    /// @param nftId The ID of the minted NFT.
    event PillSold(uint indexed pillSetId, uint nftId);

    /// @dev Mint a new NFT pill.
    /// @param _pillSetId The ID of the pill set.
    /// @param _recipient The address to receive the minted NFT.
    /// @dev The pill set must be deployed.
    /// @dev Sent value must be greater or equal to the pill price.
    /// @dev Supply limit must not be reached.
    /// @dev Sender must be the exclusive buyer if set.
    /// @return nftId The ID of the minted NFT.
    function mintPill(uint _pillSetId, address _recipient)
        external
        payable
        returns (uint nftId)
    {
        PillSet storage pillSet = pillSets[_pillSetId];

        require(address(pillSet.nftContract) != address(0),                                            "pill contract not deployed");
        require(msg.value >= pillSet.price,                                                            "not enough ether included");
        require(pillSet.supplyLimit == 0 || pillSet.nftContract.currentSupply() < pillSet.supplyLimit, "pill sold out");
        require(pillSet.exclusiveBuyer == address(0) || msg.sender == pillSet.exclusiveBuyer,          "msg.sender not exclusiveBuyer");

        // implicly returns
        nftId = pillSet.nftContract.mint(_recipient);

        (bool success, ) = pillSet.revenueRecipient.call{value: msg.value}("");
        require(success, "revenue forward failed");

        emit PillSold(_pillSetId, nftId);
    }

    /// @notice Retrieve information about a specific pill set.
    /// @param _pillSetId The ID of the pill set.
    /// @return owner The owner of the pill set.
    /// @return operator The operator for the pill set.
    /// @return price The price to mint a pill from this set.
    /// @return supplyLimit The maximum supply for this pill set.
    /// @return exclusiveBuyer If set, only this address can buy from this set.
    /// @return revenueRecipient Address where revenue is forwarded.
    /// @return nftContract The contract of the pill NFT.
    /// @return name The name of the pill set.
    /// @return currentSupply The current supply of pills from this set.
    function getPillSetInfo(uint _pillSetId)
        external
        view
        returns(
            address owner,
            address operator,
            uint price,
            uint supplyLimit,
            address exclusiveBuyer,
            address payable revenueRecipient,
            Pill nftContract,
            string memory name,
            uint currentSupply
        )
    {
        PillSet storage pillSet = pillSets[_pillSetId];

        (nftContract, name, currentSupply) =
            address(pillSet.nftContract) == address(0) ?
                (Pill(address(0)), "", 0)
              : (pillSet.nftContract, pillSet.nftContract.name(), pillSet.nftContract.currentSupply());

        return (
            pillSet.owner,
            pillSet.operator,
            pillSet.price,
            pillSet.supplyLimit,
            pillSet.exclusiveBuyer,
            pillSet.revenueRecipient,
            nftContract,
            name,
            currentSupply
        );
    }

    /// @notice Retrieve the contract for a specific pill set.
    /// @param _pillSetId The ID of the pill set to query.
    /// @return The Pill contract associated with the specified pill set.
    function getPillSetContract(uint _pillSetId)
        external
        view
        returns (Pill)
    {
        return pillSets[_pillSetId].nftContract;
    }
}

/// @title Pill - NFT Contract for Pill Minting and Management
/// @notice This contract allows for the minting and basic management of Pill NFTs. It extends ERC721 for NFT functionality.
/// @dev The contract is designed to work specifically with a PillFactory for minting operations. It assumes that the PillFactory is the sole legitimate minter.
/// @author Logan Brutsche
contract Pill is ERC721 {
    string public appName;
    string baseURI;
    address public immutable storeAddress;

    uint public currentSupply;

    /// @notice Create a new Pill NFT contract.
    /// @param _appName The name of the application.
    /// @param __baseURI The base URI for metadata.
    /// @dev We assume that only the PillFactory constructs these; our system ignores any other instances.
    constructor(string memory _appName, string memory __baseURI)
        ERC721(string.concat("Pill: ", _appName), "PILL")
    {
        appName = _appName;
        baseURI = __baseURI;

        storeAddress = msg.sender;
    }
    
    /// @notice Mint a new Pill NFT.
    /// @param who The address to receive the minted NFT.
    /// @return nftId The ID of the minted NFT.
    /// @dev Requires sender to be the store address. Relies on PillFactory for payment and validation.
    function mint(address who)
        public
        returns (uint nftId)
    {
        require(msg.sender == storeAddress, "msg.sender != PillStore"); // we rely on PillFactory to ensure and collect payment

        // implicitly returns
        nftId = currentSupply;
        _mint(who, nftId);

        currentSupply ++;
    }

    /// @notice Retrieve the base URI for metadata.
    /// @return baseURI The base URI string.
    /// @dev This function overrides the `_baseURI` function from ERC721.
    function _baseURI()
        internal
        view
        override
        returns (string memory)
    {
        return baseURI;
    }
}

File 9 of 28 : AzimuthOwnerWrapper.sol
pragma solidity ^0.8.18;

import "./Interfaces.sol";

/// @notice A wrapper contract for interfacing with the Azimuth point system.
/// @dev Provides a stable `ownerOf` method that does not depend on the potentially self-destructing Ecliptic contract.
contract AzimuthOwnerWrapper {
    IAzimuth public azimuthContract;
    
    /// @notice Initialize the contract with a given Azimuth contract.
    /// @param _azimuthContract The address of the Azimuth contract.
    constructor(IAzimuth _azimuthContract) {
        azimuthContract = _azimuthContract;
    }

    /// @notice Returns the Ecliptic contract associated with the current Azimuth contract.
    /// @return The Ecliptic contract address.
    function eclipticContract()
        public
        view
        returns(IEcliptic)
    {
        return IEcliptic(azimuthContract.owner());
    }

    /// @notice Retrieve the owner of a given Azimuth point.
    /// @param tokenId The ID of the Azimuth point.
    /// @return The address of the token owner.
    function ownerOf(uint256 tokenId) external view returns (address) {
        return eclipticContract().ownerOf(tokenId);
    }
}

File 10 of 28 : IERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol)

pragma solidity ^0.8.20;

/**
 * @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 11 of 28 : Context.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Context.sol)

pragma solidity ^0.8.20;

/**
 * @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 12 of 28 : Create2.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Create2.sol)

pragma solidity ^0.8.20;

/**
 * @dev Helper to make usage of the `CREATE2` EVM opcode easier and safer.
 * `CREATE2` can be used to compute in advance the address where a smart
 * contract will be deployed, which allows for interesting new mechanisms known
 * as 'counterfactual interactions'.
 *
 * See the https://eips.ethereum.org/EIPS/eip-1014#motivation[EIP] for more
 * information.
 */
library Create2 {
    /**
     * @dev Not enough balance for performing a CREATE2 deploy.
     */
    error Create2InsufficientBalance(uint256 balance, uint256 needed);

    /**
     * @dev There's no code to deploy.
     */
    error Create2EmptyBytecode();

    /**
     * @dev The deployment failed.
     */
    error Create2FailedDeployment();

    /**
     * @dev Deploys a contract using `CREATE2`. The address where the contract
     * will be deployed can be known in advance via {computeAddress}.
     *
     * The bytecode for a contract can be obtained from Solidity with
     * `type(contractName).creationCode`.
     *
     * Requirements:
     *
     * - `bytecode` must not be empty.
     * - `salt` must have not been used for `bytecode` already.
     * - the factory must have a balance of at least `amount`.
     * - if `amount` is non-zero, `bytecode` must have a `payable` constructor.
     */
    function deploy(uint256 amount, bytes32 salt, bytes memory bytecode) internal returns (address addr) {
        if (address(this).balance < amount) {
            revert Create2InsufficientBalance(address(this).balance, amount);
        }
        if (bytecode.length == 0) {
            revert Create2EmptyBytecode();
        }
        /// @solidity memory-safe-assembly
        assembly {
            addr := create2(amount, add(bytecode, 0x20), mload(bytecode), salt)
        }
        if (addr == address(0)) {
            revert Create2FailedDeployment();
        }
    }

    /**
     * @dev Returns the address where a contract will be stored if deployed via {deploy}. Any change in the
     * `bytecodeHash` or `salt` will result in a new destination address.
     */
    function computeAddress(bytes32 salt, bytes32 bytecodeHash) internal view returns (address) {
        return computeAddress(salt, bytecodeHash, address(this));
    }

    /**
     * @dev Returns the address where a contract will be stored if deployed via {deploy} from a contract located at
     * `deployer`. If `deployer` is this contract's address, returns the same value as {computeAddress}.
     */
    function computeAddress(bytes32 salt, bytes32 bytecodeHash, address deployer) internal pure returns (address addr) {
        /// @solidity memory-safe-assembly
        assembly {
            let ptr := mload(0x40) // Get free memory pointer

            // |                   | ↓ ptr ...  ↓ ptr + 0x0B (start) ...  ↓ ptr + 0x20 ...  ↓ ptr + 0x40 ...   |
            // |-------------------|---------------------------------------------------------------------------|
            // | bytecodeHash      |                                                        CCCCCCCCCCCCC...CC |
            // | salt              |                                      BBBBBBBBBBBBB...BB                   |
            // | deployer          | 000000...0000AAAAAAAAAAAAAAAAAAA...AA                                     |
            // | 0xFF              |            FF                                                             |
            // |-------------------|---------------------------------------------------------------------------|
            // | memory            | 000000...00FFAAAAAAAAAAAAAAAAAAA...AABBBBBBBBBBBBB...BBCCCCCCCCCCCCC...CC |
            // | keccak(start, 85) |            ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ |

            mstore(add(ptr, 0x40), bytecodeHash)
            mstore(add(ptr, 0x20), salt)
            mstore(ptr, deployer) // Right-aligned with 12 preceding garbage bytes
            let start := add(ptr, 0x0b) // The hashed data starts at the final garbage byte which we will set to 0xff
            mstore8(start, 0xff)
            addr := keccak256(start, 85)
        }
    }
}

File 13 of 28 : IERC6551Registry.sol
pragma solidity ^0.8.18;

interface IERC6551Registry {
    /**
     * @dev The registry SHALL emit the AccountCreated event upon successful account creation
     */
    event AccountCreated(
        address account,
        address indexed implementation,
        uint256 chainId,
        address indexed tokenContract,
        uint256 indexed tokenId,
        uint256 salt
    );

    /**
     * @dev Creates a token bound account for a non-fungible token.
     *
     * If account has already been created, returns the account address without calling create2.
     *
     * If initData is not empty and account has not yet been created, calls account with
     * provided initData after creation.
     *
     * Emits AccountCreated event.
     *
     * @return the address of the account
     */
    function createAccount(
        address implementation,
        uint256 chainId,
        address tokenContract,
        uint256 tokenId,
        uint256 salt,
        bytes calldata initData
    ) external returns (address);

    /**
     * @dev Returns the computed token bound account address for a non-fungible token
     *
     * @return The computed address of the token bound account
     */
    function account(
        address implementation,
        uint256 chainId,
        address tokenContract,
        uint256 tokenId,
        uint256 salt
    ) external view returns (address);
}

File 14 of 28 : IERC1271.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC1271.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC1271 standard signature validation method for
 * contracts as defined in https://eips.ethereum.org/EIPS/eip-1271[ERC-1271].
 */
interface IERC1271 {
    /**
     * @dev Should return whether the signature provided is valid for the provided data
     * @param hash      Hash of the data to be signed
     * @param signature Signature byte array associated with _data
     */
    function isValidSignature(bytes32 hash, bytes memory signature) external view returns (bytes4 magicValue);
}

File 15 of 28 : IERC1155Receiver.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC1155/IERC1155Receiver.sol)

pragma solidity ^0.8.20;

import {IERC165} from "../../utils/introspection/IERC165.sol";

/**
 * @dev Interface that must be implemented by smart contracts in order to receive
 * ERC-1155 token transfers.
 */
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 16 of 28 : IERC721Receiver.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/IERC721Receiver.sol)

pragma solidity ^0.8.20;

/**
 * @title ERC721 token receiver interface
 * @dev Interface for any contract that wants to support safeTransfers
 * from ERC721 asset contracts.
 */
interface IERC721Receiver {
    /**
     * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
     * by `operator` from `from`, this function is called.
     *
     * It must return its Solidity selector to confirm the token transfer.
     * If any other value is returned or the interface is not implemented by the recipient, the transfer will be
     * reverted.
     *
     * The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.
     */
    function onERC721Received(
        address operator,
        address from,
        uint256 tokenId,
        bytes calldata data
    ) external returns (bytes4);
}

File 17 of 28 : SignatureChecker.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/SignatureChecker.sol)

pragma solidity ^0.8.20;

import {ECDSA} from "./ECDSA.sol";
import {IERC1271} from "../../interfaces/IERC1271.sol";

/**
 * @dev Signature verification helper that can be used instead of `ECDSA.recover` to seamlessly support both ECDSA
 * signatures from externally owned accounts (EOAs) as well as ERC1271 signatures from smart contract wallets like
 * Argent and Safe Wallet (previously Gnosis Safe).
 */
library SignatureChecker {
    /**
     * @dev Checks if a signature is valid for a given signer and data hash. If the signer is a smart contract, the
     * signature is validated against that smart contract using ERC1271, otherwise it's validated using `ECDSA.recover`.
     *
     * NOTE: Unlike ECDSA signatures, contract signatures are revocable, and the outcome of this function can thus
     * change through time. It could return true at block N and false at block N+1 (or the opposite).
     */
    function isValidSignatureNow(address signer, bytes32 hash, bytes memory signature) internal view returns (bool) {
        (address recovered, ECDSA.RecoverError error, ) = ECDSA.tryRecover(hash, signature);
        return
            (error == ECDSA.RecoverError.NoError && recovered == signer) ||
            isValidERC1271SignatureNow(signer, hash, signature);
    }

    /**
     * @dev Checks if a signature is valid for a given signer and data hash. The signature is validated
     * against the signer smart contract using ERC1271.
     *
     * NOTE: Unlike ECDSA signatures, contract signatures are revocable, and the outcome of this function can thus
     * change through time. It could return true at block N and false at block N+1 (or the opposite).
     */
    function isValidERC1271SignatureNow(
        address signer,
        bytes32 hash,
        bytes memory signature
    ) internal view returns (bool) {
        (bool success, bytes memory result) = signer.staticcall(
            abi.encodeCall(IERC1271.isValidSignature, (hash, signature))
        );
        return (success &&
            result.length >= 32 &&
            abi.decode(result, (bytes32)) == bytes32(IERC1271.isValidSignature.selector));
    }
}

File 18 of 28 : Bytecode.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;


library Bytecode {
  error InvalidCodeAtRange(uint256 _size, uint256 _start, uint256 _end);

  /**
    @notice Generate a creation code that results on a contract with `_code` as bytecode
    @param _code The returning value of the resulting `creationCode`
    @return creationCode (constructor) for new contract
  */
  function creationCodeFor(bytes memory _code) internal pure returns (bytes memory) {
    /*
      0x00    0x63         0x63XXXXXX  PUSH4 _code.length  size
      0x01    0x80         0x80        DUP1                size size
      0x02    0x60         0x600e      PUSH1 14            14 size size
      0x03    0x60         0x6000      PUSH1 00            0 14 size size
      0x04    0x39         0x39        CODECOPY            size
      0x05    0x60         0x6000      PUSH1 00            0 size
      0x06    0xf3         0xf3        RETURN
      <CODE>
    */

    return abi.encodePacked(
      hex"63",
      uint32(_code.length),
      hex"80_60_0E_60_00_39_60_00_F3",
      _code
    );
  }

  /**
    @notice Returns the size of the code on a given address
    @param _addr Address that may or may not contain code
    @return size of the code on the given `_addr`
  */
  function codeSize(address _addr) internal view returns (uint256 size) {
    assembly { size := extcodesize(_addr) }
  }

  /**
    @notice Returns the code of a given address
    @dev It will fail if `_end < _start`
    @param _addr Address that may or may not contain code
    @param _start number of bytes of code to skip on read
    @param _end index before which to end extraction
    @return oCode read from `_addr` deployed bytecode

    Forked from: https://gist.github.com/KardanovIR/fe98661df9338c842b4a30306d507fbd
  */
  function codeAt(address _addr, uint256 _start, uint256 _end) internal view returns (bytes memory oCode) {
    uint256 csize = codeSize(_addr);
    if (csize == 0) return bytes("");

    if (_start > csize) return bytes("");
    if (_end < _start) revert InvalidCodeAtRange(csize, _start, _end); 

    unchecked {
      uint256 reqSize = _end - _start;
      uint256 maxSize = csize - _start;

      uint256 size = maxSize < reqSize ? maxSize : reqSize;

      assembly {
        // allocate output byte array - this could also be done without assembly
        // by using o_code = new bytes(size)
        oCode := mload(0x40)
        // new "memory end" including padding
        mstore(0x40, add(oCode, and(add(add(size, 0x20), 0x1f), not(0x1f))))
        // store length in memory
        mstore(oCode, size)
        // actually retrieve the code, this needs assembly
        extcodecopy(_addr, add(oCode, 0x20), _start, size)
      }
    }
  }
}

File 19 of 28 : IERC6551Account.sol
pragma solidity ^0.8.18;

/// @dev the ERC-165 identifier for this interface is `0x6faff5f1`
interface IERC6551Account {
    /**
     * @dev Allows the account to receive Ether
     *
     * Accounts MUST implement a `receive` function.
     *
     * Accounts MAY perform arbitrary logic to restrict conditions
     * under which Ether can be received.
     */
    receive() external payable;

    /**
     * @dev Returns the identifier of the non-fungible token which owns the account
     *
     * The return value of this function MUST be constant - it MUST NOT change
     * over time
     *
     * @return chainId       The EIP-155 ID of the chain the token exists on
     * @return tokenContract The contract address of the token
     * @return tokenId       The ID of the token
     */
    function token()
        external
        view
        returns (
            uint256 chainId,
            address tokenContract,
            uint256 tokenId
        );

    /**
     * @dev Returns a value that SHOULD be modified each time the account changes state
     *
     * @return The current account state
     */
    function state() external view returns (uint256);

    /**
     * @dev Returns a magic value indicating whether a given signer is authorized to act on behalf of the account
     *
     * MUST return the bytes4 magic value 0x523e3260 if the given signer is valid
     *
     * By default, the holder of the non-fungible token the account is bound to MUST be considered a valid
     * signer
     *
     * Accounts MAY implement additional authorization logic which invalidates the holder as a
     * signer or grants signing permissions to other non-holder accounts
     *
     * @param  signer     The address to check signing authorization for
     * @param  context    Additional data used to determine whether the signer is valid
     * @return magicValue Magic value indicating whether the signer is valid
     */
    function isValidSigner(address signer, bytes calldata context)
        external
        view
        returns (bytes4 magicValue);
}

File 20 of 28 : IERC6551Executable.sol
pragma solidity ^0.8.18;

/// @dev the ERC-165 identifier for this interface is `0x74420f4c`
interface IERC6551Executable {
    /**
     * @dev Executes a low-level operation if the caller is a valid signer on the account
     *
     * Reverts and bubbles up error if operation fails
     *
     * @param to        The target address of the operation
     * @param value     The Ether value to be sent to the target
     * @param data      The encoded operation calldata
     * @param operation A value indicating the type of operation to perform
     *
     * Accounts implementing this interface MUST accept the following operation parameter values:
     * - 0 = CALL
     * - 1 = DELEGATECALL
     * - 2 = CREATE
     * - 3 = CREATE2
     *
     * Accounts implementing this interface MAY support additional operations or restrict a signer's
     * ability to execute certain operations
     *
     * @return The result of the operation
     */
    function execute(
        address to,
        uint256 value,
        bytes calldata data,
        uint256 operation
    ) external payable returns (bytes memory);
}

File 21 of 28 : ERC721.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/ERC721.sol)

pragma solidity ^0.8.20;

import {IERC721} from "./IERC721.sol";
import {IERC721Receiver} from "./IERC721Receiver.sol";
import {IERC721Metadata} from "./extensions/IERC721Metadata.sol";
import {Context} from "../../utils/Context.sol";
import {Strings} from "../../utils/Strings.sol";
import {IERC165, ERC165} from "../../utils/introspection/ERC165.sol";
import {IERC721Errors} from "../../interfaces/draft-IERC6093.sol";

/**
 * @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including
 * the Metadata extension, but not including the Enumerable extension, which is available separately as
 * {ERC721Enumerable}.
 */
abstract contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Errors {
    using Strings for uint256;

    // Token name
    string private _name;

    // Token symbol
    string private _symbol;

    mapping(uint256 tokenId => address) private _owners;

    mapping(address owner => uint256) private _balances;

    mapping(uint256 tokenId => address) private _tokenApprovals;

    mapping(address owner => mapping(address operator => bool)) private _operatorApprovals;

    /**
     * @dev Initializes the contract by setting a `name` and a `symbol` to the token collection.
     */
    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
    }

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

    /**
     * @dev See {IERC721-balanceOf}.
     */
    function balanceOf(address owner) public view virtual returns (uint256) {
        if (owner == address(0)) {
            revert ERC721InvalidOwner(address(0));
        }
        return _balances[owner];
    }

    /**
     * @dev See {IERC721-ownerOf}.
     */
    function ownerOf(uint256 tokenId) public view virtual returns (address) {
        return _requireOwned(tokenId);
    }

    /**
     * @dev See {IERC721Metadata-name}.
     */
    function name() public view virtual returns (string memory) {
        return _name;
    }

    /**
     * @dev See {IERC721Metadata-symbol}.
     */
    function symbol() public view virtual returns (string memory) {
        return _symbol;
    }

    /**
     * @dev See {IERC721Metadata-tokenURI}.
     */
    function tokenURI(uint256 tokenId) public view virtual returns (string memory) {
        _requireOwned(tokenId);

        string memory baseURI = _baseURI();
        return bytes(baseURI).length > 0 ? string.concat(baseURI, tokenId.toString()) : "";
    }

    /**
     * @dev Base URI for computing {tokenURI}. If set, the resulting URI for each
     * token will be the concatenation of the `baseURI` and the `tokenId`. Empty
     * by default, can be overridden in child contracts.
     */
    function _baseURI() internal view virtual returns (string memory) {
        return "";
    }

    /**
     * @dev See {IERC721-approve}.
     */
    function approve(address to, uint256 tokenId) public virtual {
        _approve(to, tokenId, _msgSender());
    }

    /**
     * @dev See {IERC721-getApproved}.
     */
    function getApproved(uint256 tokenId) public view virtual returns (address) {
        _requireOwned(tokenId);

        return _getApproved(tokenId);
    }

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

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

    /**
     * @dev See {IERC721-transferFrom}.
     */
    function transferFrom(address from, address to, uint256 tokenId) public virtual {
        if (to == address(0)) {
            revert ERC721InvalidReceiver(address(0));
        }
        // Setting an "auth" arguments enables the `_isAuthorized` check which verifies that the token exists
        // (from != 0). Therefore, it is not needed to verify that the return value is not 0 here.
        address previousOwner = _update(to, tokenId, _msgSender());
        if (previousOwner != from) {
            revert ERC721IncorrectOwner(from, tokenId, previousOwner);
        }
    }

    /**
     * @dev See {IERC721-safeTransferFrom}.
     */
    function safeTransferFrom(address from, address to, uint256 tokenId) public {
        safeTransferFrom(from, to, tokenId, "");
    }

    /**
     * @dev See {IERC721-safeTransferFrom}.
     */
    function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public virtual {
        transferFrom(from, to, tokenId);
        _checkOnERC721Received(from, to, tokenId, data);
    }

    /**
     * @dev Returns the owner of the `tokenId`. Does NOT revert if token doesn't exist
     *
     * IMPORTANT: Any overrides to this function that add ownership of tokens not tracked by the
     * core ERC721 logic MUST be matched with the use of {_increaseBalance} to keep balances
     * consistent with ownership. The invariant to preserve is that for any address `a` the value returned by
     * `balanceOf(a)` must be equal to the number of tokens such that `_ownerOf(tokenId)` is `a`.
     */
    function _ownerOf(uint256 tokenId) internal view virtual returns (address) {
        return _owners[tokenId];
    }

    /**
     * @dev Returns the approved address for `tokenId`. Returns 0 if `tokenId` is not minted.
     */
    function _getApproved(uint256 tokenId) internal view virtual returns (address) {
        return _tokenApprovals[tokenId];
    }

    /**
     * @dev Returns whether `spender` is allowed to manage `owner`'s tokens, or `tokenId` in
     * particular (ignoring whether it is owned by `owner`).
     *
     * WARNING: This function assumes that `owner` is the actual owner of `tokenId` and does not verify this
     * assumption.
     */
    function _isAuthorized(address owner, address spender, uint256 tokenId) internal view virtual returns (bool) {
        return
            spender != address(0) &&
            (owner == spender || isApprovedForAll(owner, spender) || _getApproved(tokenId) == spender);
    }

    /**
     * @dev Checks if `spender` can operate on `tokenId`, assuming the provided `owner` is the actual owner.
     * Reverts if `spender` does not have approval from the provided `owner` for the given token or for all its assets
     * the `spender` for the specific `tokenId`.
     *
     * WARNING: This function assumes that `owner` is the actual owner of `tokenId` and does not verify this
     * assumption.
     */
    function _checkAuthorized(address owner, address spender, uint256 tokenId) internal view virtual {
        if (!_isAuthorized(owner, spender, tokenId)) {
            if (owner == address(0)) {
                revert ERC721NonexistentToken(tokenId);
            } else {
                revert ERC721InsufficientApproval(spender, tokenId);
            }
        }
    }

    /**
     * @dev Unsafe write access to the balances, used by extensions that "mint" tokens using an {ownerOf} override.
     *
     * NOTE: the value is limited to type(uint128).max. This protect against _balance overflow. It is unrealistic that
     * a uint256 would ever overflow from increments when these increments are bounded to uint128 values.
     *
     * WARNING: Increasing an account's balance using this function tends to be paired with an override of the
     * {_ownerOf} function to resolve the ownership of the corresponding tokens so that balances and ownership
     * remain consistent with one another.
     */
    function _increaseBalance(address account, uint128 value) internal virtual {
        unchecked {
            _balances[account] += value;
        }
    }

    /**
     * @dev Transfers `tokenId` from its current owner to `to`, or alternatively mints (or burns) if the current owner
     * (or `to`) is the zero address. Returns the owner of the `tokenId` before the update.
     *
     * The `auth` argument is optional. If the value passed is non 0, then this function will check that
     * `auth` is either the owner of the token, or approved to operate on the token (by the owner).
     *
     * Emits a {Transfer} event.
     *
     * NOTE: If overriding this function in a way that tracks balances, see also {_increaseBalance}.
     */
    function _update(address to, uint256 tokenId, address auth) internal virtual returns (address) {
        address from = _ownerOf(tokenId);

        // Perform (optional) operator check
        if (auth != address(0)) {
            _checkAuthorized(from, auth, tokenId);
        }

        // Execute the update
        if (from != address(0)) {
            // Clear approval. No need to re-authorize or emit the Approval event
            _approve(address(0), tokenId, address(0), false);

            unchecked {
                _balances[from] -= 1;
            }
        }

        if (to != address(0)) {
            unchecked {
                _balances[to] += 1;
            }
        }

        _owners[tokenId] = to;

        emit Transfer(from, to, tokenId);

        return from;
    }

    /**
     * @dev Mints `tokenId` and transfers it to `to`.
     *
     * WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible
     *
     * Requirements:
     *
     * - `tokenId` must not exist.
     * - `to` cannot be the zero address.
     *
     * Emits a {Transfer} event.
     */
    function _mint(address to, uint256 tokenId) internal {
        if (to == address(0)) {
            revert ERC721InvalidReceiver(address(0));
        }
        address previousOwner = _update(to, tokenId, address(0));
        if (previousOwner != address(0)) {
            revert ERC721InvalidSender(address(0));
        }
    }

    /**
     * @dev Mints `tokenId`, transfers it to `to` and checks for `to` acceptance.
     *
     * Requirements:
     *
     * - `tokenId` must not exist.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function _safeMint(address to, uint256 tokenId) internal {
        _safeMint(to, tokenId, "");
    }

    /**
     * @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is
     * forwarded in {IERC721Receiver-onERC721Received} to contract recipients.
     */
    function _safeMint(address to, uint256 tokenId, bytes memory data) internal virtual {
        _mint(to, tokenId);
        _checkOnERC721Received(address(0), to, tokenId, data);
    }

    /**
     * @dev Destroys `tokenId`.
     * The approval is cleared when the token is burned.
     * This is an internal function that does not check if the sender is authorized to operate on the token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     *
     * Emits a {Transfer} event.
     */
    function _burn(uint256 tokenId) internal {
        address previousOwner = _update(address(0), tokenId, address(0));
        if (previousOwner == address(0)) {
            revert ERC721NonexistentToken(tokenId);
        }
    }

    /**
     * @dev Transfers `tokenId` from `from` to `to`.
     *  As opposed to {transferFrom}, this imposes no restrictions on msg.sender.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     *
     * Emits a {Transfer} event.
     */
    function _transfer(address from, address to, uint256 tokenId) internal {
        if (to == address(0)) {
            revert ERC721InvalidReceiver(address(0));
        }
        address previousOwner = _update(to, tokenId, address(0));
        if (previousOwner == address(0)) {
            revert ERC721NonexistentToken(tokenId);
        } else if (previousOwner != from) {
            revert ERC721IncorrectOwner(from, tokenId, previousOwner);
        }
    }

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`, checking that contract recipients
     * are aware of the ERC721 standard to prevent tokens from being forever locked.
     *
     * `data` is additional data, it has no specified format and it is sent in call to `to`.
     *
     * This internal function is like {safeTransferFrom} in the sense that it invokes
     * {IERC721Receiver-onERC721Received} on the receiver, and can be used to e.g.
     * implement alternative mechanisms to perform token transfer, such as signature-based.
     *
     * Requirements:
     *
     * - `tokenId` token must exist and be owned by `from`.
     * - `to` cannot be the zero address.
     * - `from` cannot be the zero address.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function _safeTransfer(address from, address to, uint256 tokenId) internal {
        _safeTransfer(from, to, tokenId, "");
    }

    /**
     * @dev Same as {xref-ERC721-_safeTransfer-address-address-uint256-}[`_safeTransfer`], with an additional `data` parameter which is
     * forwarded in {IERC721Receiver-onERC721Received} to contract recipients.
     */
    function _safeTransfer(address from, address to, uint256 tokenId, bytes memory data) internal virtual {
        _transfer(from, to, tokenId);
        _checkOnERC721Received(from, to, tokenId, data);
    }

    /**
     * @dev Approve `to` to operate on `tokenId`
     *
     * The `auth` argument is optional. If the value passed is non 0, then this function will check that `auth` is
     * either the owner of the token, or approved to operate on all tokens held by this owner.
     *
     * Emits an {Approval} event.
     *
     * Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument.
     */
    function _approve(address to, uint256 tokenId, address auth) internal {
        _approve(to, tokenId, auth, true);
    }

    /**
     * @dev Variant of `_approve` with an optional flag to enable or disable the {Approval} event. The event is not
     * emitted in the context of transfers.
     */
    function _approve(address to, uint256 tokenId, address auth, bool emitEvent) internal virtual {
        // Avoid reading the owner unless necessary
        if (emitEvent || auth != address(0)) {
            address owner = _requireOwned(tokenId);

            // We do not use _isAuthorized because single-token approvals should not be able to call approve
            if (auth != address(0) && owner != auth && !isApprovedForAll(owner, auth)) {
                revert ERC721InvalidApprover(auth);
            }

            if (emitEvent) {
                emit Approval(owner, to, tokenId);
            }
        }

        _tokenApprovals[tokenId] = to;
    }

    /**
     * @dev Approve `operator` to operate on all of `owner` tokens
     *
     * Requirements:
     * - operator can't be the address zero.
     *
     * Emits an {ApprovalForAll} event.
     */
    function _setApprovalForAll(address owner, address operator, bool approved) internal virtual {
        if (operator == address(0)) {
            revert ERC721InvalidOperator(operator);
        }
        _operatorApprovals[owner][operator] = approved;
        emit ApprovalForAll(owner, operator, approved);
    }

    /**
     * @dev Reverts if the `tokenId` doesn't have a current owner (it hasn't been minted, or it has been burned).
     * Returns the owner.
     *
     * Overrides to ownership logic should be done to {_ownerOf}.
     */
    function _requireOwned(uint256 tokenId) internal view returns (address) {
        address owner = _ownerOf(tokenId);
        if (owner == address(0)) {
            revert ERC721NonexistentToken(tokenId);
        }
        return owner;
    }

    /**
     * @dev Private function to invoke {IERC721Receiver-onERC721Received} on a target address. This will revert if the
     * recipient doesn't accept the token transfer. The call is not executed if the target address is not a contract.
     *
     * @param from address representing the previous owner of the given token ID
     * @param to target address that will receive the tokens
     * @param tokenId uint256 ID of the token to be transferred
     * @param data bytes optional data to send along with the call
     */
    function _checkOnERC721Received(address from, address to, uint256 tokenId, bytes memory data) private {
        if (to.code.length > 0) {
            try IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, data) returns (bytes4 retval) {
                if (retval != IERC721Receiver.onERC721Received.selector) {
                    revert ERC721InvalidReceiver(to);
                }
            } catch (bytes memory reason) {
                if (reason.length == 0) {
                    revert ERC721InvalidReceiver(to);
                } else {
                    /// @solidity memory-safe-assembly
                    assembly {
                        revert(add(32, reason), mload(reason))
                    }
                }
            }
        }
    }
}

File 22 of 28 : ECDSA.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/ECDSA.sol)

pragma solidity ^0.8.20;

/**
 * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
 *
 * These functions can be used to verify that a message was signed by the holder
 * of the private keys of a given address.
 */
library ECDSA {
    enum RecoverError {
        NoError,
        InvalidSignature,
        InvalidSignatureLength,
        InvalidSignatureS
    }

    /**
     * @dev The signature derives the `address(0)`.
     */
    error ECDSAInvalidSignature();

    /**
     * @dev The signature has an invalid length.
     */
    error ECDSAInvalidSignatureLength(uint256 length);

    /**
     * @dev The signature has an S value that is in the upper half order.
     */
    error ECDSAInvalidSignatureS(bytes32 s);

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with `signature` or an error. This will not
     * return address(0) without also returning an error description. Errors are documented using an enum (error type)
     * and a bytes32 providing additional information about the error.
     *
     * If no error is returned, then the address can be used for verification purposes.
     *
     * The `ecrecover` EVM precompile allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it.
     *
     * Documentation for signature generation:
     * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
     * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
     */
    function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError, bytes32) {
        if (signature.length == 65) {
            bytes32 r;
            bytes32 s;
            uint8 v;
            // ecrecover takes the signature parameters, and the only way to get them
            // currently is to use assembly.
            /// @solidity memory-safe-assembly
            assembly {
                r := mload(add(signature, 0x20))
                s := mload(add(signature, 0x40))
                v := byte(0, mload(add(signature, 0x60)))
            }
            return tryRecover(hash, v, r, s);
        } else {
            return (address(0), RecoverError.InvalidSignatureLength, bytes32(signature.length));
        }
    }

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature`. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM precompile allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it.
     */
    function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
        (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, signature);
        _throwError(error, errorArg);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
     *
     * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
     */
    function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError, bytes32) {
        unchecked {
            bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
            // We do not check for an overflow here since the shift operation results in 0 or 1.
            uint8 v = uint8((uint256(vs) >> 255) + 27);
            return tryRecover(hash, v, r, s);
        }
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
     */
    function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) {
        (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, r, vs);
        _throwError(error, errorArg);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `v`,
     * `r` and `s` signature fields separately.
     */
    function tryRecover(
        bytes32 hash,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal pure returns (address, RecoverError, bytes32) {
        // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
        // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
        // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
        // signatures from current libraries generate a unique signature with an s-value in the lower half order.
        //
        // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
        // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
        // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
        // these malleable signatures as well.
        if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
            return (address(0), RecoverError.InvalidSignatureS, s);
        }

        // If the signature is valid (and not malleable), return the signer address
        address signer = ecrecover(hash, v, r, s);
        if (signer == address(0)) {
            return (address(0), RecoverError.InvalidSignature, bytes32(0));
        }

        return (signer, RecoverError.NoError, bytes32(0));
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `v`,
     * `r` and `s` signature fields separately.
     */
    function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
        (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, v, r, s);
        _throwError(error, errorArg);
        return recovered;
    }

    /**
     * @dev Optionally reverts with the corresponding custom error according to the `error` argument provided.
     */
    function _throwError(RecoverError error, bytes32 errorArg) private pure {
        if (error == RecoverError.NoError) {
            return; // no error: do nothing
        } else if (error == RecoverError.InvalidSignature) {
            revert ECDSAInvalidSignature();
        } else if (error == RecoverError.InvalidSignatureLength) {
            revert ECDSAInvalidSignatureLength(uint256(errorArg));
        } else if (error == RecoverError.InvalidSignatureS) {
            revert ECDSAInvalidSignatureS(errorArg);
        }
    }
}

File 23 of 28 : IERC721Metadata.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/extensions/IERC721Metadata.sol)

pragma solidity ^0.8.20;

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

/**
 * @title ERC-721 Non-Fungible Token Standard, optional metadata extension
 * @dev See https://eips.ethereum.org/EIPS/eip-721
 */
interface IERC721Metadata is IERC721 {
    /**
     * @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);
}

File 24 of 28 : Strings.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Strings.sol)

pragma solidity ^0.8.20;

import {Math} from "./math/Math.sol";
import {SignedMath} from "./math/SignedMath.sol";

/**
 * @dev String operations.
 */
library Strings {
    bytes16 private constant HEX_DIGITS = "0123456789abcdef";
    uint8 private constant ADDRESS_LENGTH = 20;

    /**
     * @dev The `value` string doesn't fit in the specified `length`.
     */
    error StringsInsufficientHexLength(uint256 value, uint256 length);

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        unchecked {
            uint256 length = Math.log10(value) + 1;
            string memory buffer = new string(length);
            uint256 ptr;
            /// @solidity memory-safe-assembly
            assembly {
                ptr := add(buffer, add(32, length))
            }
            while (true) {
                ptr--;
                /// @solidity memory-safe-assembly
                assembly {
                    mstore8(ptr, byte(mod(value, 10), HEX_DIGITS))
                }
                value /= 10;
                if (value == 0) break;
            }
            return buffer;
        }
    }

    /**
     * @dev Converts a `int256` to its ASCII `string` decimal representation.
     */
    function toStringSigned(int256 value) internal pure returns (string memory) {
        return string.concat(value < 0 ? "-" : "", toString(SignedMath.abs(value)));
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        unchecked {
            return toHexString(value, Math.log256(value) + 1);
        }
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
     */
    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
        uint256 localValue = value;
        bytes memory buffer = new bytes(2 * length + 2);
        buffer[0] = "0";
        buffer[1] = "x";
        for (uint256 i = 2 * length + 1; i > 1; --i) {
            buffer[i] = HEX_DIGITS[localValue & 0xf];
            localValue >>= 4;
        }
        if (localValue != 0) {
            revert StringsInsufficientHexLength(value, length);
        }
        return string(buffer);
    }

    /**
     * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal
     * representation.
     */
    function toHexString(address addr) internal pure returns (string memory) {
        return toHexString(uint256(uint160(addr)), ADDRESS_LENGTH);
    }

    /**
     * @dev Returns true if the two strings are equal.
     */
    function equal(string memory a, string memory b) internal pure returns (bool) {
        return bytes(a).length == bytes(b).length && keccak256(bytes(a)) == keccak256(bytes(b));
    }
}

File 25 of 28 : ERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/ERC165.sol)

pragma solidity ^0.8.20;

import {IERC165} from "./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);
 * }
 * ```
 */
abstract contract ERC165 is IERC165 {
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
        return interfaceId == type(IERC165).interfaceId;
    }
}

File 26 of 28 : draft-IERC6093.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/draft-IERC6093.sol)
pragma solidity ^0.8.20;

/**
 * @dev Standard ERC20 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC20 tokens.
 */
interface IERC20Errors {
    /**
     * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     * @param balance Current balance for the interacting account.
     * @param needed Minimum amount required to perform a transfer.
     */
    error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);

    /**
     * @dev Indicates a failure with the token `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     */
    error ERC20InvalidSender(address sender);

    /**
     * @dev Indicates a failure with the token `receiver`. Used in transfers.
     * @param receiver Address to which tokens are being transferred.
     */
    error ERC20InvalidReceiver(address receiver);

    /**
     * @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers.
     * @param spender Address that may be allowed to operate on tokens without being their owner.
     * @param allowance Amount of tokens a `spender` is allowed to operate with.
     * @param needed Minimum amount required to perform a transfer.
     */
    error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);

    /**
     * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
     * @param approver Address initiating an approval operation.
     */
    error ERC20InvalidApprover(address approver);

    /**
     * @dev Indicates a failure with the `spender` to be approved. Used in approvals.
     * @param spender Address that may be allowed to operate on tokens without being their owner.
     */
    error ERC20InvalidSpender(address spender);
}

/**
 * @dev Standard ERC721 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC721 tokens.
 */
interface IERC721Errors {
    /**
     * @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in EIP-20.
     * Used in balance queries.
     * @param owner Address of the current owner of a token.
     */
    error ERC721InvalidOwner(address owner);

    /**
     * @dev Indicates a `tokenId` whose `owner` is the zero address.
     * @param tokenId Identifier number of a token.
     */
    error ERC721NonexistentToken(uint256 tokenId);

    /**
     * @dev Indicates an error related to the ownership over a particular token. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     * @param tokenId Identifier number of a token.
     * @param owner Address of the current owner of a token.
     */
    error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner);

    /**
     * @dev Indicates a failure with the token `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     */
    error ERC721InvalidSender(address sender);

    /**
     * @dev Indicates a failure with the token `receiver`. Used in transfers.
     * @param receiver Address to which tokens are being transferred.
     */
    error ERC721InvalidReceiver(address receiver);

    /**
     * @dev Indicates a failure with the `operator`’s approval. Used in transfers.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     * @param tokenId Identifier number of a token.
     */
    error ERC721InsufficientApproval(address operator, uint256 tokenId);

    /**
     * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
     * @param approver Address initiating an approval operation.
     */
    error ERC721InvalidApprover(address approver);

    /**
     * @dev Indicates a failure with the `operator` to be approved. Used in approvals.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     */
    error ERC721InvalidOperator(address operator);
}

/**
 * @dev Standard ERC1155 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC1155 tokens.
 */
interface IERC1155Errors {
    /**
     * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     * @param balance Current balance for the interacting account.
     * @param needed Minimum amount required to perform a transfer.
     * @param tokenId Identifier number of a token.
     */
    error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId);

    /**
     * @dev Indicates a failure with the token `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     */
    error ERC1155InvalidSender(address sender);

    /**
     * @dev Indicates a failure with the token `receiver`. Used in transfers.
     * @param receiver Address to which tokens are being transferred.
     */
    error ERC1155InvalidReceiver(address receiver);

    /**
     * @dev Indicates a failure with the `operator`’s approval. Used in transfers.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     * @param owner Address of the current owner of a token.
     */
    error ERC1155MissingApprovalForAll(address operator, address owner);

    /**
     * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
     * @param approver Address initiating an approval operation.
     */
    error ERC1155InvalidApprover(address approver);

    /**
     * @dev Indicates a failure with the `operator` to be approved. Used in approvals.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     */
    error ERC1155InvalidOperator(address operator);

    /**
     * @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation.
     * Used in batch transfers.
     * @param idsLength Length of the array of token identifiers
     * @param valuesLength Length of the array of token amounts
     */
    error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength);
}

File 27 of 28 : Math.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol)

pragma solidity ^0.8.20;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    /**
     * @dev Muldiv operation overflow.
     */
    error MathOverflowedMulDiv();

    enum Rounding {
        Floor, // Toward negative infinity
        Ceil, // Toward positive infinity
        Trunc, // Toward zero
        Expand // Away from zero
    }

    /**
     * @dev Returns the addition of two unsigned integers, with an overflow flag.
     */
    function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            uint256 c = a + b;
            if (c < a) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, with an overflow flag.
     */
    function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b > a) return (false, 0);
            return (true, a - b);
        }
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
     */
    function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
            // benefit is lost if 'b' is also tested.
            // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
            if (a == 0) return (true, 0);
            uint256 c = a * b;
            if (c / a != b) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the division of two unsigned integers, with a division by zero flag.
     */
    function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a / b);
        }
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
     */
    function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a % b);
        }
    }

    /**
     * @dev Returns the largest of two numbers.
     */
    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return a > b ? a : b;
    }

    /**
     * @dev Returns the smallest of two numbers.
     */
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two numbers. The result is rounded towards
     * zero.
     */
    function average(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b) / 2 can overflow.
        return (a & b) + (a ^ b) / 2;
    }

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds towards infinity instead
     * of rounding towards zero.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        if (b == 0) {
            // Guarantee the same behavior as in a regular Solidity division.
            return a / b;
        }

        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a == 0 ? 0 : (a - 1) / b + 1;
    }

    /**
     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
     * denominator == 0.
     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by
     * Uniswap Labs also under MIT license.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
        unchecked {
            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
            // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
            // variables such that product = prod1 * 2^256 + prod0.
            uint256 prod0 = x * y; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(x, y, not(0))
                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
            }

            // Handle non-overflow cases, 256 by 256 division.
            if (prod1 == 0) {
                // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                // The surrounding unchecked block does not change this fact.
                // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                return prod0 / denominator;
            }

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            if (denominator <= prod1) {
                revert MathOverflowedMulDiv();
            }

            ///////////////////////////////////////////////
            // 512 by 256 division.
            ///////////////////////////////////////////////

            // Make division exact by subtracting the remainder from [prod1 prod0].
            uint256 remainder;
            assembly {
                // Compute remainder using mulmod.
                remainder := mulmod(x, y, denominator)

                // Subtract 256 bit number from 512 bit number.
                prod1 := sub(prod1, gt(remainder, prod0))
                prod0 := sub(prod0, remainder)
            }

            // Factor powers of two out of denominator and compute largest power of two divisor of denominator.
            // Always >= 1. See https://cs.stackexchange.com/q/138556/92363.

            uint256 twos = denominator & (0 - denominator);
            assembly {
                // Divide denominator by twos.
                denominator := div(denominator, twos)

                // Divide [prod1 prod0] by twos.
                prod0 := div(prod0, twos)

                // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                twos := add(div(sub(0, twos), twos), 1)
            }

            // Shift in bits from prod1 into prod0.
            prod0 |= prod1 * twos;

            // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
            // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
            // four bits. That is, denominator * inv = 1 mod 2^4.
            uint256 inverse = (3 * denominator) ^ 2;

            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also
            // works in modular arithmetic, doubling the correct bits in each step.
            inverse *= 2 - denominator * inverse; // inverse mod 2^8
            inverse *= 2 - denominator * inverse; // inverse mod 2^16
            inverse *= 2 - denominator * inverse; // inverse mod 2^32
            inverse *= 2 - denominator * inverse; // inverse mod 2^64
            inverse *= 2 - denominator * inverse; // inverse mod 2^128
            inverse *= 2 - denominator * inverse; // inverse mod 2^256

            // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
            // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
            // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
            // is no longer required.
            result = prod0 * inverse;
            return result;
        }
    }

    /**
     * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
        uint256 result = mulDiv(x, y, denominator);
        if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) {
            result += 1;
        }
        return result;
    }

    /**
     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
     * towards zero.
     *
     * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
     */
    function sqrt(uint256 a) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }

        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
        //
        // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
        // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
        //
        // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
        // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
        // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
        //
        // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
        uint256 result = 1 << (log2(a) >> 1);

        // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
        // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
        // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
        // into the expected uint128 result.
        unchecked {
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            return min(result, a / result);
        }
    }

    /**
     * @notice Calculates sqrt(a), following the selected rounding direction.
     */
    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = sqrt(a);
            return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 2 of a positive value rounded towards zero.
     * Returns 0 if given 0.
     */
    function log2(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 128;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 64;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 32;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 16;
            }
            if (value >> 8 > 0) {
                value >>= 8;
                result += 8;
            }
            if (value >> 4 > 0) {
                value >>= 4;
                result += 4;
            }
            if (value >> 2 > 0) {
                value >>= 2;
                result += 2;
            }
            if (value >> 1 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log2(value);
            return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 10 of a positive value rounded towards zero.
     * Returns 0 if given 0.
     */
    function log10(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >= 10 ** 64) {
                value /= 10 ** 64;
                result += 64;
            }
            if (value >= 10 ** 32) {
                value /= 10 ** 32;
                result += 32;
            }
            if (value >= 10 ** 16) {
                value /= 10 ** 16;
                result += 16;
            }
            if (value >= 10 ** 8) {
                value /= 10 ** 8;
                result += 8;
            }
            if (value >= 10 ** 4) {
                value /= 10 ** 4;
                result += 4;
            }
            if (value >= 10 ** 2) {
                value /= 10 ** 2;
                result += 2;
            }
            if (value >= 10 ** 1) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log10(value);
            return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 256 of a positive value rounded towards zero.
     * Returns 0 if given 0.
     *
     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
     */
    function log256(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 16;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 8;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 4;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 2;
            }
            if (value >> 8 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 256, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log256(value);
            return result + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0);
        }
    }

    /**
     * @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
     */
    function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
        return uint8(rounding) % 2 == 1;
    }
}

File 28 of 28 : SignedMath.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SignedMath.sol)

pragma solidity ^0.8.20;

/**
 * @dev Standard signed math utilities missing in the Solidity language.
 */
library SignedMath {
    /**
     * @dev Returns the largest of two signed numbers.
     */
    function max(int256 a, int256 b) internal pure returns (int256) {
        return a > b ? a : b;
    }

    /**
     * @dev Returns the smallest of two signed numbers.
     */
    function min(int256 a, int256 b) internal pure returns (int256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two signed numbers without overflow.
     * The result is rounded towards zero.
     */
    function average(int256 a, int256 b) internal pure returns (int256) {
        // Formula from the book "Hacker's Delight"
        int256 x = (a & b) + ((a ^ b) >> 1);
        return x + (int256(uint256(x) >> 255) & (a ^ b));
    }

    /**
     * @dev Returns the absolute unsigned value of a signed value.
     */
    function abs(int256 n) internal pure returns (uint256) {
        unchecked {
            // must be unchecked in order to support `n = type(int256).min`
            return uint256(n >= 0 ? n : -n);
        }
    }
}

Settings
{
  "remappings": [
    "ds-test/=lib/forge-std/lib/ds-test/src/",
    "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
    "forge-std/=lib/forge-std/src/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/",
    "openzeppelin/=lib/openzeppelin-contracts/contracts/",
    "sstore2/=lib/sstore2/contracts/",
    "TokenGatedAccount/=lib/TokenGatedAccount/src/",
    "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "shanghai",
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"contract IERC721","name":"_miladysContract","type":"address"},{"internalType":"contract IERC721","name":"_avatarContract","type":"address"},{"internalType":"contract AzimuthOwnerWrapper","name":"_azimuthOwnerWrapperContract","type":"address"},{"internalType":"contract ISoulboundAccessories","name":"_soulboundAccessoriesContract","type":"address"},{"internalType":"contract TBARegistry","name":"_tgaRegistry","type":"address"},{"internalType":"contract TokenGatedAccount","name":"_tgaAccountImpl","type":"address"},{"internalType":"contract ShipStore","name":"_shipStoreContract","type":"address"},{"internalType":"uint256","name":"_shipStackId","type":"uint256"},{"internalType":"contract PillStore","name":"_pillStoreContract","type":"address"},{"internalType":"uint256","name":"_mosPillSetId","type":"uint256"},{"internalType":"uint256","name":"_onboardingGasForwardAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"topLevelEOA","type":"address"},{"indexed":true,"internalType":"uint256","name":"miladyId","type":"uint256"},{"indexed":true,"internalType":"uint32","name":"shipId","type":"uint32"},{"indexed":false,"internalType":"uint256","name":"pillId","type":"uint256"}],"name":"PackageBought","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"miladyId","type":"uint256"}],"name":"SoulboundMintRequested","type":"event"},{"inputs":[],"name":"avatarContract","outputs":[{"internalType":"contract IERC721","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"azimuthOwnerWrapperContract","outputs":[{"internalType":"contract AzimuthOwnerWrapper","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getMarketInfo","outputs":[{"internalType":"uint256","name":"shipPrice","type":"uint256"},{"internalType":"uint256","name":"numShipsRemaining","type":"uint256"},{"internalType":"uint256","name":"appPrice","type":"uint256"},{"internalType":"uint256","name":"onboardingGasPrice","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"miladyId","type":"uint256"}],"name":"getOnboardedAzimuthPointsForMilady","outputs":[{"internalType":"uint256","name":"numOnboardedPoints","type":"uint256"},{"internalType":"uint32[]","name":"onboardedPoints","type":"uint32[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"miladysContract","outputs":[{"internalType":"contract IERC721","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mosPillSetId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"miladyId","type":"uint256"}],"name":"onboardAndPurchaseForMilady","outputs":[{"internalType":"uint32","name":"shipId","type":"uint32"},{"internalType":"uint256","name":"pillId","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"onboardingGasForwardAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pillStoreContract","outputs":[{"internalType":"contract PillStore","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"setOnboardingGasForwardAmount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"shipStackId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"shipStoreContract","outputs":[{"internalType":"contract ShipStore","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"soulboundAccessoriesContract","outputs":[{"internalType":"contract ISoulboundAccessories","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tgaAccountImpl","outputs":[{"internalType":"contract TokenGatedAccount","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tgaRegistry","outputs":[{"internalType":"contract TBARegistry","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"}]

6101c060405234801562000011575f80fd5b50604051620019673803806200196783398101604081905262000034916200011c565b33806200005a57604051631e4fbdf760e01b81525f600482015260240160405180910390fd5b6200006581620000b5565b506001600160a01b039a8b16608052988a1660a05296891660c05294881660e052928716610100529086166101205285166101405261016052909216610180526101a091909152600155620001f6565b5f80546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6001600160a01b038116811462000119575f80fd5b50565b5f805f805f805f805f805f6101608c8e03121562000138575f80fd5b8b51620001458162000104565b60208d0151909b50620001588162000104565b60408d0151909a506200016b8162000104565b60608d01519099506200017e8162000104565b60808d0151909850620001918162000104565b60a08d0151909750620001a48162000104565b60c08d0151909650620001b78162000104565b60e08d01516101008e01519196509450620001d28162000104565b809350506101208c015191506101408c015190509295989b509295989b9093969950565b60805160a05160c05160e05161010051610120516101405161016051610180516101a05161163d6200032a5f395f818161030b015281816105140152818161080e01528181610a830152818161101601526110c101525f81816103f6015281816105400152818161084101528181610ab80152818161104901526110fa01525f818161021501528181610489015281816109d50152610f2d01525f81816103c30152818161045a01528181610a090152610f6701525f818161019701528181610d20015261119701525f81816102a501528181610d6601526111eb01525f81816103710152610e3201525f818161033e01528181610678015281816107bb0152610fdc01525f81816101e20152610e0a01525f81816102d8015281816105e801528181610bba0152610ddf015261163d5ff3fe608060405260043610610105575f3560e01c8063acd1c3b211610092578063e100a3b711610062578063e100a3b714610360578063f2fde38b14610393578063f5ae9c40146103b2578063fb985672146103e5578063ff9f50da14610418575f80fd5b8063acd1c3b214610294578063b17afbab146102c7578063bc540919146102fa578063bd3c06501461032d575f80fd5b806345f0791d116100d857806345f0791d146101d157806370ab400614610204578063715018a6146102375780638273e7841461024b5780638da5cb5b14610278575f80fd5b80631b1baaa81461010957806321b2a6fb1461012a57806323341a05146101525780633999629514610186575b5f80fd5b348015610114575f80fd5b50610128610123366004611233565b610447565b005b348015610135575f80fd5b5061013f60015481565b6040519081526020015b60405180910390f35b34801561015d575f80fd5b50610166610454565b604080519485526020850193909352918301526060820152608001610149565b348015610191575f80fd5b506101b97f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b039091168152602001610149565b3480156101dc575f80fd5b506101b97f000000000000000000000000000000000000000000000000000000000000000081565b34801561020f575f80fd5b5061013f7f000000000000000000000000000000000000000000000000000000000000000081565b348015610242575f80fd5b506101286105cf565b348015610256575f80fd5b5061026a610265366004611233565b6105e2565b60405161014992919061124a565b348015610283575f80fd5b505f546001600160a01b03166101b9565b34801561029f575f80fd5b506101b97f000000000000000000000000000000000000000000000000000000000000000081565b3480156102d2575f80fd5b506101b97f000000000000000000000000000000000000000000000000000000000000000081565b348015610305575f80fd5b5061013f7f000000000000000000000000000000000000000000000000000000000000000081565b348015610338575f80fd5b506101b97f000000000000000000000000000000000000000000000000000000000000000081565b34801561036b575f80fd5b506101b97f000000000000000000000000000000000000000000000000000000000000000081565b34801561039e575f80fd5b506101286103ad3660046112b0565b610984565b3480156103bd575f80fd5b506101b97f000000000000000000000000000000000000000000000000000000000000000081565b3480156103f0575f80fd5b506101b97f000000000000000000000000000000000000000000000000000000000000000081565b61042b610426366004611233565b6109c6565b6040805163ffffffff9093168352602083019190915201610149565b61044f610c8e565b600155565b5f805f807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663a24eea1a7f00000000000000000000000000000000000000000000000000000000000000006040518263ffffffff1660e01b81526004016104c691815260200190565b60c060405180830381865afa1580156104e1573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061050591906112d2565b60405163132124f160e01b81527f000000000000000000000000000000000000000000000000000000000000000060048201529399509750507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316935063132124f19250506024015f60405180830381865afa15801561058f573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526105b69190810190611386565b50506001549a9b999a9499949850939650505050505050565b6105d7610c8e565b6105e05f610cba565b565b5f60605f7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316636352211e856040518263ffffffff1660e01b815260040161063491815260200190565b602060405180830381865afa15801561064f573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061067391906114a9565b90505f7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316633144d8096040518163ffffffff1660e01b8152600401602060405180830381865afa1580156106d2573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906106f691906114a9565b604051630333d49960e01b81526001600160a01b0384811660048301529190911690630333d499906024015f60405180830381865afa15801561073b573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f1916820160405261076291908101906114dc565b9050805167ffffffffffffffff81111561077e5761077e611341565b6040519080825280602002602001820160405280156107a7578160200160208202803683370190505b5092505f5b815181101561097c575f6107ff7f00000000000000000000000000000000000000000000000000000000000000008484815181106107ec576107ec611584565b602002602001015163ffffffff16610d09565b6040516364ee130960e11b81527f000000000000000000000000000000000000000000000000000000000000000060048201529091505f906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063c9dc261290602401602060405180830381865afa158015610886573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906108aa91906114a9565b6040516370a0823160e01b81526001600160a01b03848116600483015291909116906370a0823190602401602060405180830381865afa1580156108f0573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906109149190611598565b11156109695782828151811061092c5761092c611584565b602002602001015185878151811061094657610946611584565b63ffffffff9092166020928302919091019091015285610965816115c3565b9650505b5080610974816115c3565b9150506107ac565b505050915091565b61098c610c8e565b6001600160a01b0381166109ba57604051631e4fbdf760e01b81525f60048201526024015b60405180910390fd5b6109c381610cba565b50565b604051635127750d60e11b81527f000000000000000000000000000000000000000000000000000000000000000060048201525f90819081906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063a24eea1a9060240160c060405180830381865afa158015610a4e573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610a7291906112d2565b505060405163132124f160e01b81527f000000000000000000000000000000000000000000000000000000000000000060048201529194505f9350506001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016915063132124f1906024015f60405180830381865afa158015610afd573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052610b249190810190611386565b505050505050925050505f6001548284610b3e91906115db565b610b4891906115db565b9050803414610b995760405162461bcd60e51b815260206004820152601f60248201527f696e636f727265637420657468657220616d6f756e7420696e636c756465640060448201526064016109b1565b610ba286610dda565b6040516331a9108f60e11b8152600481018790525f907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690636352211e90602401602060405180830381865afa158015610c07573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c2b91906114a9565b9050610c38818585610f1e565b604051818152919750955063ffffffff87169088906001600160a01b038416907f9b209d7e6b5fcab64f06a2a7df290ffe14c3baaf0190ce220f1795854768a1989060200160405180910390a450505050915091565b5f546001600160a01b031633146105e05760405163118cdaa760e01b81523360048201526024016109b1565b5f80546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b604051632f4de29b60e11b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811660048301524660248301528381166044830152606482018390525f60848301819052917f000000000000000000000000000000000000000000000000000000000000000090911690635e9bc5369060a401602060405180830381865afa158015610dad573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610dd191906114a9565b90505b92915050565b610e047f000000000000000000000000000000000000000000000000000000000000000082611180565b50610e2f7f000000000000000000000000000000000000000000000000000000000000000082611180565b507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663be408c5c6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610e8c573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610eb091906114a9565b6001600160a01b03166108fc60015490811502906040515f60405180830381858888f19350505050158015610ee7573d5f803e3d5ffd5b506040518181527fee8cd96597e5ba5ec909c4940b70d6f3d1c4e5f728af0b94a6c9b8be4d47715f9060200160405180910390a150565b6040516308b3ac6360e41b81527f000000000000000000000000000000000000000000000000000000000000000060048201526001600160a01b0384811660248301525f9182917f00000000000000000000000000000000000000000000000000000000000000001690638b3ac63090869060440160206040518083038185885af1158015610faf573d5f803e3d5ffd5b50505050506040513d601f19601f82011682018060405250810190610fd491906115ee565b91505f6110077f00000000000000000000000000000000000000000000000000000000000000008463ffffffff16611180565b6040516364ee130960e11b81527f000000000000000000000000000000000000000000000000000000000000000060048201529091505f906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063c9dc261290602401602060405180830381865afa15801561108e573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906110b291906114a9565b604051633e3799e960e01b81527f000000000000000000000000000000000000000000000000000000000000000060048201526001600160a01b0384811660248301529192507f000000000000000000000000000000000000000000000000000000000000000090911690633e3799e990879060440160206040518083038185885af1158015611144573d5f803e3d5ffd5b50505050506040513d601f19601f820116820180604052508101906111699190611598565b92506111758184611180565b505050935093915050565b60405163da7323b360e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811660048301524660248301528381166044830152606482018390525f6084830181905260c060a484015260c48301819052917f00000000000000000000000000000000000000000000000000000000000000009091169063da7323b39060e4016020604051808303815f875af1158015610dad573d5f803e3d5ffd5b5f60208284031215611243575f80fd5b5035919050565b5f60408201848352602060408185015281855180845260608601915082870193505f5b8181101561128f57845163ffffffff168352938301939183019160010161126d565b5090979650505050505050565b6001600160a01b03811681146109c3575f80fd5b5f602082840312156112c0575f80fd5b81356112cb8161129c565b9392505050565b5f805f805f8060c087890312156112e7575f80fd5b86516112f28161129c565b60208801519096506113038161129c565b60408801516060890151919650945061131b8161129c565b608088015190935061132c8161129c565b8092505060a087015190509295509295509295565b634e487b7160e01b5f52604160045260245ffd5b604051601f8201601f1916810167ffffffffffffffff8111828210171561137e5761137e611341565b604052919050565b5f805f805f805f805f6101208a8c03121561139f575f80fd5b89516113aa8161129c565b809950506020808b01516113bd8161129c565b8099505060408b0151975060608b0151965060808b01516113dd8161129c565b60a08c01519096506113ee8161129c565b60c08c01519095506113ff8161129c565b60e08c015190945067ffffffffffffffff8082111561141c575f80fd5b818d0191508d601f83011261142f575f80fd5b81518181111561144157611441611341565b611453601f8201601f19168501611355565b91508082528e84828501011115611468575f80fd5b5f5b8181101561148557838101850151838201860152840161146a565b505f848284010152508094505050506101008a015190509295985092959850929598565b5f602082840312156114b9575f80fd5b81516112cb8161129c565b805163ffffffff811681146114d7575f80fd5b919050565b5f60208083850312156114ed575f80fd5b825167ffffffffffffffff80821115611504575f80fd5b818501915085601f830112611517575f80fd5b81518181111561152957611529611341565b8060051b915061153a848301611355565b8181529183018401918481019088841115611553575f80fd5b938501935b8385101561157857611569856114c4565b82529385019390850190611558565b98975050505050505050565b634e487b7160e01b5f52603260045260245ffd5b5f602082840312156115a8575f80fd5b5051919050565b634e487b7160e01b5f52601160045260245ffd5b5f600182016115d4576115d46115af565b5060010190565b80820180821115610dd457610dd46115af565b5f602082840312156115fe575f80fd5b610dd1826114c456fea2646970667358221220e7b9489d8af7f8da43e3563f691d3f4066c3fd4da1ab01cd59930e25e15849fb64736f6c634300081400330000000000000000000000005af0d9827e0c53e4799bb226655a1de152a425a50000000000000000000000000ef38ae5b7ba0b8641cf34c2b9bac3694b92eeff00000000000000000000000062460d1930f09f43d853c8a0dd96e0b95caccbef000000000000000000000000223d1aec02b2db27f8988807f5c56f2f421138a900000000000000000000000067d12c4db022c543cb7a678f882edc935b8989400000000000000000000000004584dbf0510e86dcc2f36038c6473b1a0fc5aef300000000000000000000000071389b712311de08a92feaf99565be13fc901b0b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000092030a3604b1883f54a2035571422ecc781d78b200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000058d15e17628000

Deployed Bytecode

0x608060405260043610610105575f3560e01c8063acd1c3b211610092578063e100a3b711610062578063e100a3b714610360578063f2fde38b14610393578063f5ae9c40146103b2578063fb985672146103e5578063ff9f50da14610418575f80fd5b8063acd1c3b214610294578063b17afbab146102c7578063bc540919146102fa578063bd3c06501461032d575f80fd5b806345f0791d116100d857806345f0791d146101d157806370ab400614610204578063715018a6146102375780638273e7841461024b5780638da5cb5b14610278575f80fd5b80631b1baaa81461010957806321b2a6fb1461012a57806323341a05146101525780633999629514610186575b5f80fd5b348015610114575f80fd5b50610128610123366004611233565b610447565b005b348015610135575f80fd5b5061013f60015481565b6040519081526020015b60405180910390f35b34801561015d575f80fd5b50610166610454565b604080519485526020850193909352918301526060820152608001610149565b348015610191575f80fd5b506101b97f0000000000000000000000004584dbf0510e86dcc2f36038c6473b1a0fc5aef381565b6040516001600160a01b039091168152602001610149565b3480156101dc575f80fd5b506101b97f0000000000000000000000000ef38ae5b7ba0b8641cf34c2b9bac3694b92eeff81565b34801561020f575f80fd5b5061013f7f000000000000000000000000000000000000000000000000000000000000000081565b348015610242575f80fd5b506101286105cf565b348015610256575f80fd5b5061026a610265366004611233565b6105e2565b60405161014992919061124a565b348015610283575f80fd5b505f546001600160a01b03166101b9565b34801561029f575f80fd5b506101b97f00000000000000000000000067d12c4db022c543cb7a678f882edc935b89894081565b3480156102d2575f80fd5b506101b97f0000000000000000000000005af0d9827e0c53e4799bb226655a1de152a425a581565b348015610305575f80fd5b5061013f7f000000000000000000000000000000000000000000000000000000000000000081565b348015610338575f80fd5b506101b97f00000000000000000000000062460d1930f09f43d853c8a0dd96e0b95caccbef81565b34801561036b575f80fd5b506101b97f000000000000000000000000223d1aec02b2db27f8988807f5c56f2f421138a981565b34801561039e575f80fd5b506101286103ad3660046112b0565b610984565b3480156103bd575f80fd5b506101b97f00000000000000000000000071389b712311de08a92feaf99565be13fc901b0b81565b3480156103f0575f80fd5b506101b97f00000000000000000000000092030a3604b1883f54a2035571422ecc781d78b281565b61042b610426366004611233565b6109c6565b6040805163ffffffff9093168352602083019190915201610149565b61044f610c8e565b600155565b5f805f807f00000000000000000000000071389b712311de08a92feaf99565be13fc901b0b6001600160a01b031663a24eea1a7f00000000000000000000000000000000000000000000000000000000000000006040518263ffffffff1660e01b81526004016104c691815260200190565b60c060405180830381865afa1580156104e1573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061050591906112d2565b60405163132124f160e01b81527f000000000000000000000000000000000000000000000000000000000000000060048201529399509750507f00000000000000000000000092030a3604b1883f54a2035571422ecc781d78b26001600160a01b0316935063132124f19250506024015f60405180830381865afa15801561058f573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526105b69190810190611386565b50506001549a9b999a9499949850939650505050505050565b6105d7610c8e565b6105e05f610cba565b565b5f60605f7f0000000000000000000000005af0d9827e0c53e4799bb226655a1de152a425a56001600160a01b0316636352211e856040518263ffffffff1660e01b815260040161063491815260200190565b602060405180830381865afa15801561064f573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061067391906114a9565b90505f7f00000000000000000000000062460d1930f09f43d853c8a0dd96e0b95caccbef6001600160a01b0316633144d8096040518163ffffffff1660e01b8152600401602060405180830381865afa1580156106d2573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906106f691906114a9565b604051630333d49960e01b81526001600160a01b0384811660048301529190911690630333d499906024015f60405180830381865afa15801561073b573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f1916820160405261076291908101906114dc565b9050805167ffffffffffffffff81111561077e5761077e611341565b6040519080825280602002602001820160405280156107a7578160200160208202803683370190505b5092505f5b815181101561097c575f6107ff7f00000000000000000000000062460d1930f09f43d853c8a0dd96e0b95caccbef8484815181106107ec576107ec611584565b602002602001015163ffffffff16610d09565b6040516364ee130960e11b81527f000000000000000000000000000000000000000000000000000000000000000060048201529091505f906001600160a01b037f00000000000000000000000092030a3604b1883f54a2035571422ecc781d78b2169063c9dc261290602401602060405180830381865afa158015610886573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906108aa91906114a9565b6040516370a0823160e01b81526001600160a01b03848116600483015291909116906370a0823190602401602060405180830381865afa1580156108f0573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906109149190611598565b11156109695782828151811061092c5761092c611584565b602002602001015185878151811061094657610946611584565b63ffffffff9092166020928302919091019091015285610965816115c3565b9650505b5080610974816115c3565b9150506107ac565b505050915091565b61098c610c8e565b6001600160a01b0381166109ba57604051631e4fbdf760e01b81525f60048201526024015b60405180910390fd5b6109c381610cba565b50565b604051635127750d60e11b81527f000000000000000000000000000000000000000000000000000000000000000060048201525f90819081906001600160a01b037f00000000000000000000000071389b712311de08a92feaf99565be13fc901b0b169063a24eea1a9060240160c060405180830381865afa158015610a4e573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610a7291906112d2565b505060405163132124f160e01b81527f000000000000000000000000000000000000000000000000000000000000000060048201529194505f9350506001600160a01b037f00000000000000000000000092030a3604b1883f54a2035571422ecc781d78b216915063132124f1906024015f60405180830381865afa158015610afd573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052610b249190810190611386565b505050505050925050505f6001548284610b3e91906115db565b610b4891906115db565b9050803414610b995760405162461bcd60e51b815260206004820152601f60248201527f696e636f727265637420657468657220616d6f756e7420696e636c756465640060448201526064016109b1565b610ba286610dda565b6040516331a9108f60e11b8152600481018790525f907f0000000000000000000000005af0d9827e0c53e4799bb226655a1de152a425a56001600160a01b031690636352211e90602401602060405180830381865afa158015610c07573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c2b91906114a9565b9050610c38818585610f1e565b604051818152919750955063ffffffff87169088906001600160a01b038416907f9b209d7e6b5fcab64f06a2a7df290ffe14c3baaf0190ce220f1795854768a1989060200160405180910390a450505050915091565b5f546001600160a01b031633146105e05760405163118cdaa760e01b81523360048201526024016109b1565b5f80546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b604051632f4de29b60e11b81526001600160a01b037f0000000000000000000000004584dbf0510e86dcc2f36038c6473b1a0fc5aef3811660048301524660248301528381166044830152606482018390525f60848301819052917f00000000000000000000000067d12c4db022c543cb7a678f882edc935b89894090911690635e9bc5369060a401602060405180830381865afa158015610dad573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610dd191906114a9565b90505b92915050565b610e047f0000000000000000000000005af0d9827e0c53e4799bb226655a1de152a425a582611180565b50610e2f7f0000000000000000000000000ef38ae5b7ba0b8641cf34c2b9bac3694b92eeff82611180565b507f000000000000000000000000223d1aec02b2db27f8988807f5c56f2f421138a96001600160a01b031663be408c5c6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610e8c573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610eb091906114a9565b6001600160a01b03166108fc60015490811502906040515f60405180830381858888f19350505050158015610ee7573d5f803e3d5ffd5b506040518181527fee8cd96597e5ba5ec909c4940b70d6f3d1c4e5f728af0b94a6c9b8be4d47715f9060200160405180910390a150565b6040516308b3ac6360e41b81527f000000000000000000000000000000000000000000000000000000000000000060048201526001600160a01b0384811660248301525f9182917f00000000000000000000000071389b712311de08a92feaf99565be13fc901b0b1690638b3ac63090869060440160206040518083038185885af1158015610faf573d5f803e3d5ffd5b50505050506040513d601f19601f82011682018060405250810190610fd491906115ee565b91505f6110077f00000000000000000000000062460d1930f09f43d853c8a0dd96e0b95caccbef8463ffffffff16611180565b6040516364ee130960e11b81527f000000000000000000000000000000000000000000000000000000000000000060048201529091505f906001600160a01b037f00000000000000000000000092030a3604b1883f54a2035571422ecc781d78b2169063c9dc261290602401602060405180830381865afa15801561108e573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906110b291906114a9565b604051633e3799e960e01b81527f000000000000000000000000000000000000000000000000000000000000000060048201526001600160a01b0384811660248301529192507f00000000000000000000000092030a3604b1883f54a2035571422ecc781d78b290911690633e3799e990879060440160206040518083038185885af1158015611144573d5f803e3d5ffd5b50505050506040513d601f19601f820116820180604052508101906111699190611598565b92506111758184611180565b505050935093915050565b60405163da7323b360e01b81526001600160a01b037f0000000000000000000000004584dbf0510e86dcc2f36038c6473b1a0fc5aef3811660048301524660248301528381166044830152606482018390525f6084830181905260c060a484015260c48301819052917f00000000000000000000000067d12c4db022c543cb7a678f882edc935b8989409091169063da7323b39060e4016020604051808303815f875af1158015610dad573d5f803e3d5ffd5b5f60208284031215611243575f80fd5b5035919050565b5f60408201848352602060408185015281855180845260608601915082870193505f5b8181101561128f57845163ffffffff168352938301939183019160010161126d565b5090979650505050505050565b6001600160a01b03811681146109c3575f80fd5b5f602082840312156112c0575f80fd5b81356112cb8161129c565b9392505050565b5f805f805f8060c087890312156112e7575f80fd5b86516112f28161129c565b60208801519096506113038161129c565b60408801516060890151919650945061131b8161129c565b608088015190935061132c8161129c565b8092505060a087015190509295509295509295565b634e487b7160e01b5f52604160045260245ffd5b604051601f8201601f1916810167ffffffffffffffff8111828210171561137e5761137e611341565b604052919050565b5f805f805f805f805f6101208a8c03121561139f575f80fd5b89516113aa8161129c565b809950506020808b01516113bd8161129c565b8099505060408b0151975060608b0151965060808b01516113dd8161129c565b60a08c01519096506113ee8161129c565b60c08c01519095506113ff8161129c565b60e08c015190945067ffffffffffffffff8082111561141c575f80fd5b818d0191508d601f83011261142f575f80fd5b81518181111561144157611441611341565b611453601f8201601f19168501611355565b91508082528e84828501011115611468575f80fd5b5f5b8181101561148557838101850151838201860152840161146a565b505f848284010152508094505050506101008a015190509295985092959850929598565b5f602082840312156114b9575f80fd5b81516112cb8161129c565b805163ffffffff811681146114d7575f80fd5b919050565b5f60208083850312156114ed575f80fd5b825167ffffffffffffffff80821115611504575f80fd5b818501915085601f830112611517575f80fd5b81518181111561152957611529611341565b8060051b915061153a848301611355565b8181529183018401918481019088841115611553575f80fd5b938501935b8385101561157857611569856114c4565b82529385019390850190611558565b98975050505050505050565b634e487b7160e01b5f52603260045260245ffd5b5f602082840312156115a8575f80fd5b5051919050565b634e487b7160e01b5f52601160045260245ffd5b5f600182016115d4576115d46115af565b5060010190565b80820180821115610dd457610dd46115af565b5f602082840312156115fe575f80fd5b610dd1826114c456fea2646970667358221220e7b9489d8af7f8da43e3563f691d3f4066c3fd4da1ab01cd59930e25e15849fb64736f6c63430008140033

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

0000000000000000000000005af0d9827e0c53e4799bb226655a1de152a425a50000000000000000000000000ef38ae5b7ba0b8641cf34c2b9bac3694b92eeff00000000000000000000000062460d1930f09f43d853c8a0dd96e0b95caccbef000000000000000000000000223d1aec02b2db27f8988807f5c56f2f421138a900000000000000000000000067d12c4db022c543cb7a678f882edc935b8989400000000000000000000000004584dbf0510e86dcc2f36038c6473b1a0fc5aef300000000000000000000000071389b712311de08a92feaf99565be13fc901b0b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000092030a3604b1883f54a2035571422ecc781d78b200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000058d15e17628000

-----Decoded View---------------
Arg [0] : _miladysContract (address): 0x5Af0D9827E0c53E4799BB226655A1de152A425a5
Arg [1] : _avatarContract (address): 0x0Ef38aE5B7Ba0B8641cf34C2B9bAC3694B92EeFF
Arg [2] : _azimuthOwnerWrapperContract (address): 0x62460d1930F09F43D853c8A0dd96E0B95cAcCbeF
Arg [3] : _soulboundAccessoriesContract (address): 0x223d1aec02B2DB27f8988807F5C56f2f421138A9
Arg [4] : _tgaRegistry (address): 0x67d12C4dB022c543cb7a678F882eDc935B898940
Arg [5] : _tgaAccountImpl (address): 0x4584DbF0510E86Dcc2F36038C6473b1a0FC5Aef3
Arg [6] : _shipStoreContract (address): 0x71389b712311De08a92feaf99565be13Fc901b0B
Arg [7] : _shipStackId (uint256): 0
Arg [8] : _pillStoreContract (address): 0x92030a3604B1883f54A2035571422eCC781d78B2
Arg [9] : _mosPillSetId (uint256): 0
Arg [10] : _onboardingGasForwardAmount (uint256): 25000000000000000

-----Encoded View---------------
11 Constructor Arguments found :
Arg [0] : 0000000000000000000000005af0d9827e0c53e4799bb226655a1de152a425a5
Arg [1] : 0000000000000000000000000ef38ae5b7ba0b8641cf34c2b9bac3694b92eeff
Arg [2] : 00000000000000000000000062460d1930f09f43d853c8a0dd96e0b95caccbef
Arg [3] : 000000000000000000000000223d1aec02b2db27f8988807f5c56f2f421138a9
Arg [4] : 00000000000000000000000067d12c4db022c543cb7a678f882edc935b898940
Arg [5] : 0000000000000000000000004584dbf0510e86dcc2f36038c6473b1a0fc5aef3
Arg [6] : 00000000000000000000000071389b712311de08a92feaf99565be13fc901b0b
Arg [7] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [8] : 00000000000000000000000092030a3604b1883f54a2035571422ecc781d78b2
Arg [9] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [10] : 0000000000000000000000000000000000000000000000000058d15e17628000


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
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.