Cryo Explorer Ethereum Mainnet

Address Contract Verified

Address 0x40a9E3fc8c8a3779AdD6f06Cb73E5c42134b0C02
Balance 0 ETH
Nonce 2
Code Size 2776 bytes
Indexed Transactions 0
External Etherscan · Sourcify

Contract Bytecode

2776 bytes
0x6080604052600436106100a75760003560e01c80637804a5dc116100645780637804a5dc146101db57806382bc73dc1461021b5780638da5cb5b1461023b578063d782d64714610259578063d897ba9614610279578063f2fde38b1461029957600080fd5b80631672b0a6146100ac57806327e45c2c146100ce578063512ea8401461011c5780635b76c04f14610147578063715018a6146101a657806371e6977e146101bb575b600080fd5b3480156100b857600080fd5b506100cc6100c7366004610770565b6102b9565b005b3480156100da57600080fd5b506101026100e9366004610770565b6003602052600090815260409020805460019091015482565b604080519283526020830191909152015b60405180910390f35b61012f61012a366004610792565b6102e3565b6040516001600160a01b039091168152602001610113565b34801561015357600080fd5b5061018761016236600461080e565b600260205260009081526040902080546001909101546001600160a01b039091169082565b604080516001600160a01b039093168352602083019190915201610113565b3480156101b257600080fd5b506100cc6104d1565b3480156101c757600080fd5b5060015461012f906001600160a01b031681565b3480156101e757600080fd5b5061020b6101f6366004610770565b60056020526000908152604090205460ff1681565b6040519015158152602001610113565b34801561022757600080fd5b506100cc610236366004610908565b6104e5565b34801561024757600080fd5b506000546001600160a01b031661012f565b34801561026557600080fd5b5061012f61027436600461080e565b6105ca565b34801561028557600080fd5b506100cc6102943660046109c1565b6105f4565b3480156102a557600080fd5b506100cc6102b4366004610770565b610627565b6102c161066a565b600180546001600160a01b0319166001600160a01b0392909216919091179055565b3360009081526005602052604081205460ff166103135760405163c19f17a960e01b815260040160405180910390fd5b336000908152600560209081526040808320805460ff191690558683526002909152908190206001549151631df7cc5d60e31b815290916001600160a01b03169063efbe62e89061036c90349088908890600401610a26565b600060405180830381600087803b15801561038657600080fd5b505af115801561039a573d6000803e3d6000fd5b505082546103b392506001600160a01b03169050610697565b60048054600180820183557f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b90910180546001600160a01b0319166001600160a01b0385169081179091556040805180820182528a8152868401546020808301918252600085815260039091528390209151825551930192909255905163439fab9160e01b81529294509163439fab91913491610454918991899101610a49565b6000604051808303818588803b15801561046d57600080fd5b505af1158015610481573d6000803e3d6000fd5b50506040516001600160a01b03861681527f2e2b3f61b70d2d131b2a807371103cc98d51adcaa5e9a8f9c32658ad8426e74e935060200191506104c19050565b60405180910390a1509392505050565b6104d961066a565b6104e36000610709565b565b6104ed61066a565b80518251146104fb57600080fd5b805160005b818110156105c45760006002600086848151811061052057610520610a65565b60200260200101518152602001908152602001600020905083828151811061054a5761054a610a65565b602090810291909101015181546001600160a01b039081169116146105bb5783828151811061057b5761057b610a65565b602090810291909101015181546001600160a01b0319166001600160a01b03909116178155600180820180546000906105b5908490610a7b565b90915550505b50600101610500565b50505050565b600481815481106105da57600080fd5b6000918252602090912001546001600160a01b0316905081565b6105fc61066a565b6001600160a01b03919091166000908152600560205260409020805460ff1916911515919091179055565b61062f61066a565b6001600160a01b03811661065e57604051631e4fbdf760e01b8152600060048201526024015b60405180910390fd5b61066781610709565b50565b6000546001600160a01b031633146104e35760405163118cdaa760e01b8152336004820152602401610655565b6000763d602d80600a3d3981f3363d3d373d3d3d363d730000008260601b60e81c176000526e5af43d82803e903d91602b57fd5bf38260781b17602052603760096000f090506001600160a01b038116610704576040516330be1a3d60e21b815260040160405180910390fd5b919050565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b80356001600160a01b038116811461070457600080fd5b60006020828403121561078257600080fd5b61078b82610759565b9392505050565b6000806000604084860312156107a757600080fd5b83359250602084013567ffffffffffffffff808211156107c657600080fd5b818601915086601f8301126107da57600080fd5b8135818111156107e957600080fd5b8760208285010111156107fb57600080fd5b6020830194508093505050509250925092565b60006020828403121561082057600080fd5b5035919050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff8111828210171561086657610866610827565b604052919050565b600067ffffffffffffffff82111561088857610888610827565b5060051b60200190565b600082601f8301126108a357600080fd5b813560206108b86108b38361086e565b61083d565b8083825260208201915060208460051b8701019350868411156108da57600080fd5b602086015b848110156108fd576108f081610759565b83529183019183016108df565b509695505050505050565b6000806040838503121561091b57600080fd5b823567ffffffffffffffff8082111561093357600080fd5b818501915085601f83011261094757600080fd5b813560206109576108b38361086e565b82815260059290921b8401810191818101908984111561097657600080fd5b948201945b838610156109945785358252948201949082019061097b565b965050860135925050808211156109aa57600080fd5b506109b785828601610892565b9150509250929050565b600080604083850312156109d457600080fd5b6109dd83610759565b9150602083013580151581146109f257600080fd5b809150509250929050565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b838152604060208201526000610a406040830184866109fd565b95945050505050565b602081526000610a5d6020830184866109fd565b949350505050565b634e487b7160e01b600052603260045260246000fd5b80820180821115610a9c57634e487b7160e01b600052601160045260246000fd5b9291505056fea26469706673582212209b4d2009522df3957be46f6577ac35f8f30c5b92ea664f635ea8d4e769c8c30164736f6c63430008180033

Verified Source Code Full Match

Compiler: v0.8.24+commit.e11b9ed9 EVM: paris Optimization: Yes (200 runs)
Factory.sol 82 lines
// SPDX-License-Identifier: None
pragma solidity ^0.8.24;

import "@openzeppelin/contracts/proxy/Clones.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

interface IToken {
    function initialize(bytes calldata params) external payable;
}

interface IValidationLogic {
    function validate(uint256 ethAmt, bytes calldata params) external;
}

contract Factory is Ownable {

    struct ImplementationData {
        address addr;
        uint256 version;
    }

    struct UserToken {
        uint256 tokenType;
        uint256 version;
    }


    IValidationLogic public validationLogic;
    mapping(uint256 => ImplementationData) public tokenImplementations;
    mapping(address => UserToken) public tokenData;
    address[] public createdTokens;
    mapping(address => bool) public canCreate;

    event TokenCreated(address _address);

    error NotApproved();

    constructor(address _validationLogic, address[] memory implementations) Ownable(msg.sender) {
        validationLogic = IValidationLogic(_validationLogic);
        uint256 len = implementations.length;
        for (uint256 i; i < len; i++) {
            tokenImplementations[i] = ImplementationData({addr: implementations[i], version: 1});
        }
    }

    function createToken(uint256 tokenID, bytes calldata params) external payable returns (address newToken) {
        if (!canCreate[msg.sender]) {
            revert NotApproved();
        }
        canCreate[msg.sender] = false;
        ImplementationData storage tokenInfo = tokenImplementations[tokenID];
        validationLogic.validate(msg.value, params);
        newToken = Clones.clone(tokenInfo.addr);
        createdTokens.push(newToken);
        tokenData[newToken] = UserToken({tokenType: tokenID, version: tokenInfo.version});
        IToken(newToken).initialize{value: msg.value}(params);
        emit TokenCreated(newToken);
    }

    function approveCreator(address creator, bool approved) external onlyOwner {
        canCreate[creator] = approved;
    }

    function setImplementations(uint256[] memory ids, address[] memory implementations) external onlyOwner {
        if (ids.length != implementations.length) {
            revert();
        }
        uint256 len = implementations.length;
        for (uint256 i; i < len; i++) {
            ImplementationData storage currImplementation = tokenImplementations[ids[i]];
            if (currImplementation.addr != implementations[i]) {
                currImplementation.addr = implementations[i];
                currImplementation.version += 1;
            }
        }
    }

    function setValidationLogic(address vLogic) external onlyOwner {
        validationLogic = IValidationLogic(vLogic);
    }

}
Clones.sol 95 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/Clones.sol)

pragma solidity ^0.8.20;

/**
 * @dev https://eips.ethereum.org/EIPS/eip-1167[EIP 1167] is a standard for
 * deploying minimal proxy contracts, also known as "clones".
 *
 * > To simply and cheaply clone contract functionality in an immutable way, this standard specifies
 * > a minimal bytecode implementation that delegates all calls to a known, fixed address.
 *
 * The library includes functions to deploy a proxy using either `create` (traditional deployment) or `create2`
 * (salted deterministic deployment). It also includes functions to predict the addresses of clones deployed using the
 * deterministic method.
 */
library Clones {
    /**
     * @dev A clone instance deployment failed.
     */
    error ERC1167FailedCreateClone();

    /**
     * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
     *
     * This function uses the create opcode, which should never revert.
     */
    function clone(address implementation) internal returns (address instance) {
        /// @solidity memory-safe-assembly
        assembly {
            // Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes
            // of the `implementation` address with the bytecode before the address.
            mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))
            // Packs the remaining 17 bytes of `implementation` with the bytecode after the address.
            mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3))
            instance := create(0, 0x09, 0x37)
        }
        if (instance == address(0)) {
            revert ERC1167FailedCreateClone();
        }
    }

    /**
     * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
     *
     * This function uses the create2 opcode and a `salt` to deterministically deploy
     * the clone. Using the same `implementation` and `salt` multiple time will revert, since
     * the clones cannot be deployed twice at the same address.
     */
    function cloneDeterministic(address implementation, bytes32 salt) internal returns (address instance) {
        /// @solidity memory-safe-assembly
        assembly {
            // Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes
            // of the `implementation` address with the bytecode before the address.
            mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))
            // Packs the remaining 17 bytes of `implementation` with the bytecode after the address.
            mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3))
            instance := create2(0, 0x09, 0x37, salt)
        }
        if (instance == address(0)) {
            revert ERC1167FailedCreateClone();
        }
    }

    /**
     * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
     */
    function predictDeterministicAddress(
        address implementation,
        bytes32 salt,
        address deployer
    ) internal pure returns (address predicted) {
        /// @solidity memory-safe-assembly
        assembly {
            let ptr := mload(0x40)
            mstore(add(ptr, 0x38), deployer)
            mstore(add(ptr, 0x24), 0x5af43d82803e903d91602b57fd5bf3ff)
            mstore(add(ptr, 0x14), implementation)
            mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73)
            mstore(add(ptr, 0x58), salt)
            mstore(add(ptr, 0x78), keccak256(add(ptr, 0x0c), 0x37))
            predicted := keccak256(add(ptr, 0x43), 0x55)
        }
    }

    /**
     * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
     */
    function predictDeterministicAddress(
        address implementation,
        bytes32 salt
    ) internal view returns (address predicted) {
        return predictDeterministicAddress(implementation, salt, address(this));
    }
}
Context.sol 28 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (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;
    }

    function _contextSuffixLength() internal view virtual returns (uint256) {
        return 0;
    }
}
Ownable.sol 100 lines
// 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);
    }
}

Read Contract

canCreate 0x7804a5dc → bool
createdTokens 0xd782d647 → address
owner 0x8da5cb5b → address
tokenData 0x27e45c2c → uint256, uint256
tokenImplementations 0x5b76c04f → address, uint256
validationLogic 0x71e6977e → address

Write Contract 6 functions

These functions modify contract state and require a wallet transaction to execute.

approveCreator 0xd897ba96
address creator
bool approved
createToken 0x512ea840
uint256 tokenID
bytes params
returns: address
renounceOwnership 0x715018a6
No parameters
setImplementations 0x82bc73dc
uint256[] ids
address[] implementations
setValidationLogic 0x1672b0a6
address vLogic
transferOwnership 0xf2fde38b
address newOwner

Recent Transactions

No transactions found for this address