Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
Latest 25 from a total of 47 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Onboard And Purc... | 18749004 | 380 days ago | IN | 0.105 ETH | 0.02735312 | ||||
Onboard And Purc... | 18722376 | 384 days ago | IN | 0.105 ETH | 0.04847728 | ||||
Onboard And Purc... | 18691784 | 388 days ago | IN | 0.105 ETH | 0.03357663 | ||||
Onboard And Purc... | 18665074 | 392 days ago | IN | 0.105 ETH | 0.04350934 | ||||
Onboard And Purc... | 18596764 | 402 days ago | IN | 0.105 ETH | 0.01416294 | ||||
Onboard And Purc... | 18550895 | 408 days ago | IN | 0.105 ETH | 0.02082285 | ||||
Onboard And Purc... | 18546918 | 409 days ago | IN | 0.105 ETH | 0.01721874 | ||||
Onboard And Purc... | 18544682 | 409 days ago | IN | 0.105 ETH | 0.0268876 | ||||
Onboard And Purc... | 18527957 | 411 days ago | IN | 0.105 ETH | 0.03118534 | ||||
Onboard And Purc... | 18507753 | 414 days ago | IN | 0.105 ETH | 0.02469691 | ||||
Onboard And Purc... | 18505851 | 414 days ago | IN | 0.105 ETH | 0.01869575 | ||||
Onboard And Purc... | 18493444 | 416 days ago | IN | 0.105 ETH | 0.01630393 | ||||
Onboard And Purc... | 18481055 | 418 days ago | IN | 0.105 ETH | 0.0204201 | ||||
Onboard And Purc... | 18480325 | 418 days ago | IN | 0.105 ETH | 0.02823739 | ||||
Onboard And Purc... | 18476963 | 418 days ago | IN | 0.105 ETH | 0.01081736 | ||||
Onboard And Purc... | 18466144 | 420 days ago | IN | 0.105 ETH | 0.02017084 | ||||
Onboard And Purc... | 18465419 | 420 days ago | IN | 0.105 ETH | 0.02327196 | ||||
Onboard And Purc... | 18462856 | 420 days ago | IN | 0.105 ETH | 0.01177043 | ||||
Onboard And Purc... | 18458073 | 421 days ago | IN | 0.105 ETH | 0.01092516 | ||||
Onboard And Purc... | 18457933 | 421 days ago | IN | 0.105 ETH | 0.01004837 | ||||
Onboard And Purc... | 18457753 | 421 days ago | IN | 0.105 ETH | 0.01072395 | ||||
Onboard And Purc... | 18455203 | 421 days ago | IN | 0.105 ETH | 0.00810657 | ||||
Onboard And Purc... | 18452789 | 422 days ago | IN | 0.105 ETH | 0.00765843 | ||||
Onboard And Purc... | 18452245 | 422 days ago | IN | 0.105 ETH | 0.00859019 | ||||
Onboard And Purc... | 18451735 | 422 days ago | IN | 0.105 ETH | 0.00824995 |
Latest 25 internal transactions (View All)
Advanced mode:
Parent Transaction Hash | Block |
From
|
To
|
|||
---|---|---|---|---|---|---|
18749004 | 380 days ago | 0.045 ETH | ||||
18749004 | 380 days ago | 0.035 ETH | ||||
18749004 | 380 days ago | 0.025 ETH | ||||
18722376 | 384 days ago | 0.045 ETH | ||||
18722376 | 384 days ago | 0.035 ETH | ||||
18722376 | 384 days ago | 0.025 ETH | ||||
18691784 | 388 days ago | 0.045 ETH | ||||
18691784 | 388 days ago | 0.035 ETH | ||||
18691784 | 388 days ago | 0.025 ETH | ||||
18665074 | 392 days ago | 0.045 ETH | ||||
18665074 | 392 days ago | 0.035 ETH | ||||
18665074 | 392 days ago | 0.025 ETH | ||||
18596764 | 402 days ago | 0.045 ETH | ||||
18596764 | 402 days ago | 0.035 ETH | ||||
18596764 | 402 days ago | 0.025 ETH | ||||
18550895 | 408 days ago | 0.045 ETH | ||||
18550895 | 408 days ago | 0.035 ETH | ||||
18550895 | 408 days ago | 0.025 ETH | ||||
18546918 | 409 days ago | 0.045 ETH | ||||
18546918 | 409 days ago | 0.035 ETH | ||||
18546918 | 409 days ago | 0.025 ETH | ||||
18544682 | 409 days ago | 0.045 ETH | ||||
18544682 | 409 days ago | 0.035 ETH | ||||
18544682 | 409 days ago | 0.025 ETH | ||||
18527957 | 411 days ago | 0.045 ETH |
Loading...
Loading
Contract Name:
MOSOnboarding
Compiler Version
v0.8.20+commit.a1b79de6
Optimization Enabled:
Yes with 200 runs
Other Settings:
shanghai EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
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 )); } }
// 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); }
// 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); } }
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) ); } }
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; } }
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); }
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); } }
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; } }
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); } }
// 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); }
// 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; } }
// 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) } } }
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); }
// 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); }
// 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); }
// 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); }
// 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)); } }
// 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) } } } }
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); }
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); }
// 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)) } } } } } }
// 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); } } }
// 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); }
// 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)); } }
// 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; } }
// 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); }
// 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; } }
// 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); } } }
{ "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
- No Contract Security Audit Submitted- Submit Audit Here
[{"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"}]
Contract Creation Code
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
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.