ETH Price: $3,078.87 (-3.21%)
Gas: 6 Gwei

Contract

0xEE72F1035C706478F84AB9480E45B427Aa6B6682
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

1 address found via
Transaction Hash
Method
Block
From
To
Value
Withdraw All193182322024-02-27 10:16:1169 days ago1709028971IN
0xEE72F103...7Aa6B6682
0 ETH0.0022094937.76006015
Refund Tickets193042132024-02-25 11:11:1171 days ago1708859471IN
0xEE72F103...7Aa6B6682
0 ETH0.0014422221.04882782
Refund Tickets193020542024-02-25 3:56:4772 days ago1708833407IN
0xEE72F103...7Aa6B6682
0 ETH0.0016156123.30701499
Refund Tickets193020402024-02-25 3:53:5972 days ago1708833239IN
0xEE72F103...7Aa6B6682
0 ETH0.0016607224.24917595
Refund Tickets192979362024-02-24 14:06:5972 days ago1708783619IN
0xEE72F103...7Aa6B6682
0 ETH0.0024811536.19482817
Refund Tickets192977862024-02-24 13:36:5972 days ago1708781819IN
0xEE72F103...7Aa6B6682
0 ETH0.0017457125.16999227
Refund Tickets192591322024-02-19 3:31:1178 days ago1708313471IN
0xEE72F103...7Aa6B6682
0 ETH0.001506121.98750541
Refund Tickets192578942024-02-18 23:19:4778 days ago1708298387IN
0xEE72F103...7Aa6B6682
0 ETH0.0016110923.51341071
Refund Tickets192328982024-02-15 11:01:4781 days ago1707994907IN
0xEE72F103...7Aa6B6682
0 ETH0.001425720.56192805
Refund Tickets192126822024-02-12 14:56:5984 days ago1707749819IN
0xEE72F103...7Aa6B6682
0 ETH0.002643938.12575371
Refund Tickets192116182024-02-12 11:22:1184 days ago1707736931IN
0xEE72F103...7Aa6B6682
0 ETH0.0013869820.24561467
Refund Tickets192033052024-02-11 7:20:3586 days ago1707636035IN
0xEE72F103...7Aa6B6682
0 ETH0.0014174420.68778961
Refund Tickets191898872024-02-09 10:10:2387 days ago1707473423IN
0xEE72F103...7Aa6B6682
0 ETH0.0033695749.18511173
Refund Tickets191711752024-02-06 19:08:2390 days ago1707246503IN
0xEE72F103...7Aa6B6682
0 ETH0.0017855826.06318741
Refund Tickets191670302024-02-06 5:10:1191 days ago1707196211IN
0xEE72F103...7Aa6B6682
0 ETH0.0014378420.99785571
Refund Tickets191530292024-02-04 5:59:5993 days ago1707026399IN
0xEE72F103...7Aa6B6682
0 ETH0.0009302413.59450532
Refund Tickets191530262024-02-04 5:59:2393 days ago1707026363IN
0xEE72F103...7Aa6B6682
0 ETH0.0009986314.39977853
Refund Tickets191468922024-02-03 9:18:4793 days ago1706951927IN
0xEE72F103...7Aa6B6682
0 ETH0.0010355414.9281333
Refund Tickets191264322024-01-31 12:22:1196 days ago1706703731IN
0xEE72F103...7Aa6B6682
0 ETH0.0014486421.14939218
Refund Tickets191143642024-01-29 19:47:5998 days ago1706557679IN
0xEE72F103...7Aa6B6682
0 ETH0.0009519413.73314344
Refund Tickets190989962024-01-27 16:05:35100 days ago1706371535IN
0xEE72F103...7Aa6B6682
0 ETH0.001327719.15131079
Refund Tickets190962452024-01-27 6:51:11101 days ago1706338271IN
0xEE72F103...7Aa6B6682
0 ETH0.0008286511.94902049
Refund Tickets190906472024-01-26 12:02:23101 days ago1706270543IN
0xEE72F103...7Aa6B6682
0 ETH0.0017443625.4740465
Refund Tickets190864182024-01-25 21:49:59102 days ago1706219399IN
0xEE72F103...7Aa6B6682
0 ETH0.0015859522.86854592
Refund Tickets190758062024-01-24 10:09:35103 days ago1706090975IN
0xEE72F103...7Aa6B6682
0 ETH0.0009134913.17248324
View all transactions

Latest 25 internal transactions (View All)

Advanced mode:
Parent Transaction Hash Block From To Value
193182322024-02-27 10:16:1169 days ago1709028971
0xEE72F103...7Aa6B6682
152.25 ETH
193042132024-02-25 11:11:1171 days ago1708859471
0xEE72F103...7Aa6B6682
0.05 ETH
193020542024-02-25 3:56:4772 days ago1708833407
0xEE72F103...7Aa6B6682
0.05 ETH
193020402024-02-25 3:53:5972 days ago1708833239
0xEE72F103...7Aa6B6682
0.05 ETH
192979362024-02-24 14:06:5972 days ago1708783619
0xEE72F103...7Aa6B6682
0.05 ETH
192977862024-02-24 13:36:5972 days ago1708781819
0xEE72F103...7Aa6B6682
0.5 ETH
192591322024-02-19 3:31:1178 days ago1708313471
0xEE72F103...7Aa6B6682
0.05 ETH
192578942024-02-18 23:19:4778 days ago1708298387
0xEE72F103...7Aa6B6682
0.5 ETH
192328982024-02-15 11:01:4781 days ago1707994907
0xEE72F103...7Aa6B6682
0.05 ETH
192126822024-02-12 14:56:5984 days ago1707749819
0xEE72F103...7Aa6B6682
0.5 ETH
192116182024-02-12 11:22:1184 days ago1707736931
0xEE72F103...7Aa6B6682
0.45 ETH
192033052024-02-11 7:20:3586 days ago1707636035
0xEE72F103...7Aa6B6682
0.05 ETH
191898872024-02-09 10:10:2387 days ago1707473423
0xEE72F103...7Aa6B6682
0.3 ETH
191711752024-02-06 19:08:2390 days ago1707246503
0xEE72F103...7Aa6B6682
0.05 ETH
191670302024-02-06 5:10:1191 days ago1707196211
0xEE72F103...7Aa6B6682
0.5 ETH
191530292024-02-04 5:59:5993 days ago1707026399
0xEE72F103...7Aa6B6682
0.05 ETH
191530262024-02-04 5:59:2393 days ago1707026363
0xEE72F103...7Aa6B6682
0.05 ETH
191468922024-02-03 9:18:4793 days ago1706951927
0xEE72F103...7Aa6B6682
0.1 ETH
191264322024-01-31 12:22:1196 days ago1706703731
0xEE72F103...7Aa6B6682
0.05 ETH
191143642024-01-29 19:47:5998 days ago1706557679
0xEE72F103...7Aa6B6682
0.1 ETH
190989962024-01-27 16:05:35100 days ago1706371535
0xEE72F103...7Aa6B6682
0.05 ETH
190962452024-01-27 6:51:11101 days ago1706338271
0xEE72F103...7Aa6B6682
0.5 ETH
190906472024-01-26 12:02:23101 days ago1706270543
0xEE72F103...7Aa6B6682
0.5 ETH
190864182024-01-25 21:49:59102 days ago1706219399
0xEE72F103...7Aa6B6682
0.05 ETH
190758062024-01-24 10:09:35103 days ago1706090975
0xEE72F103...7Aa6B6682
0.5 ETH
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
LaunchBlockGrapesSale

Compiler Version
v0.8.20+commit.a1b79de6

Optimization Enabled:
Yes with 200 runs

Other Settings:
paris EvmVersion
File 1 of 4 : LaunchBlockGrapesSale.sol
// SPDX-License-Identifier: UNLICENSED
// Developed by Liteflow.com
pragma solidity 0.8.20;

import '@openzeppelin/contracts/access/Ownable.sol';
import '@openzeppelin/contracts/utils/cryptography/MerkleProof.sol';

contract LaunchBlockGrapesSale is Ownable {
    /// @notice Start date of the sale in timestamp
    /// @dev Set in the constructor
    uint256 public immutable startDate;

    /// @notice End date of the sale in timestamp
    /// @dev Set in the constructor
    uint256 public immutable endDate;

    /// @notice Price of one ticket in wei
    /// @dev Set in the constructor
    uint256 public immutable ticketPrice;

    /// @notice Max number of tickets that can be bought per buyer
    /// @dev Set in the constructor
    uint256 public immutable maxTicketsPerBuyer;

    /// @notice Number of tickets bought by each buyer
    /// @dev Managed internally by contract
    mapping(address buyer => uint256) public balances;

    /// @notice Number of tickets refunded for each buyer
    /// @dev Managed internally by contract
    mapping(address buyer => uint256) public refunds;

    /// @notice Refund Merkle root
    bytes32 public refundMerkleRoot;

    /// @notice Emitted when a ticket is bought
    // event TicketBought(address indexed wallet, uint256[] ticketIds);
    event TicketsBought(address indexed wallet, uint256 count);

    /// @notice Emitted when a ticket is refunded
    event TicketsRefunded(address indexed wallet, uint256 count);

    /// @notice Returned when the sale is not started or ended
    error SaleClosed();

    /// @notice Returned when the amount of ETH sent is not equal to the ticket price multiplied by the number of tickets to buy
    error InvalidAmount();

    /// @notice Returned when the number of tickets to buy is greater than the max allowed
    error TicketLimitReached();

    /// @notice Returned when the refund function is not open
    error RefundClosed();

    /// @notice Returned when ticket is already refunded
    error AlreadyRefunded();

    /// @notice Returned when the merkle proof is invalid
    error InvalidMerkleProof();

    /// @notice Returned when the refund transfer fails
    error RefundFailed();

    /// @notice Returned when the sale is not ended
    error SaleNotClosed();

    /// @notice Returned when refund merkle root is already set
    error RefundMerkleRootAlreadySet();

    /// @notice Returned when the withdraw all transfer fails
    error WithdrawFailed();

    /// @notice Returned when it's too early to execute withdraw all
    error WithdrawAllNotEligible();

    /// @notice Returned when the withdraw all ETH fails
    error WithdrawAllFailed();

    /// @notice Initializes the contract
    /// @param initialOwner_ The address of the initial owner of the contract
    /// @param ticketPrice_ The price of one ticket in wei
    /// @param maxTicketsPerBuyer_ Max number of tickets that can be bought per buyer
    /// @param startDate_ Start date of the sale in timestamp
    /// @param endDate_ End date of the sale in timestamp
    constructor(
        address initialOwner_,
        uint256 ticketPrice_,
        uint256 maxTicketsPerBuyer_,
        uint256 startDate_,
        uint256 endDate_
    ) Ownable(initialOwner_) {
        ticketPrice = ticketPrice_;
        maxTicketsPerBuyer = maxTicketsPerBuyer_;
        startDate = startDate_;
        endDate = endDate_;
    }

    /// @notice Buy tickets
    /// @param numberOfTickets_ The number of tickets to buy
    function buyTickets(uint256 numberOfTickets_) external payable {
        // check if the sale is closed
        if (block.timestamp < startDate || block.timestamp > endDate) {
            revert SaleClosed();
        }

        // check amount provided is correct
        if (msg.value != ticketPrice * numberOfTickets_) revert InvalidAmount();

        // calculate the number of tickets bought by the sender
        uint256 balance = balances[msg.sender] + numberOfTickets_;

        // check that the number of tickets is not greater than the max allowed
        if (balance > maxTicketsPerBuyer) revert TicketLimitReached();

        // update ticket balance of buyer
        balances[msg.sender] = balance;

        // emit event
        emit TicketsBought(msg.sender, numberOfTickets_);
    }

    /// @notice Refund tickets
    /// @param ticketsToRefund_ The number of the tickets to refund
    /// @param merkleProof_ The merkle proof of the tickets to refund
    function refundTickets(
        uint256 ticketsToRefund_,
        bytes32[] calldata merkleProof_
    ) external {
        // check refund is activated
        if (refundMerkleRoot == bytes32(0)) revert RefundClosed();

        // check sender was not already refunded
        if (refunds[msg.sender] > 0) revert AlreadyRefunded();

        // check that the merkle proof is valid
        if (
            !MerkleProof.verifyCalldata(
                merkleProof_,
                refundMerkleRoot,
                keccak256(
                    bytes.concat(
                        keccak256(abi.encode(msg.sender, ticketsToRefund_))
                    )
                )
            )
        ) revert InvalidMerkleProof();

        // mark the sender as refunded
        refunds[msg.sender] = ticketsToRefund_;

        // emit event
        emit TicketsRefunded(msg.sender, ticketsToRefund_);

        // do the refund transfer
        (bool sent, ) = msg.sender.call{value: ticketsToRefund_ * ticketPrice}(
            ''
        );
        if (!sent) revert RefundFailed();
    }

    /// @notice Set the refund merkle root and withdraw ETH of the winning tickets. Only owner can execute this function
    /// @param refundMerkleRoot_ The refund merkle root
    /// @param numberOfWinningTickets_ The number of winning tickets
    /// @param to_ The address to send the ETH to
    function finalizeSale(
        bytes32 refundMerkleRoot_,
        uint256 numberOfWinningTickets_,
        address payable to_
    ) external onlyOwner {
        // check if the sale is closed
        if (block.timestamp <= endDate) {
            revert SaleNotClosed();
        }

        // prevent setting the Merkle root if already set
        if (refundMerkleRoot != bytes32(0)) revert RefundMerkleRootAlreadySet();

        // set the merkle root
        refundMerkleRoot = refundMerkleRoot_;

        // transfer amount corresponding to the winning tickets
        (bool sent, ) = to_.call{value: numberOfWinningTickets_ * ticketPrice}(
            ''
        );
        if (!sent) revert WithdrawFailed();
    }

    /// @notice Withdraw all ETH from the contract. Only owner can execute this function.
    /// @param to_ The address to send the ETH to.
    function withdrawAll(address payable to_) external onlyOwner {
        // check the sale is closed for at least 3 days
        if (block.timestamp <= endDate + 3 days) {
            revert WithdrawAllNotEligible();
        }

        (bool sent, ) = to_.call{value: address(this).balance}('');
        if (!sent) revert WithdrawAllFailed();
    }
}

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

pragma solidity ^0.8.20;

import {Context} from "../utils/Context.sol";

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

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

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

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

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

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

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

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

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

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        if (newOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(newOwner);
    }

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

File 3 of 4 : Context.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Context.sol)

pragma solidity ^0.8.20;

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

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

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

pragma solidity ^0.8.20;

/**
 * @dev These functions deal with verification of Merkle Tree proofs.
 *
 * The tree and the proofs can be generated using our
 * https://github.com/OpenZeppelin/merkle-tree[JavaScript library].
 * You will find a quickstart guide in the readme.
 *
 * WARNING: You should avoid using leaf values that are 64 bytes long prior to
 * hashing, or use a hash function other than keccak256 for hashing leaves.
 * This is because the concatenation of a sorted pair of internal nodes in
 * the Merkle tree could be reinterpreted as a leaf value.
 * OpenZeppelin's JavaScript library generates Merkle trees that are safe
 * against this attack out of the box.
 */
library MerkleProof {
    /**
     *@dev The multiproof provided is not valid.
     */
    error MerkleProofInvalidMultiproof();

    /**
     * @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree
     * defined by `root`. For this, a `proof` must be provided, containing
     * sibling hashes on the branch from the leaf to the root of the tree. Each
     * pair of leaves and each pair of pre-images are assumed to be sorted.
     */
    function verify(bytes32[] memory proof, bytes32 root, bytes32 leaf) internal pure returns (bool) {
        return processProof(proof, leaf) == root;
    }

    /**
     * @dev Calldata version of {verify}
     */
    function verifyCalldata(bytes32[] calldata proof, bytes32 root, bytes32 leaf) internal pure returns (bool) {
        return processProofCalldata(proof, leaf) == root;
    }

    /**
     * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up
     * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt
     * hash matches the root of the tree. When processing the proof, the pairs
     * of leafs & pre-images are assumed to be sorted.
     */
    function processProof(bytes32[] memory proof, bytes32 leaf) internal pure returns (bytes32) {
        bytes32 computedHash = leaf;
        for (uint256 i = 0; i < proof.length; i++) {
            computedHash = _hashPair(computedHash, proof[i]);
        }
        return computedHash;
    }

    /**
     * @dev Calldata version of {processProof}
     */
    function processProofCalldata(bytes32[] calldata proof, bytes32 leaf) internal pure returns (bytes32) {
        bytes32 computedHash = leaf;
        for (uint256 i = 0; i < proof.length; i++) {
            computedHash = _hashPair(computedHash, proof[i]);
        }
        return computedHash;
    }

    /**
     * @dev Returns true if the `leaves` can be simultaneously proven to be a part of a Merkle tree defined by
     * `root`, according to `proof` and `proofFlags` as described in {processMultiProof}.
     *
     * CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details.
     */
    function multiProofVerify(
        bytes32[] memory proof,
        bool[] memory proofFlags,
        bytes32 root,
        bytes32[] memory leaves
    ) internal pure returns (bool) {
        return processMultiProof(proof, proofFlags, leaves) == root;
    }

    /**
     * @dev Calldata version of {multiProofVerify}
     *
     * CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details.
     */
    function multiProofVerifyCalldata(
        bytes32[] calldata proof,
        bool[] calldata proofFlags,
        bytes32 root,
        bytes32[] memory leaves
    ) internal pure returns (bool) {
        return processMultiProofCalldata(proof, proofFlags, leaves) == root;
    }

    /**
     * @dev Returns the root of a tree reconstructed from `leaves` and sibling nodes in `proof`. The reconstruction
     * proceeds by incrementally reconstructing all inner nodes by combining a leaf/inner node with either another
     * leaf/inner node or a proof sibling node, depending on whether each `proofFlags` item is true or false
     * respectively.
     *
     * CAUTION: Not all Merkle trees admit multiproofs. To use multiproofs, it is sufficient to ensure that: 1) the tree
     * is complete (but not necessarily perfect), 2) the leaves to be proven are in the opposite order they are in the
     * tree (i.e., as seen from right to left starting at the deepest layer and continuing at the next layer).
     */
    function processMultiProof(
        bytes32[] memory proof,
        bool[] memory proofFlags,
        bytes32[] memory leaves
    ) internal pure returns (bytes32 merkleRoot) {
        // This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by
        // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
        // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
        // the Merkle tree.
        uint256 leavesLen = leaves.length;
        uint256 proofLen = proof.length;
        uint256 totalHashes = proofFlags.length;

        // Check proof validity.
        if (leavesLen + proofLen != totalHashes + 1) {
            revert MerkleProofInvalidMultiproof();
        }

        // The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
        // `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
        bytes32[] memory hashes = new bytes32[](totalHashes);
        uint256 leafPos = 0;
        uint256 hashPos = 0;
        uint256 proofPos = 0;
        // At each step, we compute the next hash using two values:
        // - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
        //   get the next hash.
        // - depending on the flag, either another value from the "main queue" (merging branches) or an element from the
        //   `proof` array.
        for (uint256 i = 0; i < totalHashes; i++) {
            bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
            bytes32 b = proofFlags[i]
                ? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++])
                : proof[proofPos++];
            hashes[i] = _hashPair(a, b);
        }

        if (totalHashes > 0) {
            if (proofPos != proofLen) {
                revert MerkleProofInvalidMultiproof();
            }
            unchecked {
                return hashes[totalHashes - 1];
            }
        } else if (leavesLen > 0) {
            return leaves[0];
        } else {
            return proof[0];
        }
    }

    /**
     * @dev Calldata version of {processMultiProof}.
     *
     * CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details.
     */
    function processMultiProofCalldata(
        bytes32[] calldata proof,
        bool[] calldata proofFlags,
        bytes32[] memory leaves
    ) internal pure returns (bytes32 merkleRoot) {
        // This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by
        // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
        // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
        // the Merkle tree.
        uint256 leavesLen = leaves.length;
        uint256 proofLen = proof.length;
        uint256 totalHashes = proofFlags.length;

        // Check proof validity.
        if (leavesLen + proofLen != totalHashes + 1) {
            revert MerkleProofInvalidMultiproof();
        }

        // The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
        // `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
        bytes32[] memory hashes = new bytes32[](totalHashes);
        uint256 leafPos = 0;
        uint256 hashPos = 0;
        uint256 proofPos = 0;
        // At each step, we compute the next hash using two values:
        // - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
        //   get the next hash.
        // - depending on the flag, either another value from the "main queue" (merging branches) or an element from the
        //   `proof` array.
        for (uint256 i = 0; i < totalHashes; i++) {
            bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
            bytes32 b = proofFlags[i]
                ? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++])
                : proof[proofPos++];
            hashes[i] = _hashPair(a, b);
        }

        if (totalHashes > 0) {
            if (proofPos != proofLen) {
                revert MerkleProofInvalidMultiproof();
            }
            unchecked {
                return hashes[totalHashes - 1];
            }
        } else if (leavesLen > 0) {
            return leaves[0];
        } else {
            return proof[0];
        }
    }

    /**
     * @dev Sorts the pair (a, b) and hashes the result.
     */
    function _hashPair(bytes32 a, bytes32 b) private pure returns (bytes32) {
        return a < b ? _efficientHash(a, b) : _efficientHash(b, a);
    }

    /**
     * @dev Implementation of keccak256(abi.encode(a, b)) that doesn't allocate or expand memory.
     */
    function _efficientHash(bytes32 a, bytes32 b) private pure returns (bytes32 value) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, a)
            mstore(0x20, b)
            value := keccak256(0x00, 0x40)
        }
    }
}

Settings
{
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "evmVersion": "paris",
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"initialOwner_","type":"address"},{"internalType":"uint256","name":"ticketPrice_","type":"uint256"},{"internalType":"uint256","name":"maxTicketsPerBuyer_","type":"uint256"},{"internalType":"uint256","name":"startDate_","type":"uint256"},{"internalType":"uint256","name":"endDate_","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AlreadyRefunded","type":"error"},{"inputs":[],"name":"InvalidAmount","type":"error"},{"inputs":[],"name":"InvalidMerkleProof","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"inputs":[],"name":"RefundClosed","type":"error"},{"inputs":[],"name":"RefundFailed","type":"error"},{"inputs":[],"name":"RefundMerkleRootAlreadySet","type":"error"},{"inputs":[],"name":"SaleClosed","type":"error"},{"inputs":[],"name":"SaleNotClosed","type":"error"},{"inputs":[],"name":"TicketLimitReached","type":"error"},{"inputs":[],"name":"WithdrawAllFailed","type":"error"},{"inputs":[],"name":"WithdrawAllNotEligible","type":"error"},{"inputs":[],"name":"WithdrawFailed","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":"wallet","type":"address"},{"indexed":false,"internalType":"uint256","name":"count","type":"uint256"}],"name":"TicketsBought","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"wallet","type":"address"},{"indexed":false,"internalType":"uint256","name":"count","type":"uint256"}],"name":"TicketsRefunded","type":"event"},{"inputs":[{"internalType":"address","name":"buyer","type":"address"}],"name":"balances","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"numberOfTickets_","type":"uint256"}],"name":"buyTickets","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"endDate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"refundMerkleRoot_","type":"bytes32"},{"internalType":"uint256","name":"numberOfWinningTickets_","type":"uint256"},{"internalType":"address payable","name":"to_","type":"address"}],"name":"finalizeSale","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"maxTicketsPerBuyer","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":"refundMerkleRoot","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"ticketsToRefund_","type":"uint256"},{"internalType":"bytes32[]","name":"merkleProof_","type":"bytes32[]"}],"name":"refundTickets","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"buyer","type":"address"}],"name":"refunds","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"startDate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ticketPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address payable","name":"to_","type":"address"}],"name":"withdrawAll","outputs":[],"stateMutability":"nonpayable","type":"function"}]

61010060405234801561001157600080fd5b50604051610c91380380610c91833981016040819052610030916100d1565b846001600160a01b03811661005f57604051631e4fbdf760e01b81526000600482015260240160405180910390fd5b61006881610081565b5060c09390935260e09190915260805260a05250610126565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b600080600080600060a086880312156100e957600080fd5b85516001600160a01b038116811461010057600080fd5b602087015160408801516060890151608090990151929a91995097965090945092505050565b60805160a05160c05160e051610afb6101966000396000818161016f01526103da01526000818161013b01528181610378015281816104e801526106a001526000818161026f0152818161033201528181610473015261077801526000818160f401526103090152610afb6000f3fe6080604052600436106100dd5760003560e01c80638da5cb5b1161007f578063da82fb4c11610059578063da82fb4c14610291578063df690509146102b1578063f2fde38b146102c7578063fa09e630146102e757600080fd5b80638da5cb5b14610208578063bc3da53514610230578063c24a0f8b1461025d57600080fd5b806327e235e3116100bb57806327e235e3146101915780632f366637146101be5780635155aa08146101d3578063715018a6146101f357600080fd5b80630b97bc86146100e25780631209b1f6146101295780631b9fd7f41461015d575b600080fd5b3480156100ee57600080fd5b506101167f000000000000000000000000000000000000000000000000000000000000000081565b6040519081526020015b60405180910390f35b34801561013557600080fd5b506101167f000000000000000000000000000000000000000000000000000000000000000081565b34801561016957600080fd5b506101167f000000000000000000000000000000000000000000000000000000000000000081565b34801561019d57600080fd5b506101166101ac366004610961565b60016020526000908152604090205481565b6101d16101cc366004610985565b610307565b005b3480156101df57600080fd5b506101d16101ee36600461099e565b610469565b3480156101ff57600080fd5b506101d1610576565b34801561021457600080fd5b506000546040516001600160a01b039091168152602001610120565b34801561023c57600080fd5b5061011661024b366004610961565b60026020526000908152604090205481565b34801561026957600080fd5b506101167f000000000000000000000000000000000000000000000000000000000000000081565b34801561029d57600080fd5b506101d16102ac3660046109d7565b61058a565b3480156102bd57600080fd5b5061011660035481565b3480156102d357600080fd5b506101d16102e2366004610961565b610728565b3480156102f357600080fd5b506101d1610302366004610961565b61076b565b7f000000000000000000000000000000000000000000000000000000000000000042108061035457507f000000000000000000000000000000000000000000000000000000000000000042115b1561037257604051634c013bd760e01b815260040160405180910390fd5b61039c817f0000000000000000000000000000000000000000000000000000000000000000610a6c565b34146103bb5760405163162908e360e11b815260040160405180910390fd5b336000908152600160205260408120546103d6908390610a83565b90507f0000000000000000000000000000000000000000000000000000000000000000811115610419576040516329fd6a2360e11b815260040160405180910390fd5b3360008181526001602052604090819020839055517fb24ee662b0a257d0321d324e737d2507fbe45df8af8d0da951f630f3754eb6a09061045d9085815260200190565b60405180910390a25050565b610471610837565b7f000000000000000000000000000000000000000000000000000000000000000042116104b1576040516378fa807760e01b815260040160405180910390fd5b600354156104d2576040516308fdde9360e01b815260040160405180910390fd5b600383905560006001600160a01b03821661050d7f000000000000000000000000000000000000000000000000000000000000000085610a6c565b604051600081818185875af1925050503d8060008114610549576040519150601f19603f3d011682016040523d82523d6000602084013e61054e565b606091505b505090508061057057604051631d42c86760e21b815260040160405180910390fd5b50505050565b61057e610837565b6105886000610864565b565b6003546105aa57604051630262240360e31b815260040160405180910390fd5b33600090815260026020526040902054156105d85760405163542f378d60e11b815260040160405180910390fd5b6003546040805133602082015290810185905261062f91849184919060600160408051601f1981840301815282825280516020918201209083015201604051602081830303815290604052805190602001206108b4565b61064c5760405163582f497d60e11b815260040160405180910390fd5b3360008181526002602052604090819020859055517f3edd1ff272ea97def9fad80e63c2aa62496cf7dd22a8919d178b1bbe73e04ed3906106909086815260200190565b60405180910390a26000336106c57f000000000000000000000000000000000000000000000000000000000000000086610a6c565b604051600081818185875af1925050503d8060008114610701576040519150601f19603f3d011682016040523d82523d6000602084013e610706565b606091505b505090508061057057604051633c31275160e21b815260040160405180910390fd5b610730610837565b6001600160a01b03811661075f57604051631e4fbdf760e01b8152600060048201526024015b60405180910390fd5b61076881610864565b50565b610773610837565b6107a07f00000000000000000000000000000000000000000000000000000000000000006203f480610a83565b42116107bf57604051630b48d16960e41b815260040160405180910390fd5b6000816001600160a01b03164760405160006040518083038185875af1925050503d806000811461080c576040519150601f19603f3d011682016040523d82523d6000602084013e610811565b606091505b505090508061083357604051630651aee160e41b815260040160405180910390fd5b5050565b6000546001600160a01b031633146105885760405163118cdaa760e01b8152336004820152602401610756565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6000826108c28686856108cc565b1495945050505050565b600081815b8481101561090f576108fb828787848181106108ef576108ef610a96565b90506020020135610918565b91508061090781610aac565b9150506108d1565b50949350505050565b6000818310610934576000828152602084905260409020610943565b60008381526020839052604090205b90505b92915050565b6001600160a01b038116811461076857600080fd5b60006020828403121561097357600080fd5b813561097e8161094c565b9392505050565b60006020828403121561099757600080fd5b5035919050565b6000806000606084860312156109b357600080fd5b833592506020840135915060408401356109cc8161094c565b809150509250925092565b6000806000604084860312156109ec57600080fd5b83359250602084013567ffffffffffffffff80821115610a0b57600080fd5b818601915086601f830112610a1f57600080fd5b813581811115610a2e57600080fd5b8760208260051b8501011115610a4357600080fd5b6020830194508093505050509250925092565b634e487b7160e01b600052601160045260246000fd5b808202811582820484141761094657610946610a56565b8082018082111561094657610946610a56565b634e487b7160e01b600052603260045260246000fd5b600060018201610abe57610abe610a56565b506001019056fea26469706673582212205cd9238c106a5ddb2c460c9953e379cbaf76b9135c2bddfaacef09181ef4daec64736f6c634300081400330000000000000000000000003ae9bf5331e076b55b61933206f797f4375d11d900000000000000000000000000000000000000000000000000b1a2bc2ec50000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000658177b0000000000000000000000000000000000000000000000000000000006582c930

Deployed Bytecode

0x6080604052600436106100dd5760003560e01c80638da5cb5b1161007f578063da82fb4c11610059578063da82fb4c14610291578063df690509146102b1578063f2fde38b146102c7578063fa09e630146102e757600080fd5b80638da5cb5b14610208578063bc3da53514610230578063c24a0f8b1461025d57600080fd5b806327e235e3116100bb57806327e235e3146101915780632f366637146101be5780635155aa08146101d3578063715018a6146101f357600080fd5b80630b97bc86146100e25780631209b1f6146101295780631b9fd7f41461015d575b600080fd5b3480156100ee57600080fd5b506101167f00000000000000000000000000000000000000000000000000000000658177b081565b6040519081526020015b60405180910390f35b34801561013557600080fd5b506101167f00000000000000000000000000000000000000000000000000b1a2bc2ec5000081565b34801561016957600080fd5b506101167f000000000000000000000000000000000000000000000000000000000000000a81565b34801561019d57600080fd5b506101166101ac366004610961565b60016020526000908152604090205481565b6101d16101cc366004610985565b610307565b005b3480156101df57600080fd5b506101d16101ee36600461099e565b610469565b3480156101ff57600080fd5b506101d1610576565b34801561021457600080fd5b506000546040516001600160a01b039091168152602001610120565b34801561023c57600080fd5b5061011661024b366004610961565b60026020526000908152604090205481565b34801561026957600080fd5b506101167f000000000000000000000000000000000000000000000000000000006582c93081565b34801561029d57600080fd5b506101d16102ac3660046109d7565b61058a565b3480156102bd57600080fd5b5061011660035481565b3480156102d357600080fd5b506101d16102e2366004610961565b610728565b3480156102f357600080fd5b506101d1610302366004610961565b61076b565b7f00000000000000000000000000000000000000000000000000000000658177b042108061035457507f000000000000000000000000000000000000000000000000000000006582c93042115b1561037257604051634c013bd760e01b815260040160405180910390fd5b61039c817f00000000000000000000000000000000000000000000000000b1a2bc2ec50000610a6c565b34146103bb5760405163162908e360e11b815260040160405180910390fd5b336000908152600160205260408120546103d6908390610a83565b90507f000000000000000000000000000000000000000000000000000000000000000a811115610419576040516329fd6a2360e11b815260040160405180910390fd5b3360008181526001602052604090819020839055517fb24ee662b0a257d0321d324e737d2507fbe45df8af8d0da951f630f3754eb6a09061045d9085815260200190565b60405180910390a25050565b610471610837565b7f000000000000000000000000000000000000000000000000000000006582c93042116104b1576040516378fa807760e01b815260040160405180910390fd5b600354156104d2576040516308fdde9360e01b815260040160405180910390fd5b600383905560006001600160a01b03821661050d7f00000000000000000000000000000000000000000000000000b1a2bc2ec5000085610a6c565b604051600081818185875af1925050503d8060008114610549576040519150601f19603f3d011682016040523d82523d6000602084013e61054e565b606091505b505090508061057057604051631d42c86760e21b815260040160405180910390fd5b50505050565b61057e610837565b6105886000610864565b565b6003546105aa57604051630262240360e31b815260040160405180910390fd5b33600090815260026020526040902054156105d85760405163542f378d60e11b815260040160405180910390fd5b6003546040805133602082015290810185905261062f91849184919060600160408051601f1981840301815282825280516020918201209083015201604051602081830303815290604052805190602001206108b4565b61064c5760405163582f497d60e11b815260040160405180910390fd5b3360008181526002602052604090819020859055517f3edd1ff272ea97def9fad80e63c2aa62496cf7dd22a8919d178b1bbe73e04ed3906106909086815260200190565b60405180910390a26000336106c57f00000000000000000000000000000000000000000000000000b1a2bc2ec5000086610a6c565b604051600081818185875af1925050503d8060008114610701576040519150601f19603f3d011682016040523d82523d6000602084013e610706565b606091505b505090508061057057604051633c31275160e21b815260040160405180910390fd5b610730610837565b6001600160a01b03811661075f57604051631e4fbdf760e01b8152600060048201526024015b60405180910390fd5b61076881610864565b50565b610773610837565b6107a07f000000000000000000000000000000000000000000000000000000006582c9306203f480610a83565b42116107bf57604051630b48d16960e41b815260040160405180910390fd5b6000816001600160a01b03164760405160006040518083038185875af1925050503d806000811461080c576040519150601f19603f3d011682016040523d82523d6000602084013e610811565b606091505b505090508061083357604051630651aee160e41b815260040160405180910390fd5b5050565b6000546001600160a01b031633146105885760405163118cdaa760e01b8152336004820152602401610756565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6000826108c28686856108cc565b1495945050505050565b600081815b8481101561090f576108fb828787848181106108ef576108ef610a96565b90506020020135610918565b91508061090781610aac565b9150506108d1565b50949350505050565b6000818310610934576000828152602084905260409020610943565b60008381526020839052604090205b90505b92915050565b6001600160a01b038116811461076857600080fd5b60006020828403121561097357600080fd5b813561097e8161094c565b9392505050565b60006020828403121561099757600080fd5b5035919050565b6000806000606084860312156109b357600080fd5b833592506020840135915060408401356109cc8161094c565b809150509250925092565b6000806000604084860312156109ec57600080fd5b83359250602084013567ffffffffffffffff80821115610a0b57600080fd5b818601915086601f830112610a1f57600080fd5b813581811115610a2e57600080fd5b8760208260051b8501011115610a4357600080fd5b6020830194508093505050509250925092565b634e487b7160e01b600052601160045260246000fd5b808202811582820484141761094657610946610a56565b8082018082111561094657610946610a56565b634e487b7160e01b600052603260045260246000fd5b600060018201610abe57610abe610a56565b506001019056fea26469706673582212205cd9238c106a5ddb2c460c9953e379cbaf76b9135c2bddfaacef09181ef4daec64736f6c63430008140033

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

0000000000000000000000003ae9bf5331e076b55b61933206f797f4375d11d900000000000000000000000000000000000000000000000000b1a2bc2ec50000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000658177b0000000000000000000000000000000000000000000000000000000006582c930

-----Decoded View---------------
Arg [0] : initialOwner_ (address): 0x3Ae9Bf5331e076b55B61933206f797F4375D11D9
Arg [1] : ticketPrice_ (uint256): 50000000000000000
Arg [2] : maxTicketsPerBuyer_ (uint256): 10
Arg [3] : startDate_ (uint256): 1702983600
Arg [4] : endDate_ (uint256): 1703070000

-----Encoded View---------------
5 Constructor Arguments found :
Arg [0] : 0000000000000000000000003ae9bf5331e076b55b61933206f797f4375d11d9
Arg [1] : 00000000000000000000000000000000000000000000000000b1a2bc2ec50000
Arg [2] : 000000000000000000000000000000000000000000000000000000000000000a
Arg [3] : 00000000000000000000000000000000000000000000000000000000658177b0
Arg [4] : 000000000000000000000000000000000000000000000000000000006582c930


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading
[ Download: CSV Export  ]
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.