MATIC Price: $0.353018 (-21.73%)
 

Overview

MATIC Balance

Polygon PoS Chain LogoPolygon PoS Chain LogoPolygon PoS Chain Logo9,900.57999999999999889 MATIC

MATIC Value

$3,495.08 (@ $0.35/MATIC)

Token Holdings

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Adventure602212822024-08-05 11:45:3222 secs ago1722858332IN
0xFC3AA5e6...4B0A9FF01
0.02 MATIC0.00706455186.81401839
Adventure602212702024-08-05 11:45:0648 secs ago1722858306IN
0xFC3AA5e6...4B0A9FF01
0.02 MATIC0.0065327172.83649973
Adventure602212552024-08-05 11:44:181 min ago1722858258IN
0xFC3AA5e6...4B0A9FF01
0.02 MATIC0.00656649161.63676704
Adventure602212532024-08-05 11:44:101 min ago1722858250IN
0xFC3AA5e6...4B0A9FF01
0.02 MATIC0.00641778157.97613164
Adventure602212462024-08-05 11:43:422 mins ago1722858222IN
0xFC3AA5e6...4B0A9FF01
0.02 MATIC0.00540243142.86115421
Adventure602212432024-08-05 11:43:362 mins ago1722858216IN
0xFC3AA5e6...4B0A9FF01
0.02 MATIC0.00544554144.00113436
Adventure602212332024-08-05 11:43:162 mins ago1722858196IN
0xFC3AA5e6...4B0A9FF01
0.02 MATIC0.00585351154.86719182
Adventure602212212024-08-05 11:42:503 mins ago1722858170IN
0xFC3AA5e6...4B0A9FF01
0.02 MATIC0.00631462167.06674158
Adventure602212152024-08-05 11:42:363 mins ago1722858156IN
0xFC3AA5e6...4B0A9FF01
0.02 MATIC0.00723095177.99265483
Adventure602212142024-08-05 11:42:343 mins ago1722858154IN
0xFC3AA5e6...4B0A9FF01
0.02 MATIC0.00733474180.54756302
Adventure602212042024-08-05 11:42:143 mins ago1722858134IN
0xFC3AA5e6...4B0A9FF01
0.02 MATIC0.00734865179.7
Adventure602212032024-08-05 11:42:123 mins ago1722858132IN
0xFC3AA5e6...4B0A9FF01
0.02 MATIC0.00656913173.71315565
Adventure602212002024-08-05 11:42:063 mins ago1722858126IN
0xFC3AA5e6...4B0A9FF01
0.02 MATIC0.00633516167.52611862
Adventure602211962024-08-05 11:41:563 mins ago1722858116IN
0xFC3AA5e6...4B0A9FF01
0.02 MATIC0.00663382175.51200934
Adventure602211932024-08-05 11:41:504 mins ago1722858110IN
0xFC3AA5e6...4B0A9FF01
0.02 MATIC0.00698068171.83219632
Adventure602211872024-08-05 11:41:384 mins ago1722858098IN
0xFC3AA5e6...4B0A9FF01
0.02 MATIC0.00738712195.44197927
Adventure602211812024-08-05 11:41:244 mins ago1722858084IN
0xFC3AA5e6...4B0A9FF01
0.02 MATIC0.00684387179.7
Adventure602211802024-08-05 11:41:224 mins ago1722858082IN
0xFC3AA5e6...4B0A9FF01
0.02 MATIC0.0084728208.56140747
Adventure602211542024-08-05 11:40:285 mins ago1722858028IN
0xFC3AA5e6...4B0A9FF01
0.02 MATIC0.00817286216.12189092
Adventure602211502024-08-05 11:40:185 mins ago1722858018IN
0xFC3AA5e6...4B0A9FF01
0.02 MATIC0.00684046179.7
Adventure602211442024-08-05 11:40:065 mins ago1722858006IN
0xFC3AA5e6...4B0A9FF01
0.02 MATIC0.00673499165.78460929
Adventure602211392024-08-05 11:39:565 mins ago1722857996IN
0xFC3AA5e6...4B0A9FF01
0.02 MATIC0.00606827149.3728199
Adventure602211382024-08-05 11:39:546 mins ago1722857994IN
0xFC3AA5e6...4B0A9FF01
0.02 MATIC0.00553268146.30537577
Adventure602211352024-08-05 11:39:466 mins ago1722857986IN
0xFC3AA5e6...4B0A9FF01
0.02 MATIC0.00563184149.00245819
Adventure602211252024-08-05 11:39:266 mins ago1722857966IN
0xFC3AA5e6...4B0A9FF01
0.02 MATIC0.00483556127.9351382
View all transactions

Latest 25 internal transactions (View All)

Parent Transaction Hash Block From To
602122912024-08-05 6:10:405 hrs ago1722838240
0xFC3AA5e6...4B0A9FF01
150 MATIC
602121182024-08-05 6:04:245 hrs ago1722837864
0xFC3AA5e6...4B0A9FF01
175 MATIC
601337652024-08-03 5:38:462 days ago1722663526
0xFC3AA5e6...4B0A9FF01
450 MATIC
601132202024-08-02 17:11:482 days ago1722618708
0xFC3AA5e6...4B0A9FF01
2,398 MATIC
599854452024-07-30 10:45:436 days ago1722336343
0xFC3AA5e6...4B0A9FF01
300 MATIC
599681612024-07-30 0:04:426 days ago1722297882
0xFC3AA5e6...4B0A9FF01
600 MATIC
599538222024-07-29 15:31:516 days ago1722267111
0xFC3AA5e6...4B0A9FF01
300 MATIC
599104242024-07-28 12:56:097 days ago1722171369
0xFC3AA5e6...4B0A9FF01
150 MATIC
596372542024-07-21 16:26:1514 days ago1721579175
0xFC3AA5e6...4B0A9FF01
150 MATIC
596163802024-07-21 4:01:2015 days ago1721534480
0xFC3AA5e6...4B0A9FF01
600 MATIC
595952642024-07-20 15:14:4015 days ago1721488480
0xFC3AA5e6...4B0A9FF01
150 MATIC
593150342024-07-13 15:42:5222 days ago1720885372
0xFC3AA5e6...4B0A9FF01
150 MATIC
592993512024-07-13 6:14:3923 days ago1720851279
0xFC3AA5e6...4B0A9FF01
175 MATIC
592686352024-07-12 11:43:3224 days ago1720784612
0xFC3AA5e6...4B0A9FF01
245 MATIC
591084052024-07-08 12:27:4127 days ago1720441661
0xFC3AA5e6...4B0A9FF01
1,500 MATIC
590738872024-07-07 15:45:3628 days ago1720367136
0xFC3AA5e6...4B0A9FF01
150 MATIC
588864042024-07-02 23:27:0833 days ago1719962828
0xFC3AA5e6...4B0A9FF01
1,110 MATIC
588863282024-07-02 23:24:2633 days ago1719962666
0xFC3AA5e6...4B0A9FF01
0 MATIC
588682202024-07-02 12:30:5333 days ago1719923453
0xFC3AA5e6...4B0A9FF01
150 MATIC
587976982024-06-30 17:52:3535 days ago1719769955
0xFC3AA5e6...4B0A9FF01
150 MATIC
586576482024-06-27 5:36:2139 days ago1719466581
0xFC3AA5e6...4B0A9FF01
150 MATIC
584215212024-06-21 7:48:2645 days ago1718956106
0xFC3AA5e6...4B0A9FF01
150 MATIC
584003352024-06-20 18:57:2945 days ago1718909849
0xFC3AA5e6...4B0A9FF01
150 MATIC
583808462024-06-20 7:16:5646 days ago1718867816
0xFC3AA5e6...4B0A9FF01
150 MATIC
581656972024-06-14 21:41:2551 days ago1718401285
0xFC3AA5e6...4B0A9FF01
150 MATIC
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
AdventureContract

Compiler Version
v0.8.24+commit.e11b9ed9

Optimization Enabled:
Yes with 200 runs

Other Settings:
cancun EvmVersion
File 1 of 14 : AdventureContract.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.24;

import {Ownable, Ownable2Step} from "@openzeppelin/contracts/access/Ownable2Step.sol";
import {ReentrancyGuardTransient} from "@openzeppelin/contracts/utils/ReentrancyGuardTransient.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
/**
 * @title Space Mavericks contract.
 * @notice Contract for the Space Mavericks game with staking functionality onboard.
 */

contract AdventureContract is Ownable2Step, ReentrancyGuardTransient {
    /**
     * @param loopFinishedTimestamp The timestamp of the last finished game loop played in adventure mode.
     * @param loopCount This represents a number of gaming cycles completed by the user in the adventure mode.
     * Design of the game assumes holding an airdrop for several months.
     * Since the minimal cooldown time is one hour and number of iterations to complete one cycle is 3,
     * we can find out that the maximum number of iterations(loopCount) is 24/3 = 8 per day.
     * So, the maximum number of iterations for 30 days is 8 * 30 = 240.
     * Since uint16 maximum value equals 65535, it's considered as safe to run the airdrop
     * with no worries an overflow could occur.
     * @param loopState Is an iterator of the cycle in the adventure mode
     * that is limited by a number of iterations that can be no more than 3 (that is specified in FINAL_LOOP_STATE constant).
     * so uint8 can be use to hold this number without an overflow.
     */
    struct GameStats {
        uint256 loopFinishedTimestamp;
        uint16 loopCount;
        uint8 loopState;
    }

    /**
     * @param stakedAmount Is a number of user native tokens staked in contract.
     * @param lastDepositTimestamp The timestamp of the last deposit action of user, it is needed for staking lock functionality.
     */
    struct StakingInfo {
        uint256 stakedAmount;
        uint256 lastDepositTimestamp;
    }

    /**
     * @notice Throw if the user staking balance is insufficient to perform operation.
     */
    error InsufficientStakedBalance();
    /**
     * @notice Throw if transaction with native token transfer failed while withdrawing the staking assets.
     */
    error WithdrawFailed();
    /**
     * @notice Throw if transaction with native token transfer failed while sweeping tokens.
     */
    error TransferOutFailed();
    /**
     * @notice Throw if provided amount is equal to zero.
     */
    error InvalidAmount();
    /**
     * @notice Throw if the payment amount is not equal to the required amount for the adventure game.
     */
    error AdventureWrongPayment();
    /**
     * @notice Throw if the owner is trying to withdraw user's staked assets.
     */
    error StakingGuard();
    /**
     * @notice Throw if deposit amount is less than minimal deposit amount allowed to stake.
     */
    error AmountBelowThreshold();
    /**
     * @notice Throw if the user is trying to play adventure mode while the cooldown is active.
     */
    error CooldownLock();
    /**
     * @notice Throw if adventure mode is disabled.
     */
    error AdventureDisabled();
    /**
     * @notice Throw if staking is disabled.
     */
    error StakingDisabled();
    /**
     * @notice Throw if time since the last deposit is less than 1 days.
     */
    error WithdrawLock();
    /**
     * @notice Throw if the owner is trying to set the minimal staking amount more then the constraint allows.
     */
    error MinStakingAmountConstraintViolation();
    /**
     * @notice Throw if the address of token receiver is zero address.
     */
    error ZeroAddress();
    /**
     * @notice Throw if owner try to renounce ownership.
     */
    error CannotRenounceOwnership();

    uint256 public constant ADVENTURE_MODE_PRICE = 0.02 ether;
    uint256 public constant COOLDOWN_BASE = 1 hours;
    uint256 public constant COOLDOWN_REDUCTION_MAX = 1 hours;
    uint256 public constant COOLDOWN_MIN = COOLDOWN_BASE - COOLDOWN_REDUCTION_MAX; // 1 hours
    uint256 public constant ONE_MINUTE_BOOST = 35 ether;
    uint256 public constant MIN_STAKING_AMOUNT_CONSTRAINT = 500 ether;
    uint256 public constant WITHDRAW_TIME_LOCK = 1 days;
    uint256 public constant FINAL_LOOP_STATE = 3;
    uint256 public constant BASE_COOLDOWN_UNIT = 1 minutes;
    uint256 public minStakingAmount;
    uint256 public totalStaked;
    bool public isStakingEnabled;
    bool public isAdventureEnabled;

    mapping(address userAddress => StakingInfo) public stakingData;
    mapping(address userAddress => GameStats) public adventureGameStats;

    /**
     * @notice Emitted when native tokens are received.
     * @param sender The address of the sender.
     * @param amount The amount of native tokens received.
     */
    event Received(address sender, uint256 amount);
    /**
     * @notice Emitted when the user deposits native tokens to the staking balance.
     * @param staker The address of the user.
     * @param amount The amount of native tokens deposited.
     */
    event Deposited(address indexed staker, uint256 amount);
    /**
     * @notice Emitted when the user withdraws native tokens from the staking balance.
     * @param staker The address of the user.
     * @param amount The amount of native tokens withdrawn.
     */
    event Withdrew(address indexed staker, uint256 amount);
    /**
     * @notice Emitted when the user plays in the adventure mode.
     * @param adventurer The address of the user.
     * @param actionTimestamp The timestamp of the action.
     * @param loopCount The number of cycles completed.
     * @param loopState The iteration of the cycle.
     */
    event Adventure(address indexed adventurer, uint256 actionTimestamp, uint16 loopCount, uint8 loopState);
    /**
     * @notice Emitted when the owner toggles the adventure game mode.
     * @param enabled The status of the adventure game mode.
     */
    event AdventureToggled(bool enabled);
    /**
     * @notice Emitted when the owner toggles the staking mode.
     * @param enabled The status of the staking mode.
     */
    event StakingToggled(bool enabled);
    /**
     * @notice Emitted when the owner sweeps native tokens to the recipient.
     * @param recipient The address of the recipient.
     * @param amount The amount of native tokens swept.
     */
    event NativeTokenSwept(address recipient, uint256 amount);
    /**
     * @notice Emitted when the owner sweeps ERC-20 tokens to the recipient.
     * @param recipient The address of the recipient.
     * @param asset The address of the ERC-20 token.
     * @param amount The amount of ERC-20 tokens swept.
     */
    event ERC20TokenSwept(address recipient, address asset, uint256 amount);
    /**
     * @notice Emitted when the owner changes the minimal staking amount.
     * @param amount The new minimal staking amount.
     */
    event MinStakingAmountChanged(uint256 amount);

    /**
     * @notice Modifier to check if the adventure game is enabled.
     */
    modifier whenAdventureEnabled() {
        if (!isAdventureEnabled) revert AdventureDisabled();
        _;
    }

    /**
     * @notice Modifier to check if the staking is enabled.
     */
    modifier whenStakingEnabled() {
        if (!isStakingEnabled) revert StakingDisabled();
        _;
    }

    constructor(address owner) Ownable(owner) {
        isAdventureEnabled = true;
        isStakingEnabled = true;
        minStakingAmount = 100 ether;
    }

    receive() external payable {
        deposit();
        emit Received(msg.sender, msg.value);
    }

    /**
     * @notice Allows the owner to turn on/off the AdventureContract.s.sol game functionality.
     */
    function toggleAdventure() external onlyOwner {
        isAdventureEnabled = !isAdventureEnabled;
        emit AdventureToggled(isAdventureEnabled);
    }

    /**
     * @notice Allows the owner to turn on/off the Staking functionality.
     */
    function toggleStaking() external onlyOwner {
        isStakingEnabled = !isStakingEnabled;
        emit StakingToggled(isStakingEnabled);
    }

    /**
     * @notice Allows the owner to modify the minimum staking amount.
     * @param amount New minimum amount for staking.
     */
    function changeMinStakingAmount(uint256 amount) external onlyOwner {
        if (amount > MIN_STAKING_AMOUNT_CONSTRAINT) revert MinStakingAmountConstraintViolation();
        minStakingAmount = amount;
        emit MinStakingAmountChanged(amount);
    }

    /**
     * @notice Allows the user to play in the adventure mode for a price of 0.02 ether.
     * @dev The user must send exactly 0.02 ether to adventure.
     */
    function adventure() external payable whenAdventureEnabled {
        uint256 amount = msg.value;
        if (amount != ADVENTURE_MODE_PRICE) revert AdventureWrongPayment();

        address sender = msg.sender;
        uint256 timestamp = block.timestamp;

        GameStats memory stats = adventureGameStats[sender];
        bool cooldownLock = timestamp - stats.loopFinishedTimestamp < computeCooldown(sender);
        if (cooldownLock) revert CooldownLock();

        if (stats.loopState == FINAL_LOOP_STATE) {
            stats.loopCount += 1;
            stats.loopState = 1; // refresh loop state
        } else {
            stats.loopState += 1;
            if (stats.loopState == FINAL_LOOP_STATE) stats.loopFinishedTimestamp = timestamp;
        }
        adventureGameStats[sender] = stats;
        emit Adventure(sender, timestamp, stats.loopCount, stats.loopState);
    }

    /**
     * @notice Withdraws the specified amount from the staked balance of the caller.
     * @dev Note: The user must wait for 1 day after the last deposit to withdraw the staked amount.
     * If a user's remaining staking balance is less than the minimum staking amount, all tokens that were staked by the user are returned to user.
     * @param amount The amount to withdraw.
     */
    function withdraw(uint256 amount) external nonReentrant {
        if (amount == 0) revert InvalidAmount();
        address sender = msg.sender;
        StakingInfo memory stakingInfo = stakingData[sender];
        if (block.timestamp - stakingInfo.lastDepositTimestamp < WITHDRAW_TIME_LOCK) revert WithdrawLock();
        if (amount > stakingInfo.stakedAmount) revert InsufficientStakedBalance();
        uint256 _amount = amount;
        if (stakingInfo.stakedAmount - _amount < minStakingAmount) {
            _amount = stakingInfo.stakedAmount;
        }
        stakingData[sender].stakedAmount -= _amount;
        totalStaked -= _amount;
        (bool success,) = address(sender).call{value: _amount}("");
        if (!success) revert WithdrawFailed();
        emit Withdrew(sender, _amount);
    }

    /**
     * @notice Allows the owner to sweep accidental ERC-20 transfers to this contract.
     * @dev Note: Make sure to check that the asset being swept out is not malicious.
     * @param recipient The address that will receive the swept funds.
     * @param asset The address of the ERC-20 token to sweep.
     */
    function sweepToken(address recipient, address asset) external onlyOwner {
        if (recipient == address(0)) revert ZeroAddress();
        IERC20 token = IERC20(asset);
        uint256 balance = token.balanceOf(address(this));
        SafeERC20.safeTransfer(token, recipient, balance);
        emit ERC20TokenSwept(recipient, asset, balance);
    }

    /**
     * @notice Allows the owner to collect game payments for the adventure game mode.
     * @param recipient The address that will receive the swept funds.
     */
    function collectPayments(address recipient, uint256 amount) external onlyOwner {
        if (recipient == address(0)) revert ZeroAddress();
        if (amount == 0) revert InvalidAmount();
        if (amount > address(this).balance - totalStaked) revert StakingGuard();
        (bool success,) = recipient.call{value: amount}("");
        if (!success) revert WithdrawFailed();
        emit NativeTokenSwept(recipient, amount);
    }

    /**
     * @notice Returns the user's last action, last loop, cooldown, and last loop state.
     * @param user The address of the user.
     * @return The user's last action, last loop, cooldown, and last loop state.
     */
    function getAdventureGameStats(address user) external view returns (uint256, uint256, uint16, uint8) {
        GameStats memory stats = adventureGameStats[user];
        uint256 cooldown = computeCooldown(user);
        return (stats.loopFinishedTimestamp, cooldown, stats.loopCount, stats.loopState);
    }

    /**
     * @notice Returns the user's staked balance and withdraw lock status.
     * @param user The address of the user.
     * @return The staked balance of the user, withdraw lock status.
     */
    function getStakingInfo(address user) external view returns (uint256, bool) {
        StakingInfo memory stakingInfo = stakingData[user];
        bool withdrawLocked = block.timestamp - stakingInfo.lastDepositTimestamp < WITHDRAW_TIME_LOCK;
        return (stakingInfo.stakedAmount, withdrawLocked);
    }

    /**
     * @notice Deposits the specified amount to the staked balance of the caller.
     * @dev Note: The amount of tokens deposited will be locked for 1 day.
     */
    function deposit() public payable whenStakingEnabled {
        uint256 amount = msg.value;
        address sender = msg.sender;
        if (amount < minStakingAmount) revert AmountBelowThreshold();
        stakingData[sender].stakedAmount += amount;
        stakingData[sender].lastDepositTimestamp = block.timestamp;
        totalStaked += amount;
        emit Deposited(sender, amount);
    }

    /**
     * @notice Computes the cooldown between iterations in the adventure mode cycle for the specific user.
     * @dev The cooldown is rounded to seconds.
     * @param user The address of the user.
     * @return The cooldown for the user in seconds.
     */
    function computeCooldown(address user) public view returns (uint256) {
        uint256 stakedAmount = stakingData[user].stakedAmount;
        if (stakedAmount < minStakingAmount) {
            return COOLDOWN_BASE;
        }
        uint256 cooldownBoost = (stakedAmount * BASE_COOLDOWN_UNIT) / ONE_MINUTE_BOOST;
        return (cooldownBoost > COOLDOWN_REDUCTION_MAX) ? COOLDOWN_MIN : COOLDOWN_BASE - cooldownBoost;
    }

    /**
     * @notice Overrides the renounceOwnership function to prevent contract being without an owner.
     */
    function renounceOwnership() public override onlyOwner {
        revert CannotRenounceOwnership();
    }
}

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

pragma solidity ^0.8.20;

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

/**
 * @dev Contract module which provides access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * This extension of the {Ownable} contract includes a two-step mechanism to transfer
 * ownership, where the new owner must call {acceptOwnership} in order to replace the
 * old one. This can help prevent common mistakes, such as transfers of ownership to
 * incorrect accounts, or to contracts that are unable to interact with the
 * permission system.
 *
 * The initial owner is specified at deployment time in the constructor for `Ownable`. This
 * can later be changed with {transferOwnership} and {acceptOwnership}.
 *
 * This module is used through inheritance. It will make available all functions
 * from parent (Ownable).
 */
abstract contract Ownable2Step is Ownable {
    address private _pendingOwner;

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

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

    /**
     * @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual override onlyOwner {
        _pendingOwner = newOwner;
        emit OwnershipTransferStarted(owner(), newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual override {
        delete _pendingOwner;
        super._transferOwnership(newOwner);
    }

    /**
     * @dev The new owner accepts the ownership transfer.
     */
    function acceptOwnership() public virtual {
        address sender = _msgSender();
        if (pendingOwner() != sender) {
            revert OwnableUnauthorizedAccount(sender);
        }
        _transferOwnership(sender);
    }
}

File 3 of 14 : ReentrancyGuardTransient.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.24;

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

/**
 * @dev Variant of {ReentrancyGuard} that uses transient storage.
 *
 * NOTE: This variant only works on networks where EIP-1153 is available.
 */
abstract contract ReentrancyGuardTransient {
    using StorageSlot for *;

    // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ReentrancyGuard")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant REENTRANCY_GUARD_STORAGE =
        0x9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00;

    /**
     * @dev Unauthorized reentrant call.
     */
    error ReentrancyGuardReentrantCall();

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        _nonReentrantBefore();
        _;
        _nonReentrantAfter();
    }

    function _nonReentrantBefore() private {
        // On the first call to nonReentrant, _status will be NOT_ENTERED
        if (_reentrancyGuardEntered()) {
            revert ReentrancyGuardReentrantCall();
        }

        // Any calls to nonReentrant after this point will fail
        REENTRANCY_GUARD_STORAGE.asBoolean().tstore(true);
    }

    function _nonReentrantAfter() private {
        REENTRANCY_GUARD_STORAGE.asBoolean().tstore(false);
    }

    /**
     * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
     * `nonReentrant` function in the call stack.
     */
    function _reentrancyGuardEntered() internal view returns (bool) {
        return REENTRANCY_GUARD_STORAGE.asBoolean().tload();
    }
}

File 4 of 14 : SafeERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../IERC20.sol";
import {IERC1363} from "../../../interfaces/IERC1363.sol";
import {Address} from "../../../utils/Address.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC-20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    using Address for address;

    /**
     * @dev An operation with an ERC-20 token failed.
     */
    error SafeERC20FailedOperation(address token);

    /**
     * @dev Indicates a failed `decreaseAllowance` request.
     */
    error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);

    /**
     * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
    }

    /**
     * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
     * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
     */
    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
    }

    /**
     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 oldAllowance = token.allowance(address(this), spender);
        forceApprove(token, spender, oldAllowance + value);
    }

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
     * value, non-reverting calls are assumed to be successful.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
        unchecked {
            uint256 currentAllowance = token.allowance(address(this), spender);
            if (currentAllowance < requestedDecrease) {
                revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
            }
            forceApprove(token, spender, currentAllowance - requestedDecrease);
        }
    }

    /**
     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
     * to be set to zero before setting it to a non-zero value, such as USDT.
     */
    function forceApprove(IERC20 token, address spender, uint256 value) internal {
        bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));

        if (!_callOptionalReturnBool(token, approvalCall)) {
            _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
            _callOptionalReturn(token, approvalCall);
        }
    }

    /**
     * @dev Performs an {ERC1363} transferAndCall, with a fallback to the simple {ERC20} transfer if the target has no
     * code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
     * targeting contracts.
     *
     * Reverts if the returned value is other than `true`.
     */
    function transferAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
        if (to.code.length == 0) {
            safeTransfer(token, to, value);
        } else if (!token.transferAndCall(to, value, data)) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Performs an {ERC1363} transferFromAndCall, with a fallback to the simple {ERC20} transferFrom if the target
     * has no code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
     * targeting contracts.
     *
     * Reverts if the returned value is other than `true`.
     */
    function transferFromAndCallRelaxed(
        IERC1363 token,
        address from,
        address to,
        uint256 value,
        bytes memory data
    ) internal {
        if (to.code.length == 0) {
            safeTransferFrom(token, from, to, value);
        } else if (!token.transferFromAndCall(from, to, value, data)) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Performs an {ERC1363} approveAndCall, with a fallback to the simple {ERC20} approve if the target has no
     * code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
     * targeting contracts.
     *
     * NOTE: When the recipient address (`to`) has no code (i.e. is an EOA), this function behaves as {forceApprove}.
     * Opposedly, when the recipient address (`to`) has code, this function only attempts to call {ERC1363-approveAndCall}
     * once without retrying, and relies on the returned value to be true.
     *
     * Reverts if the returned value is other than `true`.
     */
    function approveAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
        if (to.code.length == 0) {
            forceApprove(token, to, value);
        } else if (!token.approveAndCall(to, value, data)) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.

        bytes memory returndata = address(token).functionCall(data);
        if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
        // and not revert is the subcall reverts.

        (bool success, bytes memory returndata) = address(token).call(data);
        return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;
    }
}

File 5 of 14 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC-20 standard as defined in the ERC.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the value of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the value of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves a `value` amount of tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 value) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
     * caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the
     * allowance mechanism. `value` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 value) external returns (bool);
}

File 6 of 14 : 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 7 of 14 : StorageSlot.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/StorageSlot.sol)
// This file was procedurally generated from scripts/generate/templates/StorageSlot.js.

pragma solidity ^0.8.24;

/**
 * @dev Library for reading and writing primitive types to specific storage slots.
 *
 * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
 * This library helps with reading and writing to such slots without the need for inline assembly.
 *
 * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
 *
 * Example usage to set ERC-1967 implementation slot:
 * ```solidity
 * contract ERC1967 {
 *     // Define the slot. Alternatively, use the SlotDerivation library to derive the slot.
 *     bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
 *
 *     function _getImplementation() internal view returns (address) {
 *         return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
 *     }
 *
 *     function _setImplementation(address newImplementation) internal {
 *         require(newImplementation.code.length > 0);
 *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
 *     }
 * }
 * ```
 *
 * Since version 5.1, this library also support writing and reading value types to and from transient storage.
 *
 *  * Example using transient storage:
 * ```solidity
 * contract Lock {
 *     // Define the slot. Alternatively, use the SlotDerivation library to derive the slot.
 *     bytes32 internal constant _LOCK_SLOT = 0xf4678858b2b588224636b8522b729e7722d32fc491da849ed75b3fdf3c84f542;
 *
 *     modifier locked() {
 *         require(!_LOCK_SLOT.asBoolean().tload());
 *
 *         _LOCK_SLOT.asBoolean().tstore(true);
 *         _;
 *         _LOCK_SLOT.asBoolean().tstore(false);
 *     }
 * }
 * ```
 *
 * TIP: Consider using this library along with {SlotDerivation}.
 */
library StorageSlot {
    struct AddressSlot {
        address value;
    }

    struct BooleanSlot {
        bool value;
    }

    struct Bytes32Slot {
        bytes32 value;
    }

    struct Uint256Slot {
        uint256 value;
    }

    struct Int256Slot {
        int256 value;
    }

    struct StringSlot {
        string value;
    }

    struct BytesSlot {
        bytes value;
    }

    /**
     * @dev Returns an `AddressSlot` with member `value` located at `slot`.
     */
    function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
     */
    function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
     */
    function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
     */
    function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `Int256Slot` with member `value` located at `slot`.
     */
    function getInt256Slot(bytes32 slot) internal pure returns (Int256Slot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `StringSlot` with member `value` located at `slot`.
     */
    function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `StringSlot` representation of the string storage pointer `store`.
     */
    function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := store.slot
        }
    }

    /**
     * @dev Returns an `BytesSlot` with member `value` located at `slot`.
     */
    function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.
     */
    function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := store.slot
        }
    }

    /**
     * @dev UDVT that represent a slot holding a address.
     */
    type AddressSlotType is bytes32;

    /**
     * @dev Cast an arbitrary slot to a AddressSlotType.
     */
    function asAddress(bytes32 slot) internal pure returns (AddressSlotType) {
        return AddressSlotType.wrap(slot);
    }

    /**
     * @dev UDVT that represent a slot holding a bool.
     */
    type BooleanSlotType is bytes32;

    /**
     * @dev Cast an arbitrary slot to a BooleanSlotType.
     */
    function asBoolean(bytes32 slot) internal pure returns (BooleanSlotType) {
        return BooleanSlotType.wrap(slot);
    }

    /**
     * @dev UDVT that represent a slot holding a bytes32.
     */
    type Bytes32SlotType is bytes32;

    /**
     * @dev Cast an arbitrary slot to a Bytes32SlotType.
     */
    function asBytes32(bytes32 slot) internal pure returns (Bytes32SlotType) {
        return Bytes32SlotType.wrap(slot);
    }

    /**
     * @dev UDVT that represent a slot holding a uint256.
     */
    type Uint256SlotType is bytes32;

    /**
     * @dev Cast an arbitrary slot to a Uint256SlotType.
     */
    function asUint256(bytes32 slot) internal pure returns (Uint256SlotType) {
        return Uint256SlotType.wrap(slot);
    }

    /**
     * @dev UDVT that represent a slot holding a int256.
     */
    type Int256SlotType is bytes32;

    /**
     * @dev Cast an arbitrary slot to a Int256SlotType.
     */
    function asInt256(bytes32 slot) internal pure returns (Int256SlotType) {
        return Int256SlotType.wrap(slot);
    }

    /**
     * @dev Load the value held at location `slot` in transient storage.
     */
    function tload(AddressSlotType slot) internal view returns (address value) {
        /// @solidity memory-safe-assembly
        assembly {
            value := tload(slot)
        }
    }

    /**
     * @dev Store `value` at location `slot` in transient storage.
     */
    function tstore(AddressSlotType slot, address value) internal {
        /// @solidity memory-safe-assembly
        assembly {
            tstore(slot, value)
        }
    }

    /**
     * @dev Load the value held at location `slot` in transient storage.
     */
    function tload(BooleanSlotType slot) internal view returns (bool value) {
        /// @solidity memory-safe-assembly
        assembly {
            value := tload(slot)
        }
    }

    /**
     * @dev Store `value` at location `slot` in transient storage.
     */
    function tstore(BooleanSlotType slot, bool value) internal {
        /// @solidity memory-safe-assembly
        assembly {
            tstore(slot, value)
        }
    }

    /**
     * @dev Load the value held at location `slot` in transient storage.
     */
    function tload(Bytes32SlotType slot) internal view returns (bytes32 value) {
        /// @solidity memory-safe-assembly
        assembly {
            value := tload(slot)
        }
    }

    /**
     * @dev Store `value` at location `slot` in transient storage.
     */
    function tstore(Bytes32SlotType slot, bytes32 value) internal {
        /// @solidity memory-safe-assembly
        assembly {
            tstore(slot, value)
        }
    }

    /**
     * @dev Load the value held at location `slot` in transient storage.
     */
    function tload(Uint256SlotType slot) internal view returns (uint256 value) {
        /// @solidity memory-safe-assembly
        assembly {
            value := tload(slot)
        }
    }

    /**
     * @dev Store `value` at location `slot` in transient storage.
     */
    function tstore(Uint256SlotType slot, uint256 value) internal {
        /// @solidity memory-safe-assembly
        assembly {
            tstore(slot, value)
        }
    }

    /**
     * @dev Load the value held at location `slot` in transient storage.
     */
    function tload(Int256SlotType slot) internal view returns (int256 value) {
        /// @solidity memory-safe-assembly
        assembly {
            value := tload(slot)
        }
    }

    /**
     * @dev Store `value` at location `slot` in transient storage.
     */
    function tstore(Int256SlotType slot, int256 value) internal {
        /// @solidity memory-safe-assembly
        assembly {
            tstore(slot, value)
        }
    }
}

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

pragma solidity ^0.8.20;

import {IERC20} from "./IERC20.sol";
import {IERC165} from "./IERC165.sol";

/**
 * @title IERC1363
 * @dev Interface of the ERC-1363 standard as defined in the https://eips.ethereum.org/EIPS/eip-1363[ERC-1363].
 *
 * Defines an extension interface for ERC-20 tokens that supports executing code on a recipient contract
 * after `transfer` or `transferFrom`, or code on a spender contract after `approve`, in a single transaction.
 */
interface IERC1363 is IERC20, IERC165 {
    /*
     * Note: the ERC-165 identifier for this interface is 0xb0202a11.
     * 0xb0202a11 ===
     *   bytes4(keccak256('transferAndCall(address,uint256)')) ^
     *   bytes4(keccak256('transferAndCall(address,uint256,bytes)')) ^
     *   bytes4(keccak256('transferFromAndCall(address,address,uint256)')) ^
     *   bytes4(keccak256('transferFromAndCall(address,address,uint256,bytes)')) ^
     *   bytes4(keccak256('approveAndCall(address,uint256)')) ^
     *   bytes4(keccak256('approveAndCall(address,uint256,bytes)'))
     */

    /**
     * @dev Moves a `value` amount of tokens from the caller's account to `to`
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferAndCall(address to, uint256 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from the caller's account to `to`
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @param data Additional data with no specified format, sent in call to `to`.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param from The address which you want to send tokens from.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferFromAndCall(address from, address to, uint256 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param from The address which you want to send tokens from.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @param data Additional data with no specified format, sent in call to `to`.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferFromAndCall(address from, address to, uint256 value, bytes calldata data) external returns (bool);

    /**
     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
     * caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
     * @param spender The address which will spend the funds.
     * @param value The amount of tokens to be spent.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function approveAndCall(address spender, uint256 value) external returns (bool);

    /**
     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
     * caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
     * @param spender The address which will spend the funds.
     * @param value The amount of tokens to be spent.
     * @param data Additional data with no specified format, sent in call to `spender`.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function approveAndCall(address spender, uint256 value, bytes calldata data) external returns (bool);
}

File 9 of 14 : Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)

pragma solidity ^0.8.20;

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

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev There's no code at `target` (it is not a contract).
     */
    error AddressEmptyCode(address target);

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        if (address(this).balance < amount) {
            revert Errors.InsufficientBalance(address(this).balance, amount);
        }

        (bool success, ) = recipient.call{value: amount}("");
        if (!success) {
            revert Errors.FailedCall();
        }
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason or custom error, it is bubbled
     * up by this function (like regular Solidity function calls). However, if
     * the call reverted with no returned reason, this function reverts with a
     * {Errors.FailedCall} error.
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        if (address(this).balance < value) {
            revert Errors.InsufficientBalance(address(this).balance, value);
        }
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
     * was not a contract or bubbling up the revert reason (falling back to {Errors.FailedCall}) in case
     * of an unsuccessful call.
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata
    ) internal view returns (bytes memory) {
        if (!success) {
            _revert(returndata);
        } else {
            // only check if target is a contract if the call was successful and the return data is empty
            // otherwise we already know that it was a contract
            if (returndata.length == 0 && target.code.length == 0) {
                revert AddressEmptyCode(target);
            }
            return returndata;
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
     * revert reason or with a default {Errors.FailedCall} error.
     */
    function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
        if (!success) {
            _revert(returndata);
        } else {
            return returndata;
        }
    }

    /**
     * @dev Reverts with returndata if present. Otherwise reverts with {Errors.FailedCall}.
     */
    function _revert(bytes memory returndata) private pure {
        // Look for revert reason and bubble it up if present
        if (returndata.length > 0) {
            // The easiest way to bubble the revert reason is using memory via assembly
            /// @solidity memory-safe-assembly
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert Errors.FailedCall();
        }
    }
}

File 10 of 14 : Context.sol
// 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;
    }
}

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

pragma solidity ^0.8.20;

import {IERC20} from "../token/ERC20/IERC20.sol";

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

pragma solidity ^0.8.20;

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

File 13 of 14 : Errors.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.20;

/**
 * @dev Collection of common custom errors used in multiple contracts
 *
 * IMPORTANT: Backwards compatibility is not guaranteed in future versions of the library.
 * It is recommended to avoid relying on the error API for critical functionality.
 */
library Errors {
    /**
     * @dev The ETH balance of the account is not enough to perform the operation.
     */
    error InsufficientBalance(uint256 balance, uint256 needed);

    /**
     * @dev A call to an address target failed. The target may have reverted.
     */
    error FailedCall();

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

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

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC-165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[ERC].
 *
 * 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[ERC 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);
}

Settings
{
  "remappings": [
    "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
    "ds-test/=lib/openzeppelin-contracts/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/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "cancun",
  "viaIR": false,
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[],"name":"AdventureDisabled","type":"error"},{"inputs":[],"name":"AdventureWrongPayment","type":"error"},{"inputs":[],"name":"AmountBelowThreshold","type":"error"},{"inputs":[],"name":"CannotRenounceOwnership","type":"error"},{"inputs":[],"name":"CooldownLock","type":"error"},{"inputs":[],"name":"FailedCall","type":"error"},{"inputs":[{"internalType":"uint256","name":"balance","type":"uint256"},{"internalType":"uint256","name":"needed","type":"uint256"}],"name":"InsufficientBalance","type":"error"},{"inputs":[],"name":"InsufficientStakedBalance","type":"error"},{"inputs":[],"name":"InvalidAmount","type":"error"},{"inputs":[],"name":"MinStakingAmountConstraintViolation","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":"ReentrancyGuardReentrantCall","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"inputs":[],"name":"StakingDisabled","type":"error"},{"inputs":[],"name":"StakingGuard","type":"error"},{"inputs":[],"name":"TransferOutFailed","type":"error"},{"inputs":[],"name":"WithdrawFailed","type":"error"},{"inputs":[],"name":"WithdrawLock","type":"error"},{"inputs":[],"name":"ZeroAddress","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"adventurer","type":"address"},{"indexed":false,"internalType":"uint256","name":"actionTimestamp","type":"uint256"},{"indexed":false,"internalType":"uint16","name":"loopCount","type":"uint16"},{"indexed":false,"internalType":"uint8","name":"loopState","type":"uint8"}],"name":"Adventure","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"enabled","type":"bool"}],"name":"AdventureToggled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"staker","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Deposited","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"address","name":"asset","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"ERC20TokenSwept","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"MinStakingAmountChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"NativeTokenSwept","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferStarted","type":"event"},{"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":false,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Received","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"enabled","type":"bool"}],"name":"StakingToggled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"staker","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Withdrew","type":"event"},{"inputs":[],"name":"ADVENTURE_MODE_PRICE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"BASE_COOLDOWN_UNIT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"COOLDOWN_BASE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"COOLDOWN_MIN","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"COOLDOWN_REDUCTION_MAX","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"FINAL_LOOP_STATE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MIN_STAKING_AMOUNT_CONSTRAINT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ONE_MINUTE_BOOST","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"WITHDRAW_TIME_LOCK","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"adventure","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"userAddress","type":"address"}],"name":"adventureGameStats","outputs":[{"internalType":"uint256","name":"loopFinishedTimestamp","type":"uint256"},{"internalType":"uint16","name":"loopCount","type":"uint16"},{"internalType":"uint8","name":"loopState","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"changeMinStakingAmount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"collectPayments","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"computeCooldown","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"deposit","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getAdventureGameStats","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint16","name":"","type":"uint16"},{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getStakingInfo","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isAdventureEnabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isStakingEnabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minStakingAmount","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":"pendingOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"userAddress","type":"address"}],"name":"stakingData","outputs":[{"internalType":"uint256","name":"stakedAmount","type":"uint256"},{"internalType":"uint256","name":"lastDepositTimestamp","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"address","name":"asset","type":"address"}],"name":"sweepToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"toggleAdventure","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"toggleStaking","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"totalStaked","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":"uint256","name":"amount","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]

608060405234801561000f575f80fd5b506040516200161138038062001611833981016040819052610030916100f5565b806001600160a01b03811661005e57604051631e4fbdf760e01b81525f600482015260240160405180910390fd5b6100678161008a565b50506004805461ffff191661010117905568056bc75e2d63100000600255610122565b600180546001600160a01b03191690556100a3816100a6565b50565b5f80546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b5f60208284031215610105575f80fd5b81516001600160a01b038116811461011b575f80fd5b9392505050565b6114e180620001305f395ff3fe6080604052600436106101d3575f3560e01c8063809c5ef1116100fd578063c73e35f411610092578063e29a937011610062578063e29a937014610551578063e30c39781461056a578063eca5113914610587578063f2fde38b146105ce575f80fd5b8063c73e35f4146104e2578063d0e30db0146104fc578063d1c754a214610504578063e20869f114610523575f80fd5b8063b00cbdec116100cd578063b00cbdec146104b1578063bc1f846f146104b1578063be3d6781146104c6578063c4f89745146104ce575f80fd5b8063809c5ef1146103ec578063817b1cd2146104385780638da5cb5b1461044d578063aa4704f31461047d575f80fd5b80633fbbaba011610173578063715018a611610143578063715018a61461038c57806376dfd50e146103a057806379ba5097146103bc5780637a69d9cf146103d0575f80fd5b80633fbbaba0146103245780634375995d1461033a5780635100699c146103595780635611f1e214610378575f80fd5b80632b92879b116101ae5780632b92879b1461027b5780632cc26f29146102dd5780632e1a7d4d146102f15780633b8105b314610310575f80fd5b8063019fdc551461021e578063258836fe1461023457806325c065b114610253575f80fd5b3661021a576101e06105ed565b604080513381523460208201527f88a5966d370b9919b20f3e2c13ff65706f196a4e32cc2c12bf57088f88525874910160405180910390a1005b5f80fd5b348015610229575f80fd5b506102326106da565b005b34801561023f575f80fd5b5061023261024e366004611315565b610744565b34801561025e575f80fd5b5061026860025481565b6040519081526020015b60405180910390f35b348015610286575f80fd5b506102bc610295366004611346565b60066020525f90815260409020805460019091015461ffff81169062010000900460ff1683565b6040805193845261ffff909216602084015260ff1690820152606001610272565b3480156102e8575f80fd5b50610268600381565b3480156102fc575f80fd5b5061023261030b36600461135f565b61083b565b34801561031b575f80fd5b506102326109fd565b34801561032f575f80fd5b506102686201518081565b348015610345575f80fd5b5061023261035436600461135f565b610a4d565b348015610364575f80fd5b50610232610373366004611376565b610aba565b348015610383575f80fd5b50610268610bed565b348015610397575f80fd5b50610232610bfc565b3480156103ab575f80fd5b50610268681b1ae4d6e2ef50000081565b3480156103c7575f80fd5b50610232610c1d565b3480156103db575f80fd5b506102686801e5b8fa8fe2ac000081565b3480156103f7575f80fd5b5061040b610406366004611346565b610c63565b6040516102729493929190938452602084019290925261ffff16604083015260ff16606082015260800190565b348015610443575f80fd5b5061026860035481565b348015610458575f80fd5b505f546001600160a01b03165b6040516001600160a01b039091168152602001610272565b348015610488575f80fd5b5061049c610497366004611346565b610cd8565b60408051928352901515602083015201610272565b3480156104bc575f80fd5b50610268610e1081565b610232610d29565b3480156104d9575f80fd5b50610268603c81565b3480156104ed575f80fd5b5061026866470de4df82000081565b6102326105ed565b34801561050f575f80fd5b5061026861051e366004611346565b610ee5565b34801561052e575f80fd5b5060045461054190610100900460ff1681565b6040519015158152602001610272565b34801561055c575f80fd5b506004546105419060ff1681565b348015610575575f80fd5b506001546001600160a01b0316610465565b348015610592575f80fd5b506105b96105a1366004611346565b60056020525f90815260409020805460019091015482565b60408051928352602083019190915201610272565b3480156105d9575f80fd5b506102326105e8366004611346565b610f61565b60045460ff16610610576040516343e2e81560e11b815260040160405180910390fd5b60025434903390821015610637576040516354ada46f60e01b815260040160405180910390fd5b6001600160a01b0381165f908152600560205260408120805484929061065e9084906113b2565b90915550506001600160a01b0381165f90815260056020526040812042600190910155600380548492906106939084906113b2565b90915550506040518281526001600160a01b038216907f2da466a7b24304f47e87fa2e1e5a81b9831ce54fec19055ce277ca2f39ba42c49060200160405180910390a25050565b6106e2610fd1565b6004805460ff610100808304821615810261ff001990931692909217928390556040517f1e3ab8298917d9c28260d8132e3784a52fb999bbe1e6ac323b72441945c164ff9361073a9390049091161515815260200190565b60405180910390a1565b61074c610fd1565b6001600160a01b0382166107735760405163d92e233d60e01b815260040160405180910390fd5b6040516370a0823160e01b815230600482015281905f906001600160a01b038316906370a0823190602401602060405180830381865afa1580156107b9573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906107dd91906113c5565b90506107ea828583610fff565b604080516001600160a01b038087168252851660208201529081018290527ff4317f71dd1519821bc4a55421345e5fb9b87882bc2e8d7a1c04fd576abdd1109060600160405180910390a150505050565b610843611056565b805f036108635760405163162908e360e11b815260040160405180910390fd5b335f8181526005602090815260409182902082518084019093528054835260010154908201819052620151809061089a90426113dc565b10156108b957604051633ff8e95960e01b815260040160405180910390fd5b80518311156108db576040516345a5c39960e11b815260040160405180910390fd5b60025481518491906108ee9083906113dc565b10156108f8575080515b6001600160a01b0383165f908152600560205260408120805483929061091f9084906113dc565b925050819055508060035f82825461093791906113dc565b90915550506040515f906001600160a01b0385169083908381818185875af1925050503d805f8114610984576040519150601f19603f3d011682016040523d82523d5f602084013e610989565b606091505b50509050806109ab57604051631d42c86760e21b815260040160405180910390fd5b836001600160a01b03167fb244b9a17ad633c6e83b7983ee04320484956a68ddbe96a0b70dfca1cf19d723836040516109e691815260200190565b60405180910390a2505050506109fa6110c3565b50565b610a05610fd1565b6004805460ff8082161560ff1990921682179092556040519116151581527f886bf152398e89a7de17f229134a054c5448ada42007eaae5187b82753278be69060200161073a565b610a55610fd1565b681b1ae4d6e2ef500000811115610a7f57604051636472c0bd60e01b815260040160405180910390fd5b60028190556040518181527fcc0cac2fe6e951d2648de0d413fd25e5e685a6551fef63029ba3e28374e1631b9060200160405180910390a150565b610ac2610fd1565b6001600160a01b038216610ae95760405163d92e233d60e01b815260040160405180910390fd5b805f03610b095760405163162908e360e11b815260040160405180910390fd5b600354610b1690476113dc565b811115610b3657604051630bd8030760e41b815260040160405180910390fd5b5f826001600160a01b0316826040515f6040518083038185875af1925050503d805f8114610b7f576040519150601f19603f3d011682016040523d82523d5f602084013e610b84565b606091505b5050905080610ba657604051631d42c86760e21b815260040160405180910390fd5b604080516001600160a01b0385168152602081018490527fdb38a5f6571d0068fff8ada9a839c473a98f4c61a3d12524f365392a99d9a1c9910160405180910390a1505050565b610bf9610e10806113dc565b81565b610c04610fd1565b6040516377aeb0ad60e01b815260040160405180910390fd5b60015433906001600160a01b03168114610c5a5760405163118cdaa760e01b81526001600160a01b03821660048201526024015b60405180910390fd5b6109fa816110ed565b6001600160a01b0381165f90815260066020908152604080832081516060810183528154815260019091015461ffff8116938201939093526201000090920460ff169082015281908190819081610cb987610ee5565b8251602084015160409094015190999198509296509194509092505050565b6001600160a01b0381165f90815260056020908152604080832081518083019092528054825260010154918101829052829182906201518090610d1b90426113dc565b925196921094509092505050565b600454610100900460ff16610d51576040516306aad43f60e21b815260040160405180910390fd5b3466470de4df8200008114610d79576040516326d8fc8d60e01b815260040160405180910390fd5b335f81815260066020908152604080832081516060810183528154815260019091015461ffff8116938201939093526201000090920460ff16908201524291610dc184610ee5565b8251610dcd90856113dc565b1090508015610def576040516363aac31560e11b815260040160405180910390fd5b6003826040015160ff1603610e2457600182602001818151610e1191906113ef565b61ffff1690525060016040830152610e53565b600182604001818151610e379190611411565b60ff908116909152604084015116600219019050610e53578282525b6001600160a01b0384165f818152600660209081526040918290208551815585820151600190910180548785015161ffff90931662ffffff1990911681176201000060ff90941693840217909155835188815292830152918101919091527f8e96fce97a63d112d16be5825863e68d6f248069d8b683cb8cab89b99f34a2319060600160405180910390a25050505050565b6001600160a01b0381165f90815260056020526040812054600254811015610f115750610e1092915050565b5f6801e5b8fa8fe2ac0000610f27603c8461142a565b610f319190611441565b9050610e108111610f4d57610f4881610e106113dc565b610f59565b610f59610e10806113dc565b949350505050565b610f69610fd1565b600180546001600160a01b0383166001600160a01b03199091168117909155610f995f546001600160a01b031690565b6001600160a01b03167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270060405160405180910390a350565b5f546001600160a01b03163314610ffd5760405163118cdaa760e01b8152336004820152602401610c51565b565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b179052611051908490611106565b505050565b7f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f005c1561109657604051633ee5aeb560e01b815260040160405180910390fd5b610ffd60017f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f005b90611167565b610ffd5f7f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f006110bd565b600180546001600160a01b03191690556109fa8161116e565b5f61111a6001600160a01b038416836111bd565b905080515f1415801561113e57508080602001905181019061113c9190611460565b155b1561105157604051635274afe760e01b81526001600160a01b0384166004820152602401610c51565b80825d5050565b5f80546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b60606111ca83835f6111d3565b90505b92915050565b6060814710156111ff5760405163cf47918160e01b815247600482015260248101839052604401610c51565b5f80856001600160a01b0316848660405161121a919061147f565b5f6040518083038185875af1925050503d805f8114611254576040519150601f19603f3d011682016040523d82523d5f602084013e611259565b606091505b5091509150611269868383611275565b925050505b9392505050565b60608261128a57611285826112d1565b61126e565b81511580156112a157506001600160a01b0384163b155b156112ca57604051639996b31560e01b81526001600160a01b0385166004820152602401610c51565b508061126e565b8051156112e15780518082602001fd5b60405163d6bda27560e01b815260040160405180910390fd5b80356001600160a01b0381168114611310575f80fd5b919050565b5f8060408385031215611326575f80fd5b61132f836112fa565b915061133d602084016112fa565b90509250929050565b5f60208284031215611356575f80fd5b6111ca826112fa565b5f6020828403121561136f575f80fd5b5035919050565b5f8060408385031215611387575f80fd5b611390836112fa565b946020939093013593505050565b634e487b7160e01b5f52601160045260245ffd5b808201808211156111cd576111cd61139e565b5f602082840312156113d5575f80fd5b5051919050565b818103818111156111cd576111cd61139e565b61ffff81811683821601908082111561140a5761140a61139e565b5092915050565b60ff81811683821601908111156111cd576111cd61139e565b80820281158282048414176111cd576111cd61139e565b5f8261145b57634e487b7160e01b5f52601260045260245ffd5b500490565b5f60208284031215611470575f80fd5b8151801515811461126e575f80fd5b5f82515f5b8181101561149e5760208186018101518583015201611484565b505f92019182525091905056fea264697066735822122018fb26f23e738532335bd4390f54eec0f53da8c27a366cc8e38f8ad37ce99d4e64736f6c634300081800330000000000000000000000003a6768214990ca8dbd31adca10df142f4aa82b59

Deployed Bytecode

0x6080604052600436106101d3575f3560e01c8063809c5ef1116100fd578063c73e35f411610092578063e29a937011610062578063e29a937014610551578063e30c39781461056a578063eca5113914610587578063f2fde38b146105ce575f80fd5b8063c73e35f4146104e2578063d0e30db0146104fc578063d1c754a214610504578063e20869f114610523575f80fd5b8063b00cbdec116100cd578063b00cbdec146104b1578063bc1f846f146104b1578063be3d6781146104c6578063c4f89745146104ce575f80fd5b8063809c5ef1146103ec578063817b1cd2146104385780638da5cb5b1461044d578063aa4704f31461047d575f80fd5b80633fbbaba011610173578063715018a611610143578063715018a61461038c57806376dfd50e146103a057806379ba5097146103bc5780637a69d9cf146103d0575f80fd5b80633fbbaba0146103245780634375995d1461033a5780635100699c146103595780635611f1e214610378575f80fd5b80632b92879b116101ae5780632b92879b1461027b5780632cc26f29146102dd5780632e1a7d4d146102f15780633b8105b314610310575f80fd5b8063019fdc551461021e578063258836fe1461023457806325c065b114610253575f80fd5b3661021a576101e06105ed565b604080513381523460208201527f88a5966d370b9919b20f3e2c13ff65706f196a4e32cc2c12bf57088f88525874910160405180910390a1005b5f80fd5b348015610229575f80fd5b506102326106da565b005b34801561023f575f80fd5b5061023261024e366004611315565b610744565b34801561025e575f80fd5b5061026860025481565b6040519081526020015b60405180910390f35b348015610286575f80fd5b506102bc610295366004611346565b60066020525f90815260409020805460019091015461ffff81169062010000900460ff1683565b6040805193845261ffff909216602084015260ff1690820152606001610272565b3480156102e8575f80fd5b50610268600381565b3480156102fc575f80fd5b5061023261030b36600461135f565b61083b565b34801561031b575f80fd5b506102326109fd565b34801561032f575f80fd5b506102686201518081565b348015610345575f80fd5b5061023261035436600461135f565b610a4d565b348015610364575f80fd5b50610232610373366004611376565b610aba565b348015610383575f80fd5b50610268610bed565b348015610397575f80fd5b50610232610bfc565b3480156103ab575f80fd5b50610268681b1ae4d6e2ef50000081565b3480156103c7575f80fd5b50610232610c1d565b3480156103db575f80fd5b506102686801e5b8fa8fe2ac000081565b3480156103f7575f80fd5b5061040b610406366004611346565b610c63565b6040516102729493929190938452602084019290925261ffff16604083015260ff16606082015260800190565b348015610443575f80fd5b5061026860035481565b348015610458575f80fd5b505f546001600160a01b03165b6040516001600160a01b039091168152602001610272565b348015610488575f80fd5b5061049c610497366004611346565b610cd8565b60408051928352901515602083015201610272565b3480156104bc575f80fd5b50610268610e1081565b610232610d29565b3480156104d9575f80fd5b50610268603c81565b3480156104ed575f80fd5b5061026866470de4df82000081565b6102326105ed565b34801561050f575f80fd5b5061026861051e366004611346565b610ee5565b34801561052e575f80fd5b5060045461054190610100900460ff1681565b6040519015158152602001610272565b34801561055c575f80fd5b506004546105419060ff1681565b348015610575575f80fd5b506001546001600160a01b0316610465565b348015610592575f80fd5b506105b96105a1366004611346565b60056020525f90815260409020805460019091015482565b60408051928352602083019190915201610272565b3480156105d9575f80fd5b506102326105e8366004611346565b610f61565b60045460ff16610610576040516343e2e81560e11b815260040160405180910390fd5b60025434903390821015610637576040516354ada46f60e01b815260040160405180910390fd5b6001600160a01b0381165f908152600560205260408120805484929061065e9084906113b2565b90915550506001600160a01b0381165f90815260056020526040812042600190910155600380548492906106939084906113b2565b90915550506040518281526001600160a01b038216907f2da466a7b24304f47e87fa2e1e5a81b9831ce54fec19055ce277ca2f39ba42c49060200160405180910390a25050565b6106e2610fd1565b6004805460ff610100808304821615810261ff001990931692909217928390556040517f1e3ab8298917d9c28260d8132e3784a52fb999bbe1e6ac323b72441945c164ff9361073a9390049091161515815260200190565b60405180910390a1565b61074c610fd1565b6001600160a01b0382166107735760405163d92e233d60e01b815260040160405180910390fd5b6040516370a0823160e01b815230600482015281905f906001600160a01b038316906370a0823190602401602060405180830381865afa1580156107b9573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906107dd91906113c5565b90506107ea828583610fff565b604080516001600160a01b038087168252851660208201529081018290527ff4317f71dd1519821bc4a55421345e5fb9b87882bc2e8d7a1c04fd576abdd1109060600160405180910390a150505050565b610843611056565b805f036108635760405163162908e360e11b815260040160405180910390fd5b335f8181526005602090815260409182902082518084019093528054835260010154908201819052620151809061089a90426113dc565b10156108b957604051633ff8e95960e01b815260040160405180910390fd5b80518311156108db576040516345a5c39960e11b815260040160405180910390fd5b60025481518491906108ee9083906113dc565b10156108f8575080515b6001600160a01b0383165f908152600560205260408120805483929061091f9084906113dc565b925050819055508060035f82825461093791906113dc565b90915550506040515f906001600160a01b0385169083908381818185875af1925050503d805f8114610984576040519150601f19603f3d011682016040523d82523d5f602084013e610989565b606091505b50509050806109ab57604051631d42c86760e21b815260040160405180910390fd5b836001600160a01b03167fb244b9a17ad633c6e83b7983ee04320484956a68ddbe96a0b70dfca1cf19d723836040516109e691815260200190565b60405180910390a2505050506109fa6110c3565b50565b610a05610fd1565b6004805460ff8082161560ff1990921682179092556040519116151581527f886bf152398e89a7de17f229134a054c5448ada42007eaae5187b82753278be69060200161073a565b610a55610fd1565b681b1ae4d6e2ef500000811115610a7f57604051636472c0bd60e01b815260040160405180910390fd5b60028190556040518181527fcc0cac2fe6e951d2648de0d413fd25e5e685a6551fef63029ba3e28374e1631b9060200160405180910390a150565b610ac2610fd1565b6001600160a01b038216610ae95760405163d92e233d60e01b815260040160405180910390fd5b805f03610b095760405163162908e360e11b815260040160405180910390fd5b600354610b1690476113dc565b811115610b3657604051630bd8030760e41b815260040160405180910390fd5b5f826001600160a01b0316826040515f6040518083038185875af1925050503d805f8114610b7f576040519150601f19603f3d011682016040523d82523d5f602084013e610b84565b606091505b5050905080610ba657604051631d42c86760e21b815260040160405180910390fd5b604080516001600160a01b0385168152602081018490527fdb38a5f6571d0068fff8ada9a839c473a98f4c61a3d12524f365392a99d9a1c9910160405180910390a1505050565b610bf9610e10806113dc565b81565b610c04610fd1565b6040516377aeb0ad60e01b815260040160405180910390fd5b60015433906001600160a01b03168114610c5a5760405163118cdaa760e01b81526001600160a01b03821660048201526024015b60405180910390fd5b6109fa816110ed565b6001600160a01b0381165f90815260066020908152604080832081516060810183528154815260019091015461ffff8116938201939093526201000090920460ff169082015281908190819081610cb987610ee5565b8251602084015160409094015190999198509296509194509092505050565b6001600160a01b0381165f90815260056020908152604080832081518083019092528054825260010154918101829052829182906201518090610d1b90426113dc565b925196921094509092505050565b600454610100900460ff16610d51576040516306aad43f60e21b815260040160405180910390fd5b3466470de4df8200008114610d79576040516326d8fc8d60e01b815260040160405180910390fd5b335f81815260066020908152604080832081516060810183528154815260019091015461ffff8116938201939093526201000090920460ff16908201524291610dc184610ee5565b8251610dcd90856113dc565b1090508015610def576040516363aac31560e11b815260040160405180910390fd5b6003826040015160ff1603610e2457600182602001818151610e1191906113ef565b61ffff1690525060016040830152610e53565b600182604001818151610e379190611411565b60ff908116909152604084015116600219019050610e53578282525b6001600160a01b0384165f818152600660209081526040918290208551815585820151600190910180548785015161ffff90931662ffffff1990911681176201000060ff90941693840217909155835188815292830152918101919091527f8e96fce97a63d112d16be5825863e68d6f248069d8b683cb8cab89b99f34a2319060600160405180910390a25050505050565b6001600160a01b0381165f90815260056020526040812054600254811015610f115750610e1092915050565b5f6801e5b8fa8fe2ac0000610f27603c8461142a565b610f319190611441565b9050610e108111610f4d57610f4881610e106113dc565b610f59565b610f59610e10806113dc565b949350505050565b610f69610fd1565b600180546001600160a01b0383166001600160a01b03199091168117909155610f995f546001600160a01b031690565b6001600160a01b03167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270060405160405180910390a350565b5f546001600160a01b03163314610ffd5760405163118cdaa760e01b8152336004820152602401610c51565b565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b179052611051908490611106565b505050565b7f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f005c1561109657604051633ee5aeb560e01b815260040160405180910390fd5b610ffd60017f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f005b90611167565b610ffd5f7f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f006110bd565b600180546001600160a01b03191690556109fa8161116e565b5f61111a6001600160a01b038416836111bd565b905080515f1415801561113e57508080602001905181019061113c9190611460565b155b1561105157604051635274afe760e01b81526001600160a01b0384166004820152602401610c51565b80825d5050565b5f80546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b60606111ca83835f6111d3565b90505b92915050565b6060814710156111ff5760405163cf47918160e01b815247600482015260248101839052604401610c51565b5f80856001600160a01b0316848660405161121a919061147f565b5f6040518083038185875af1925050503d805f8114611254576040519150601f19603f3d011682016040523d82523d5f602084013e611259565b606091505b5091509150611269868383611275565b925050505b9392505050565b60608261128a57611285826112d1565b61126e565b81511580156112a157506001600160a01b0384163b155b156112ca57604051639996b31560e01b81526001600160a01b0385166004820152602401610c51565b508061126e565b8051156112e15780518082602001fd5b60405163d6bda27560e01b815260040160405180910390fd5b80356001600160a01b0381168114611310575f80fd5b919050565b5f8060408385031215611326575f80fd5b61132f836112fa565b915061133d602084016112fa565b90509250929050565b5f60208284031215611356575f80fd5b6111ca826112fa565b5f6020828403121561136f575f80fd5b5035919050565b5f8060408385031215611387575f80fd5b611390836112fa565b946020939093013593505050565b634e487b7160e01b5f52601160045260245ffd5b808201808211156111cd576111cd61139e565b5f602082840312156113d5575f80fd5b5051919050565b818103818111156111cd576111cd61139e565b61ffff81811683821601908082111561140a5761140a61139e565b5092915050565b60ff81811683821601908111156111cd576111cd61139e565b80820281158282048414176111cd576111cd61139e565b5f8261145b57634e487b7160e01b5f52601260045260245ffd5b500490565b5f60208284031215611470575f80fd5b8151801515811461126e575f80fd5b5f82515f5b8181101561149e5760208186018101518583015201611484565b505f92019182525091905056fea264697066735822122018fb26f23e738532335bd4390f54eec0f53da8c27a366cc8e38f8ad37ce99d4e64736f6c63430008180033

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

0000000000000000000000003a6768214990ca8dbd31adca10df142f4aa82b59

-----Decoded View---------------
Arg [0] : owner (address): 0x3a6768214990cA8dBD31adCa10df142f4AA82b59

-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 0000000000000000000000003a6768214990ca8dbd31adca10df142f4aa82b59


Block Transaction 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
[ 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.