账户
0x3e...c150
0x3E...c150

0x3E...c150

$500
此合同的源代码已经过验证!
合同元数据
编译器
0.8.25+commit.b61c2a91
语言
Solidity
合同源代码
文件 1 的 10:CreatorFixedPriceSaleStrategy.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

import { IMinter1155 } from "../../interfaces/IMinter1155.sol";
import { ICreatorCommands } from "../../interfaces/ICreatorCommands.sol";
import { SaleStrategy } from "../SaleStrategy.sol";
import { SaleCommandHelper } from "../utils/SaleCommandHelper.sol";
import { LimitedMintPerAddress } from "../utils/LimitedMintPerAddress.sol";

/// @title CreatorFixedPriceSaleStrategy
/// @notice A sale strategy for Creator that allows for fixed price sales over a given time period
contract CreatorFixedPriceSaleStrategy is SaleStrategy, LimitedMintPerAddress {
    struct SalesConfig {
        /// @notice Unix timestamp for the sale start
        uint64 saleStart;
        /// @notice Unix timestamp for the sale end
        uint64 saleEnd;
        /// @notice Max tokens that can be minted for an address, 0 if unlimited
        uint64 maxTokensPerAddress;
        /// @notice Price per token in eth wei
        uint96 pricePerToken;
        /// @notice Funds recipient (0 if no different funds recipient than the contract global)
        address fundsRecipient;
    }

    // target -> tokenId -> settings
    mapping(address => mapping(uint256 => SalesConfig)) internal salesConfigs;

    using SaleCommandHelper for ICreatorCommands.CommandSet;

    /// @notice The name of the sale strategy
    function contractName() external pure override returns (string memory) {
        return "Fixed Price Sale Strategy";
    }

    /// @notice The version of the sale strategy
    function contractVersion() external pure override returns (string memory) {
        return "1";
    }

    error WrongValueSent();
    error SaleEnded();
    error SaleHasNotStarted();

    event SaleSet(address indexed mediaContract, uint256 indexed tokenId, SalesConfig salesConfig);
    event MintComment(
        address indexed sender, address indexed tokenContract, uint256 indexed tokenId, uint256 quantity, string comment
    );

    /// @notice Compiles and returns the commands needed to mint a token using this sales strategy
    /// @param tokenId The token ID to mint
    /// @param quantity The quantity of tokens to mint
    /// @param ethValueSent The amount of ETH sent with the transaction
    /// @param minterArguments The arguments passed to the minter, which should be the address to mint to
    function requestMint(
        address,
        uint256 tokenId,
        uint256 quantity,
        uint256 ethValueSent,
        bytes calldata minterArguments
    )
        external
        returns (ICreatorCommands.CommandSet memory commands)
    {
        address mintTo;
        string memory comment = "";
        if (minterArguments.length == 32) {
            mintTo = abi.decode(minterArguments, (address));
        } else {
            (mintTo, comment) = abi.decode(minterArguments, (address, string));
        }

        SalesConfig storage config = salesConfigs[msg.sender][tokenId];

        // If sales config does not exist this first check will always fail.

        // Check sale end
        if (block.timestamp > config.saleEnd) {
            revert SaleEnded();
        }

        // Check sale start
        if (block.timestamp < config.saleStart) {
            revert SaleHasNotStarted();
        }

        // Check value sent
        if (config.pricePerToken * quantity != ethValueSent) {
            revert WrongValueSent();
        }

        // Check minted per address limit
        if (config.maxTokensPerAddress > 0) {
            _requireMintNotOverLimitAndUpdate(config.maxTokensPerAddress, quantity, msg.sender, tokenId, mintTo);
        }

        bool shouldTransferFunds = config.fundsRecipient != address(0);
        commands.setSize(shouldTransferFunds ? 2 : 1);

        // Mint command
        commands.mint(mintTo, tokenId, quantity);

        if (bytes(comment).length > 0) {
            emit MintComment(mintTo, msg.sender, tokenId, quantity, comment);
        }

        // Should transfer funds if funds recipient is set to a non-default address
        if (shouldTransferFunds) {
            commands.transfer(config.fundsRecipient, ethValueSent);
        }
    }

    /// @notice Sets the sale config for a given token
    function setSale(uint256 tokenId, SalesConfig memory salesConfig) external {
        salesConfigs[msg.sender][tokenId] = salesConfig;

        // Emit event
        emit SaleSet(msg.sender, tokenId, salesConfig);
    }

    /// @notice Deletes the sale config for a given token
    function resetSale(uint256 tokenId) external override {
        delete salesConfigs[msg.sender][tokenId];

        // Deleted sale emit event
        emit SaleSet(msg.sender, tokenId, salesConfigs[msg.sender][tokenId]);
    }

    /// @notice Returns the sale config for a given token
    function sale(address tokenContract, uint256 tokenId) external view returns (SalesConfig memory) {
        return salesConfigs[tokenContract][tokenId];
    }

    function supportsInterface(bytes4 interfaceId)
        public
        pure
        virtual
        override(LimitedMintPerAddress, SaleStrategy)
        returns (bool)
    {
        return super.supportsInterface(interfaceId) || LimitedMintPerAddress.supportsInterface(interfaceId)
            || SaleStrategy.supportsInterface(interfaceId);
    }
}
合同源代码
文件 2 的 10:IContractMetadata.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

interface IContractMetadata {
    /// @notice Contract name returns the pretty contract name
    function contractName() external returns (string memory);
}
合同源代码
文件 3 的 10:ICreatorCommands.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

/// @notice Creator Commands used by minter modules passed back to the main modules
interface ICreatorCommands {
    /// @notice This enum is used to define supported creator action types.
    /// This can change in the future
    enum CreatorActions
    // No operation - also the default for mintings that may not return a command
    {
        NO_OP,
        // Send ether
        SEND_ETH,
        // Mint operation
        MINT
    }

    /// @notice This command is for
    struct Command {
        // Method for operation
        CreatorActions method;
        // Arguments used for this operation
        bytes args;
    }

    /// @notice This command set is returned from the minter back to the user
    struct CommandSet {
        Command[] commands;
        uint256 at;
    }
}
合同源代码
文件 4 的 10:IERC165Upgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (interfaces/IERC165.sol)

pragma solidity ^0.8.0;

import "../utils/introspection/IERC165Upgradeable.sol";
合同源代码
文件 5 的 10:ILimitedMintPerAddress.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

import { IERC165Upgradeable } from "@openzeppelin/contracts-upgradeable/interfaces/IERC165Upgradeable.sol";

interface ILimitedMintPerAddress is IERC165Upgradeable {
    error UserExceedsMintLimit(address user, uint256 limit, uint256 requestedAmount);

    function getMintedPerWallet(address token, uint256 tokenId, address wallet) external view returns (uint256);
}
合同源代码
文件 6 的 10:IMinter1155.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

import { IERC165Upgradeable } from "@openzeppelin/contracts-upgradeable/interfaces/IERC165Upgradeable.sol";
import { ICreatorCommands } from "./ICreatorCommands.sol";

/// @notice Minter standard interface
/// @dev Minters need to confirm to the ERC165 selector of type(IMinter1155).interfaceId
interface IMinter1155 is IERC165Upgradeable {
    function requestMint(
        address sender,
        uint256 tokenId,
        uint256 quantity,
        uint256 ethValueSent,
        bytes calldata minterArguments
    )
        external
        returns (ICreatorCommands.CommandSet memory commands);
}
合同源代码
文件 7 的 10:IVersionedContract.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

interface IVersionedContract {
    function contractVersion() external returns (string memory);
}
合同源代码
文件 8 的 10:LimitedMintPerAddress.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

import { ILimitedMintPerAddress } from "../../interfaces/ILimitedMintPerAddress.sol";

contract LimitedMintPerAddress is ILimitedMintPerAddress {
    /// @notice Storage for slot to check user mints
    /// @notice target contract -> tokenId -> minter user -> numberMinted
    /// @dev No gap or stroage interface since this is used within non-upgradeable contracts
    mapping(address => mapping(uint256 => mapping(address => uint256))) internal mintedPerAddress;

    function getMintedPerWallet(
        address tokenContract,
        uint256 tokenId,
        address wallet
    )
        external
        view
        returns (uint256)
    {
        return mintedPerAddress[tokenContract][tokenId][wallet];
    }

    function _requireMintNotOverLimitAndUpdate(
        uint256 limit,
        uint256 numRequestedMint,
        address tokenContract,
        uint256 tokenId,
        address wallet
    )
        internal
    {
        mintedPerAddress[tokenContract][tokenId][wallet] += numRequestedMint;
        if (mintedPerAddress[tokenContract][tokenId][wallet] > limit) {
            revert UserExceedsMintLimit(wallet, limit, mintedPerAddress[tokenContract][tokenId][wallet]);
        }
    }

    function supportsInterface(bytes4 interfaceId) public pure virtual override returns (bool) {
        return interfaceId == type(ILimitedMintPerAddress).interfaceId;
    }
}
合同源代码
文件 9 的 10:SaleCommandHelper.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

import { ICreatorCommands } from "../../interfaces/ICreatorCommands.sol";

/// @title SaleCommandHelper
/// @notice Helper library for creating commands for the sale contract
library SaleCommandHelper {
    /// @notice Sets the size of commands and initializes command array. Empty entries are skipped by the resolver.
    /// @dev Beware: this removes all previous command entries from memory
    /// @param commandSet command set struct storage.
    /// @param size size to set for the new struct
    function setSize(ICreatorCommands.CommandSet memory commandSet, uint256 size) internal pure {
        commandSet.commands = new ICreatorCommands.Command[](size);
    }

    /// @notice Creates a command to mint a token
    /// @param commandSet The command set to add the command to
    /// @param to The address to mint to
    /// @param tokenId The token ID to mint
    /// @param quantity The quantity of tokens to mint
    function mint(
        ICreatorCommands.CommandSet memory commandSet,
        address to,
        uint256 tokenId,
        uint256 quantity
    )
        internal
        pure
    {
        unchecked {
            commandSet.commands[commandSet.at++] = ICreatorCommands.Command({
                method: ICreatorCommands.CreatorActions.MINT,
                args: abi.encode(to, tokenId, quantity)
            });
        }
    }

    /// @notice Creates a command to transfer ETH
    /// @param commandSet The command set to add the command to
    /// @param to The address to transfer to
    /// @param amount The amount of ETH to transfer
    function transfer(ICreatorCommands.CommandSet memory commandSet, address to, uint256 amount) internal pure {
        unchecked {
            commandSet.commands[commandSet.at++] = ICreatorCommands.Command({
                method: ICreatorCommands.CreatorActions.SEND_ETH,
                args: abi.encode(to, amount)
            });
        }
    }
}
合同源代码
文件 10 的 10:SaleStrategy.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

import { IERC165Upgradeable } from "@openzeppelin/contracts-upgradeable/interfaces/IERC165Upgradeable.sol";
import { IMinter1155 } from "../interfaces/IMinter1155.sol";
import { IContractMetadata } from "../interfaces/IContractMetadata.sol";
import { IVersionedContract } from "../interfaces/IVersionedContract.sol";

/// @notice Sales Strategy Helper contract template on top of IMinter1155
abstract contract SaleStrategy is IMinter1155, IVersionedContract, IContractMetadata {
    /// @notice This function resets the sales configuration for a given tokenId and contract.
    /// @dev This function is intentioned to be called directly from the affected sales contract
    function resetSale(uint256 tokenId) external virtual;

    function supportsInterface(bytes4 interfaceId) public pure virtual returns (bool) {
        return interfaceId == type(IMinter1155).interfaceId || interfaceId == type(IERC165Upgradeable).interfaceId;
    }
}
设置
{
  "compilationTarget": {
    "src/minters/fixed-price/CreatorFixedPriceSaleStrategy.sol": "CreatorFixedPriceSaleStrategy"
  },
  "evmVersion": "paris",
  "libraries": {},
  "metadata": {
    "bytecodeHash": "ipfs"
  },
  "optimizer": {
    "enabled": true,
    "runs": 500
  },
  "remappings": [
    ":@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
    ":@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
    ":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-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
    ":openzeppelin-contracts/=lib/openzeppelin-contracts/",
    ":openzeppelin/=lib/openzeppelin-contracts/contracts/"
  ],
  "viaIR": true
}
ABI
[{"inputs":[],"name":"SaleEnded","type":"error"},{"inputs":[],"name":"SaleHasNotStarted","type":"error"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"limit","type":"uint256"},{"internalType":"uint256","name":"requestedAmount","type":"uint256"}],"name":"UserExceedsMintLimit","type":"error"},{"inputs":[],"name":"WrongValueSent","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"tokenContract","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"quantity","type":"uint256"},{"indexed":false,"internalType":"string","name":"comment","type":"string"}],"name":"MintComment","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"mediaContract","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"components":[{"internalType":"uint64","name":"saleStart","type":"uint64"},{"internalType":"uint64","name":"saleEnd","type":"uint64"},{"internalType":"uint64","name":"maxTokensPerAddress","type":"uint64"},{"internalType":"uint96","name":"pricePerToken","type":"uint96"},{"internalType":"address","name":"fundsRecipient","type":"address"}],"indexed":false,"internalType":"struct CreatorFixedPriceSaleStrategy.SalesConfig","name":"salesConfig","type":"tuple"}],"name":"SaleSet","type":"event"},{"inputs":[],"name":"contractName","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"contractVersion","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"tokenContract","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"address","name":"wallet","type":"address"}],"name":"getMintedPerWallet","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"quantity","type":"uint256"},{"internalType":"uint256","name":"ethValueSent","type":"uint256"},{"internalType":"bytes","name":"minterArguments","type":"bytes"}],"name":"requestMint","outputs":[{"components":[{"components":[{"internalType":"enum ICreatorCommands.CreatorActions","name":"method","type":"uint8"},{"internalType":"bytes","name":"args","type":"bytes"}],"internalType":"struct ICreatorCommands.Command[]","name":"commands","type":"tuple[]"},{"internalType":"uint256","name":"at","type":"uint256"}],"internalType":"struct ICreatorCommands.CommandSet","name":"commands","type":"tuple"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"resetSale","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenContract","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"sale","outputs":[{"components":[{"internalType":"uint64","name":"saleStart","type":"uint64"},{"internalType":"uint64","name":"saleEnd","type":"uint64"},{"internalType":"uint64","name":"maxTokensPerAddress","type":"uint64"},{"internalType":"uint96","name":"pricePerToken","type":"uint96"},{"internalType":"address","name":"fundsRecipient","type":"address"}],"internalType":"struct CreatorFixedPriceSaleStrategy.SalesConfig","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"components":[{"internalType":"uint64","name":"saleStart","type":"uint64"},{"internalType":"uint64","name":"saleEnd","type":"uint64"},{"internalType":"uint64","name":"maxTokensPerAddress","type":"uint64"},{"internalType":"uint96","name":"pricePerToken","type":"uint96"},{"internalType":"address","name":"fundsRecipient","type":"address"}],"internalType":"struct CreatorFixedPriceSaleStrategy.SalesConfig","name":"salesConfig","type":"tuple"}],"name":"setSale","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"}]