ETH Price: $2,940.99 (-0.57%)

Contract

0x441536bd2d9766Aa4E0DD5d5c8F0EcB3F6BE5432

Overview

ETH Balance

0 ETH

ETH Value

$0.00

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Block
From
To
Exchange310951552025-12-17 14:18:0938 days ago1765981089IN
0x441536bd...3F6BE5432
0 ETH0.000006110.04525
Exchange282726002025-12-02 9:07:4353 days ago1764666463IN
0x441536bd...3F6BE5432
0 ETH0.000004570.04525
Exchange282725842025-12-02 9:07:3653 days ago1764666456IN
0x441536bd...3F6BE5432
0 ETH0.000005440.04525
Exchange282709232025-12-02 8:55:1953 days ago1764665719IN
0x441536bd...3F6BE5432
0 ETH0.000004730.04525
Exchange282701132025-12-02 8:49:0353 days ago1764665343IN
0x441536bd...3F6BE5432
0 ETH0.00000560.04525
Exchange282691342025-12-02 8:41:1053 days ago1764664870IN
0x441536bd...3F6BE5432
0 ETH0.000004570.04525
Exchange282686932025-12-02 8:37:4753 days ago1764664667IN
0x441536bd...3F6BE5432
0 ETH0.000004570.04525
Exchange282683752025-12-02 8:35:3153 days ago1764664531IN
0x441536bd...3F6BE5432
0 ETH0.000005690.04525
Exchange282646682025-12-02 8:05:5053 days ago1764662750IN
0x441536bd...3F6BE5432
0 ETH0.000005440.04525
Exchange282627302025-12-02 7:49:1453 days ago1764661754IN
0x441536bd...3F6BE5432
0 ETH0.000005440.04525
Exchange282599572025-12-02 7:23:5453 days ago1764660234IN
0x441536bd...3F6BE5432
0 ETH0.000004730.04525
Exchange282596842025-12-02 7:21:0253 days ago1764660062IN
0x441536bd...3F6BE5432
0 ETH0.000005440.04525
Exchange282561632025-12-02 6:45:2453 days ago1764657924IN
0x441536bd...3F6BE5432
0 ETH0.000004730.04525
Exchange282561342025-12-02 6:45:1053 days ago1764657910IN
0x441536bd...3F6BE5432
0 ETH0.00000560.04525
Exchange282544482025-12-02 6:29:2053 days ago1764656960IN
0x441536bd...3F6BE5432
0 ETH0.000005690.04525
Exchange282514632025-12-02 5:58:0253 days ago1764655082IN
0x441536bd...3F6BE5432
0 ETH0.000004970.04525
Exchange282514612025-12-02 5:58:0153 days ago1764655081IN
0x441536bd...3F6BE5432
0 ETH0.000006320.04525
Exchange282415372025-12-02 3:50:3154 days ago1764647431IN
0x441536bd...3F6BE5432
0 ETH0.000006530.04525
Exchange282369442025-12-02 2:49:4554 days ago1764643785IN
0x441536bd...3F6BE5432
0 ETH0.00000590.04525
Exchange282338492025-12-02 2:12:4754 days ago1764641567IN
0x441536bd...3F6BE5432
0 ETH0.000004690.04525
Exchange282338262025-12-02 2:12:2954 days ago1764641549IN
0x441536bd...3F6BE5432
0 ETH0.000004690.04525
Exchange282336612025-12-02 2:10:4554 days ago1764641445IN
0x441536bd...3F6BE5432
0 ETH0.000004690.04525
Exchange282335802025-12-02 2:09:5454 days ago1764641394IN
0x441536bd...3F6BE5432
0 ETH0.000004690.04525
Exchange282335552025-12-02 2:09:3454 days ago1764641374IN
0x441536bd...3F6BE5432
0 ETH0.000004690.04525
Exchange282335282025-12-02 2:09:1254 days ago1764641352IN
0x441536bd...3F6BE5432
0 ETH0.000004690.04525
View all transactions

Latest 1 internal transaction

Advanced mode:
Parent Transaction Hash Block From To
208334392025-10-08 7:33:29108 days ago1759908809  Contract Creation0 ETH
Cross-Chain Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
PenguToPoints

Compiler Version
v0.8.28+commit.7893614a

ZkSolc Version
v1.5.15

Optimization Enabled:
Yes with Mode 3

Other Settings:
paris EvmVersion
// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;

import {ContractOwnership} from "@animoca/ethereum-contracts/contracts/access/ContractOwnership.sol";
import {PayoutWallet} from "@animoca/ethereum-contracts/contracts/payment/PayoutWallet.sol";
import {PayoutWalletStorage} from "@animoca/ethereum-contracts/contracts/payment/libraries/PayoutWalletStorage.sol";
import {SafeERC20, IERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {IPointsV2} from "../points/interface/IPointsV2.sol";

contract PenguToPoints is PayoutWallet {
    using SafeERC20 for IERC20;
    using PayoutWalletStorage for PayoutWalletStorage.Layout;

    uint256 public constant EXCHANGE_RATE = 100; // 1 PENGU = 100 Points
    bytes32 public constant DEPOSIT_REASON = keccak256("PenguToPoints deposit");

    IERC20 public immutable PENGU_TOKEN;
    IPointsV2 public immutable POINTS_V2;

    event Exchanged(address indexed user, uint256 penguAmount, uint256 pointsAmount);

    error InvalidPointsAmount();

    constructor(IERC20 penguToken, IPointsV2 pointsV2, address payable payoutAddress) ContractOwnership(msg.sender) PayoutWallet(payoutAddress) {
        PENGU_TOKEN = penguToken;
        POINTS_V2 = pointsV2;
    }

    /// @notice Exchanges PENGU tokens for Points.
    /// @dev Reverts with {InvalidPointsAmount} if pointsAmount is zero.
    /// @param pointsAmount the amount of Points to exchange.
    function exchange(uint256 pointsAmount) external {
        require(pointsAmount != 0, InvalidPointsAmount());

        uint256 penguAmount = (pointsAmount * 1 ether) / EXCHANGE_RATE;

        PENGU_TOKEN.safeTransferFrom(msg.sender, PayoutWalletStorage.layout().payoutWallet(), penguAmount);
        POINTS_V2.deposit(msg.sender, pointsAmount, DEPOSIT_REASON);

        emit Exchanged(msg.sender, penguAmount, pointsAmount);
    }
}

// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.28;

interface IPointsV2 {
    /// @notice Emitted when an amount is deposited to a balance.
    /// @param depositor The depositor.
    /// @param reasonCode The reason code of the deposit.
    /// @param holder The holder of the balance deposited to.
    /// @param amount The amount deposited.
    event Deposited(address indexed depositor, bytes32 indexed reasonCode, address indexed holder, uint256 amount);

    /// @notice Emitted when an approval is made.
    /// @param holder The holder of the balance.
    /// @param spender The spender allowed to spend the balance.
    /// @param amount The amount approved.
    event Approval(address indexed holder, address indexed spender, uint256 amount);

    /// @notice Emitted when an amount is spent from a balance.
    /// @param spender The spender of the balance.
    /// @param holder The holder of the balance spent from.
    /// @param amount The amount spent.
    event Spent(address indexed spender, address indexed holder, uint256 amount);

    /// @notice Deposits an amount to a holder's balance for a given reason code.
    /// @dev Emits a {Deposited} event.
    /// @param holder The holder of the balance to deposit to.
    /// @param amount The amount to deposit.
    /// @param depositReasonCode The reason code for the deposit.
    function deposit(address holder, uint256 amount, bytes32 depositReasonCode) external;

    /// @notice Approves a spender to spend an amount from the caller's balance.
    /// @dev Emits an {Approval} event.
    /// @param spender The spender allowed to spend the balance.
    /// @param amount The amount approved.
    function approve(address spender, uint256 amount) external;

    /// @notice Approves a spender to spend an amount from a holder's balance using a signature.
    /// @dev Emits an {Approval} event.
    /// @param holder The holder of the balance.
    /// @param spender The spender allowed to spend the balance.
    /// @param amount The amount approved.
    /// @param deadline The deadline timestamp by which the signature must be submitted.
    /// @param signature The signature of the approval.
    function approveWithSignature(address holder, address spender, uint256 amount, uint256 deadline, bytes calldata signature) external;

    /// @notice Spends an amount from a holder's balance.
    /// @dev Emits an {Approval} event if the caller is not the holder.
    /// @dev Emits a {Spent} event.
    /// @param holder The holder of the balance to spend from.
    /// @param amount The amount to spend.
    function spendFrom(address holder, uint256 amount) external;

    /// @notice Spends an amount and calls a target contract with data.
    /// @dev Emits a {Spent} event.
    /// @param amount The amount to spend.
    /// @param target The target contract to call.
    /// @param data The data to call the target contract with.
    function spendAndCall(uint256 amount, address target, bytes calldata data) external;
}

File 3 of 30 : ContractOwnership.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

import {ContractOwnershipStorage} from "./libraries/ContractOwnershipStorage.sol";
import {ContractOwnershipBase} from "./base/ContractOwnershipBase.sol";
import {InterfaceDetection} from "./../introspection/InterfaceDetection.sol";

/// @title ERC173 Contract Ownership Standard (immutable version).
/// @dev See https://eips.ethereum.org/EIPS/eip-173
/// @dev This contract is to be used via inheritance in an immutable (non-proxied) implementation.
abstract contract ContractOwnership is ContractOwnershipBase, InterfaceDetection {
    using ContractOwnershipStorage for ContractOwnershipStorage.Layout;

    /// @notice Initializes the storage with an initial contract owner.
    /// @notice Marks the following ERC165 interface(s) as supported: ERC173.
    /// @dev Emits an {OwnershipTransferred} if `initialOwner` is not the zero address.
    /// @param initialOwner the initial contract owner.
    constructor(address initialOwner) {
        ContractOwnershipStorage.layout().constructorInit(initialOwner);
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

import {ZeroAddressPayoutWallet} from "./../errors/PayoutWalletErrors.sol";
import {PayoutWalletSet} from "./../events/PayoutWalletEvents.sol";
import {ProxyInitialization} from "./../../proxy/libraries/ProxyInitialization.sol";

library PayoutWalletStorage {
    using PayoutWalletStorage for PayoutWalletStorage.Layout;

    struct Layout {
        address payable wallet;
    }

    bytes32 internal constant LAYOUT_STORAGE_SLOT = bytes32(uint256(keccak256("animoca.core.payment.PayoutWallet.storage")) - 1);
    bytes32 internal constant PROXY_INIT_PHASE_SLOT = bytes32(uint256(keccak256("animoca.core.payment.PayoutWallet.phase")) - 1);

    /// @notice Initializes the storage with an initial payout wallet (immutable version).
    /// @dev Note: This function should be called ONLY in the constructor of an immutable (non-proxied) contract.
    /// @dev Reverts with {ZeroAddressPayoutWallet} if `initialPayoutWallet` is the zero address.
    /// @dev Emits a {PayoutWalletSet} event.
    /// @param initialPayoutWallet The initial payout wallet.
    function constructorInit(Layout storage s, address payable initialPayoutWallet) internal {
        s.setPayoutWallet(initialPayoutWallet);
    }

    /// @notice Initializes the storage with an initial payout wallet (proxied version).
    /// @notice Sets the proxy initialization phase to `1`.
    /// @dev Note: This function should be called ONLY in the init function of a proxied contract.
    /// @dev Reverts with {InitializationPhaseAlreadyReached} if the proxy initialization phase is set to `1` or above.
    /// @dev Reverts with {ZeroAddressPayoutWallet} if `initialPayoutWallet` is the zero address.
    /// @dev Emits a {PayoutWalletSet} event.
    /// @param initialPayoutWallet The initial payout wallet.
    function proxyInit(Layout storage s, address payable initialPayoutWallet) internal {
        ProxyInitialization.setPhase(PROXY_INIT_PHASE_SLOT, 1);
        s.setPayoutWallet(initialPayoutWallet);
    }

    /// @notice Sets the payout wallet.
    /// @dev Reverts with {ZeroAddressPayoutWallet} if `newPayoutWallet` is the zero address.
    /// @dev Emits a {PayoutWalletSet} event.
    /// @param newPayoutWallet The payout wallet.
    function setPayoutWallet(Layout storage s, address payable newPayoutWallet) internal {
        if (newPayoutWallet == address(0)) revert ZeroAddressPayoutWallet();
        s.wallet = newPayoutWallet;
        emit PayoutWalletSet(newPayoutWallet);
    }

    /// @notice Gets the payout wallet.
    /// @return wallet The payout wallet.
    function payoutWallet(Layout storage s) internal view returns (address payable) {
        return s.wallet;
    }

    function layout() internal pure returns (Layout storage s) {
        bytes32 position = LAYOUT_STORAGE_SLOT;
        assembly {
            s.slot := position
        }
    }
}

File 5 of 30 : PayoutWallet.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

import {PayoutWalletStorage} from "./libraries/PayoutWalletStorage.sol";
import {PayoutWalletBase} from "./base/PayoutWalletBase.sol";
import {ContractOwnership} from "../access/ContractOwnership.sol";

/// @title Payout wallet (immutable version).
/// @dev This contract is to be used via inheritance in an immutable (non-proxied) implementation.
abstract contract PayoutWallet is PayoutWalletBase, ContractOwnership {
    using PayoutWalletStorage for PayoutWalletStorage.Layout;

    /// @notice Initializes the storage with an initial payout wallet.
    /// @dev Reverts with {ZeroAddressPayoutWallet} if `initialPayoutWallet` is the zero address.
    /// @dev Emits a {PayoutWalletSet} event.
    /// @param initialPayoutWallet The initial payout wallet.
    constructor(address payable initialPayoutWallet) {
        PayoutWalletStorage.layout().constructorInit(initialPayoutWallet);
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.2.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../IERC20.sol";
import {IERC1363} from "../../../interfaces/IERC1363.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 {
    /**
     * @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.
     *
     * IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
     * smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
     * this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
     * that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
     */
    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.
     *
     * IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
     * smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
     * this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
     * that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
     */
    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.
     *
     * NOTE: If the token implements ERC-7674, this function will not modify any temporary allowance. This function
     * only sets the "standard" allowance. Any temporary allowance will remain active, in addition to the value being
     * set here.
     */
    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).
     *
     * This is a variant of {_callOptionalReturnBool} that reverts if call fails to meet the requirements.
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        uint256 returnSize;
        uint256 returnValue;
        assembly ("memory-safe") {
            let success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
            // bubble errors
            if iszero(success) {
                let ptr := mload(0x40)
                returndatacopy(ptr, 0, returndatasize())
                revert(ptr, returndatasize())
            }
            returnSize := returndatasize()
            returnValue := mload(0)
        }

        if (returnSize == 0 ? address(token).code.length == 0 : returnValue != 1) {
            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 silently catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        bool success;
        uint256 returnSize;
        uint256 returnValue;
        assembly ("memory-safe") {
            success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
            returnSize := returndatasize()
            returnValue := mload(0)
        }
        return success && (returnSize == 0 ? address(token).code.length > 0 : returnValue == 1);
    }
}

File 7 of 30 : InterfaceDetection.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

import {IERC165} from "./interfaces/IERC165.sol";
import {InterfaceDetectionStorage} from "./libraries/InterfaceDetectionStorage.sol";

/// @title ERC165 Interface Detection Standard (immutable or proxiable version).
/// @dev This contract is to be used via inheritance in an immutable (non-proxied) or proxied implementation.
abstract contract InterfaceDetection is IERC165 {
    using InterfaceDetectionStorage for InterfaceDetectionStorage.Layout;

    /// @inheritdoc IERC165
    function supportsInterface(bytes4 interfaceId) external view returns (bool) {
        return InterfaceDetectionStorage.layout().supportsInterface(interfaceId);
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

import {NotContractOwner, NotTargetContractOwner} from "./../errors/ContractOwnershipErrors.sol";
import {TargetIsNotAContract} from "./../errors/Common.sol";
import {OwnershipTransferred} from "./../events/ERC173Events.sol";
import {IERC173} from "./../interfaces/IERC173.sol";
import {Address} from "./../../utils/libraries/Address.sol";
import {ProxyInitialization} from "./../../proxy/libraries/ProxyInitialization.sol";
import {InterfaceDetectionStorage} from "./../../introspection/libraries/InterfaceDetectionStorage.sol";

library ContractOwnershipStorage {
    using Address for address;
    using ContractOwnershipStorage for ContractOwnershipStorage.Layout;
    using InterfaceDetectionStorage for InterfaceDetectionStorage.Layout;

    struct Layout {
        address contractOwner;
    }

    bytes32 internal constant LAYOUT_STORAGE_SLOT = bytes32(uint256(keccak256("animoca.core.access.ContractOwnership.storage")) - 1);
    bytes32 internal constant PROXY_INIT_PHASE_SLOT = bytes32(uint256(keccak256("animoca.core.access.ContractOwnership.phase")) - 1);

    /// @notice Initializes the storage with an initial contract owner (immutable version).
    /// @notice Marks the following ERC165 interface(s) as supported: ERC173.
    /// @dev Note: This function should be called ONLY in the constructor of an immutable (non-proxied) contract.
    /// @dev Emits an {OwnershipTransferred} if `initialOwner` is not the zero address.
    /// @param initialOwner The initial contract owner.
    function constructorInit(Layout storage s, address initialOwner) internal {
        if (initialOwner != address(0)) {
            s.contractOwner = initialOwner;
            emit OwnershipTransferred(address(0), initialOwner);
        }
        InterfaceDetectionStorage.layout().setSupportedInterface(type(IERC173).interfaceId, true);
    }

    /// @notice Initializes the storage with an initial contract owner (proxied version).
    /// @notice Sets the proxy initialization phase to `1`.
    /// @notice Marks the following ERC165 interface(s) as supported: ERC173.
    /// @dev Note: This function should be called ONLY in the init function of a proxied contract.
    /// @dev Reverts with {InitializationPhaseAlreadyReached} if the proxy initialization phase is set to `1` or above.
    /// @dev Emits an {OwnershipTransferred} if `initialOwner` is not the zero address.
    /// @param initialOwner The initial contract owner.
    function proxyInit(Layout storage s, address initialOwner) internal {
        ProxyInitialization.setPhase(PROXY_INIT_PHASE_SLOT, 1);
        s.constructorInit(initialOwner);
    }

    /// @notice Sets the address of the new contract owner.
    /// @dev Reverts with {NotContractOwner} if `sender` is not the contract owner.
    /// @dev Emits an {OwnershipTransferred} event if `newOwner` is different from the current contract owner.
    /// @param newOwner The address of the new contract owner. Using the zero address means renouncing ownership.
    function transferOwnership(Layout storage s, address sender, address newOwner) internal {
        address previousOwner = s.contractOwner;
        if (sender != previousOwner) revert NotContractOwner(sender);
        if (previousOwner != newOwner) {
            s.contractOwner = newOwner;
            emit OwnershipTransferred(previousOwner, newOwner);
        }
    }

    /// @notice Gets the address of the contract owner.
    /// @return contractOwner The address of the contract owner.
    function owner(Layout storage s) internal view returns (address contractOwner) {
        return s.contractOwner;
    }

    /// @notice Checks whether an account is the owner of a target contract.
    /// @param targetContract The contract to check.
    /// @param account The account to check.
    /// @return isTargetContractOwner_ Whether `account` is the owner of `targetContract`.
    function isTargetContractOwner(address targetContract, address account) internal view returns (bool isTargetContractOwner_) {
        if (!targetContract.hasBytecode()) revert TargetIsNotAContract(targetContract);
        return IERC173(targetContract).owner() == account;
    }

    /// @notice Ensures that an account is the contract owner.
    /// @dev Reverts with {NotContractOwner} if `account` is not the contract owner.
    /// @param account The account.
    function enforceIsContractOwner(Layout storage s, address account) internal view {
        if (account != s.contractOwner) revert NotContractOwner(account);
    }

    /// @notice Enforces that an account is the owner of a target contract.
    /// @dev Reverts with {NotTheTargetContractOwner} if the account is not the owner.
    /// @param targetContract The contract to check.
    /// @param account The account to check.
    function enforceIsTargetContractOwner(address targetContract, address account) internal view {
        if (!isTargetContractOwner(targetContract, account)) revert NotTargetContractOwner(targetContract, account);
    }

    function layout() internal pure returns (Layout storage s) {
        bytes32 position = LAYOUT_STORAGE_SLOT;
        assembly {
            s.slot := position
        }
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

import {IERC173} from "./../interfaces/IERC173.sol";
import {ContractOwnershipStorage} from "./../libraries/ContractOwnershipStorage.sol";
import {Context} from "@openzeppelin/contracts/utils/Context.sol";

/// @title ERC173 Contract Ownership Standard (proxiable version).
/// @dev See https://eips.ethereum.org/EIPS/eip-173
/// @dev This contract is to be used via inheritance in a proxied implementation.
/// @dev Note: This contract requires ERC165 (Interface Detection Standard).
abstract contract ContractOwnershipBase is IERC173, Context {
    using ContractOwnershipStorage for ContractOwnershipStorage.Layout;

    /// @inheritdoc IERC173
    function owner() public view virtual returns (address) {
        return ContractOwnershipStorage.layout().owner();
    }

    /// @inheritdoc IERC173
    function transferOwnership(address newOwner) public virtual {
        ContractOwnershipStorage.layout().transferOwnership(_msgSender(), newOwner);
    }
}

File 10 of 30 : PayoutWalletErrors.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

/// @notice Thrown when setting the zero address as the payout wallet.
error ZeroAddressPayoutWallet();

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

import {InitializationPhaseAlreadyReached} from "./../errors/ProxyInitializationErrors.sol";
import {StorageSlot} from "@openzeppelin/contracts/utils/StorageSlot.sol";

/// @notice Multiple calls protection for storage-modifying proxy initialization functions.
library ProxyInitialization {
    /// @notice Sets the initialization phase during a storage-modifying proxy initialization function.
    /// @dev Reverts with {InitializationPhaseAlreadyReached} if `phase` has been reached already.
    /// @param storageSlot the storage slot where `phase` is stored.
    /// @param phase the initialization phase.
    function setPhase(bytes32 storageSlot, uint256 phase) internal {
        StorageSlot.Uint256Slot storage currentVersion = StorageSlot.getUint256Slot(storageSlot);
        uint256 currentPhase = currentVersion.value;
        if (currentPhase >= phase) revert InitializationPhaseAlreadyReached(currentPhase, phase);
        currentVersion.value = phase;
    }
}

File 12 of 30 : PayoutWalletEvents.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

/// @notice Emitted when the payout wallet address changes.
/// @param payoutWallet the new payout wallet address.
event PayoutWalletSet(address payoutWallet);

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

import {IPayoutWallet} from "./../interfaces/IPayoutWallet.sol";
import {ContractOwnershipStorage} from "./../../access/libraries/ContractOwnershipStorage.sol";
import {PayoutWalletStorage} from "./../libraries/PayoutWalletStorage.sol";
import {Context} from "@openzeppelin/contracts/utils/Context.sol";

/// @title Payout wallet (proxiable version).
/// @dev This contract is to be used via inheritance in a proxied implementation.
/// @dev Note: This contract requires ERC173 (Contract Ownership standard).
abstract contract PayoutWalletBase is IPayoutWallet, Context {
    using ContractOwnershipStorage for ContractOwnershipStorage.Layout;
    using PayoutWalletStorage for PayoutWalletStorage.Layout;

    /// @notice Sets the payout wallet.
    /// @dev Reverts with {NotContractOwner} if the sender is not the contract owner.
    /// @dev Reverts with {ZeroAddressPayoutWallet} if `newPayoutWallet` is the zero address.
    /// @dev Emits a {PayoutWalletSet} event.
    /// @param newPayoutWallet The payout wallet.
    function setPayoutWallet(address payable newPayoutWallet) external virtual {
        ContractOwnershipStorage.layout().enforceIsContractOwner(_msgSender());
        PayoutWalletStorage.layout().setPayoutWallet(newPayoutWallet);
    }

    /// @notice Gets the payout wallet.
    /// @return wallet The payout wallet.
    function payoutWallet() external view virtual returns (address payable wallet) {
        return PayoutWalletStorage.layout().payoutWallet();
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.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);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.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);
}

// 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;
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

/// @title ERC165 Interface Detection Standard.
/// @dev See https://eips.ethereum.org/EIPS/eip-165.
/// @dev Note: The ERC-165 identifier for this interface is 0x01ffc9a7.
interface IERC165 {
    /// @notice Returns whether this contract implements a given interface.
    /// @dev Note: This function call must use less than 30 000 gas.
    /// @param interfaceId the interface identifier to test.
    /// @return supported True if the interface is supported, false if `interfaceId` is `0xffffffff` or if the interface is not supported.
    function supportsInterface(bytes4 interfaceId) external view returns (bool supported);
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

import {IllegalInterfaceId} from "./../errors/InterfaceDetectionErrors.sol";
import {IERC165} from "./../interfaces/IERC165.sol";

library InterfaceDetectionStorage {
    struct Layout {
        mapping(bytes4 => bool) supportedInterfaces;
    }

    bytes32 internal constant LAYOUT_STORAGE_SLOT = bytes32(uint256(keccak256("animoca.core.introspection.InterfaceDetection.storage")) - 1);

    bytes4 internal constant ILLEGAL_INTERFACE_ID = 0xffffffff;

    /// @notice Sets or unsets an ERC165 interface.
    /// @dev Revertswith {IllegalInterfaceId} if `interfaceId` is `0xffffffff`.
    /// @param interfaceId the interface identifier.
    /// @param supported True to set the interface, false to unset it.
    function setSupportedInterface(Layout storage s, bytes4 interfaceId, bool supported) internal {
        if (interfaceId == ILLEGAL_INTERFACE_ID) revert IllegalInterfaceId();
        s.supportedInterfaces[interfaceId] = supported;
    }

    /// @notice Returns whether this contract implements a given interface.
    /// @dev Note: This function call must use less than 30 000 gas.
    /// @param interfaceId The interface identifier to test.
    /// @return supported True if the interface is supported, false if `interfaceId` is `0xffffffff` or if the interface is not supported.
    function supportsInterface(Layout storage s, bytes4 interfaceId) internal view returns (bool supported) {
        if (interfaceId == ILLEGAL_INTERFACE_ID) {
            return false;
        }
        if (interfaceId == type(IERC165).interfaceId) {
            return true;
        }
        return s.supportedInterfaces[interfaceId];
    }

    function layout() internal pure returns (Layout storage s) {
        bytes32 position = LAYOUT_STORAGE_SLOT;
        assembly {
            s.slot := position
        }
    }
}

File 19 of 30 : Common.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

/// @notice Thrown when the target contract is actually not a contract.
/// @param targetContract The contract that was checked
error TargetIsNotAContract(address targetContract);

File 20 of 30 : ContractOwnershipErrors.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

/// @notice Thrown when an account is not the contract owner but is required to.
/// @param account The account that was checked.
error NotContractOwner(address account);

/// @notice Thrown when an account is not the pending contract owner but is required to.
/// @param account The account that was checked.
error NotPendingContractOwner(address account);

/// @notice Thrown when an account is not the target contract owner but is required to.
/// @param targetContract The contract that was checked.
/// @param account The account that was checked.
error NotTargetContractOwner(address targetContract, address account);

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

library Address {
    /// @notice Checks if the address is a deployed smart contract.
    /// @param addr The address to check.
    /// @return hasBytecode True if `addr` is a deployed smart contract, false otherwise.
    function hasBytecode(address addr) internal view returns (bool) {
        uint256 size;
        assembly {
            size := extcodesize(addr)
        }
        return size != 0;
    }
}

File 22 of 30 : ERC173Events.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

/// @notice Emitted when the contract ownership changes.
/// @param previousOwner the previous contract owner.
/// @param newOwner the new contract owner.
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

/// @notice Emitted when a new contract owner is pending.
/// @param pendingOwner the address of the new contract owner.
event OwnershipTransferPending(address indexed pendingOwner);

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

/// @title ERC-173 Contract Ownership Standard (functions)
/// @dev See https://eips.ethereum.org/EIPS/eip-173
/// @dev Note: the ERC-165 identifier for this interface is 0x7f5828d0
interface IERC173 {
    /// @notice Sets the address of the new contract owner.
    /// @dev Reverts if the sender is not the contract owner.
    /// @dev Emits an {OwnershipTransferred} event if `newOwner` is different from the current contract owner.
    /// @param newOwner The address of the new contract owner. Using the zero address means renouncing ownership.
    function transferOwnership(address newOwner) external;

    /// @notice Gets the address of the contract owner.
    /// @return contractOwner The address of the contract owner.
    function owner() external view returns (address contractOwner);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/StorageSlot.sol)
// This file was procedurally generated from scripts/generate/templates/StorageSlot.js.

pragma solidity ^0.8.20;

/**
 * @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;
 *     }
 * }
 * ```
 *
 * 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) {
        assembly ("memory-safe") {
            r.slot := slot
        }
    }

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

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

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

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

    /**
     * @dev Returns a `StringSlot` with member `value` located at `slot`.
     */
    function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
        assembly ("memory-safe") {
            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) {
        assembly ("memory-safe") {
            r.slot := store.slot
        }
    }

    /**
     * @dev Returns a `BytesSlot` with member `value` located at `slot`.
     */
    function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
        assembly ("memory-safe") {
            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) {
        assembly ("memory-safe") {
            r.slot := store.slot
        }
    }
}

File 25 of 30 : ProxyInitializationErrors.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

/// @notice Emitted when trying to set a phase value that has already been reached.
/// @param currentPhase The current phase.
/// @param newPhase The new phase trying to be set.
error InitializationPhaseAlreadyReached(uint256 currentPhase, uint256 newPhase);

File 26 of 30 : 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 27 of 30 : 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 28 of 30 : IPayoutWallet.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

/// @title Payout wallet (functions)
interface IPayoutWallet {
    /// @notice Gets the payout wallet.
    /// @return wallet The payout wallet.
    function payoutWallet() external view returns (address payable wallet);
}

File 29 of 30 : InterfaceDetectionErrors.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

/// @notice Thrown when setting the illegal interfaceId 0xffffffff.
error IllegalInterfaceId();

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.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
{
  "optimizer": {
    "enabled": true,
    "mode": "3"
  },
  "evmVersion": "paris",
  "outputSelection": {
    "*": {
      "*": [
        "abi"
      ]
    }
  },
  "detectMissingLibraries": false,
  "forceEVMLA": false,
  "enableEraVMExtensions": false,
  "codegen": "yul",
  "libraries": {}
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"contract IERC20","name":"penguToken","type":"address"},{"internalType":"contract IPointsV2","name":"pointsV2","type":"address"},{"internalType":"address payable","name":"payoutAddress","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"IllegalInterfaceId","type":"error"},{"inputs":[],"name":"InvalidPointsAmount","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"NotContractOwner","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"inputs":[],"name":"ZeroAddressPayoutWallet","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"penguAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"pointsAmount","type":"uint256"}],"name":"Exchanged","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":"payoutWallet","type":"address"}],"name":"PayoutWalletSet","type":"event"},{"inputs":[],"name":"DEPOSIT_REASON","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"EXCHANGE_RATE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PENGU_TOKEN","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"POINTS_V2","outputs":[{"internalType":"contract IPointsV2","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"pointsAmount","type":"uint256"}],"name":"exchange","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"payoutWallet","outputs":[{"internalType":"address payable","name":"wallet","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address payable","name":"newPayoutWallet","type":"address"}],"name":"setPayoutWallet","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"}]

9c4d535b0000000000000000000000000000000000000000000000000000000000000000010000e12a6e198564a4aa04cbd780e05ffdf91815042c1a55cec80558427d8c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000600000000000000000000000009ebe3a824ca958e4b3da772d2065518f009cba620000000000000000000000002f65ffb50f16e3ee19a8d22606d9114d1ed66f2c0000000000000000000000009d5127495ba4eecdf5b60f9808a362404e1d39d6

Deployed Bytecode

0x000a0000000000020000006003100270000000a5033001970000000100200190000000380000c13d0000008002000039000000400020043f000000040030008c0000021b0000413d000000000401043b000000e004400270000000b40040009c0000008a0000213d000000bb0040009c000000b50000a13d000000bc0040009c000000e80000613d000000bd0040009c000000f80000613d000000be0040009c0000021b0000c13d0000000002000416000000000002004b0000021b0000c13d000000240030008c0000021b0000413d0000000401100370000000000101043b000000a80010009c0000021b0000213d000000a902000041000000000202041a000000a8032001970000000002000411000000000032004b000001610000c13d000000a802100198000000860000613d000000b003000041000000000403041a000000aa04400197000000000114019f000000000013041b000000800020043f0000000001000414000000a50010009c000000a501008041000000c001100210000000c6011001c70000800d020000390000000103000039000000b204000041028f02850000040f0000000100200190000000b30000c13d0000021b0000013d0000000002000416000000000002004b0000021b0000c13d0000001f02300039000000a602200197000000c002200039000000400020043f0000001f0430018f000000a705300198000000c002500039000000490000613d000000c006000039000000000701034f000000007807043c0000000006860436000000000026004b000000450000c13d000000000004004b000000560000613d000000000151034f0000000304400210000000000502043300000000054501cf000000000545022f000000000101043b0000010004400089000000000141022f00000000014101cf000000000151019f0000000000120435000000600030008c0000021b0000413d000000c00500043d000000a80050009c0000021b0000213d000000e00700043d000000a80070009c0000021b0000213d000001000800043d000000a80080009c0000021b0000213d0000000006000411000000000006004b0000007b0000613d000000a901000041000000000201041a000000aa02200197000000000262019f000000000021041b0000000001000414000000a50010009c000000a501008041000000c001100210000000ab011001c70000800d020000390000000303000039000000ac04000041000600000005001d0000000005000019000500000007001d000400000008001d028f02850000040f00000004080000290000000507000029000000060500002900000001002001900000021b0000613d000000a801800198000000ad02000041000000000020043f000000ae02000041000000200020043f000000af02000041000000000302041a000000db0330019700000001033001bf000000000032041b0000010c0000c13d000000c701000041000000000010043f000000c8010000410000029100010430000000b50040009c000000c40000a13d000000b60040009c000000ef0000613d000000b70040009c000001050000613d000000b80040009c0000021b0000c13d0000000002000416000000000002004b0000021b0000c13d000000240030008c0000021b0000413d0000000401100370000000000101043b000000a80010009c0000021b0000213d000000a902000041000000000202041a000000a8032001970000000005000411000000000035004b000001660000c13d000000a806100197000000000065004b000000b30000613d000000aa02200197000000000112019f000000a902000041000000000012041b0000000001000414000000a50010009c000000a501008041000000c001100210000000ab011001c70000800d020000390000000303000039000000ac04000041028f02850000040f00000001002001900000021b0000613d0000000001000019000002900001042e000000bf0040009c000000d60000613d000000c00040009c0000021b0000c13d0000000001000416000000000001004b0000021b0000c13d0000000001000412000a00000001001d000900000000003d0000800501000039000000440300003900000000040004150000000a0440008a000000d20000013d000000b90040009c000000e30000613d000000ba0040009c0000021b0000c13d0000000001000416000000000001004b0000021b0000c13d0000000001000412000800000001001d000700200000003d000080050100003900000044030000390000000004000415000000080440008a0000000504400210000000c502000041028f026c0000040f000000f40000013d0000000004000416000000000004004b0000021b0000c13d000000240030008c0000021b0000413d0000000401100370000000000101043b000000d7001001980000021b0000c13d000000d80010009c0000016b0000a13d0000000001000019000001870000013d0000000001000416000000000001004b0000021b0000c13d000000b001000041000000f30000013d0000000001000416000000000001004b0000021b0000c13d0000006401000039000000800010043f000000c401000041000002900001042e0000000001000416000000000001004b0000021b0000c13d000000a901000041000000000101041a000000a801100197000000800010043f000000c401000041000002900001042e0000000002000416000000000002004b0000021b0000c13d000000240030008c0000021b0000413d0000000401100370000000000201043b000000000002004b000001320000c13d000000d601000041000000000010043f000000c80100004100000291000104300000000001000416000000000001004b0000021b0000c13d000000c301000041000000800010043f000000c401000041000002900001042e000000b002000041000000000302041a000000aa03300197000000000383019f000000000032041b000000400200043d0000000000120435000000a50020009c000000a50200804100000040012002100000000002000414000000a50020009c000000a502008041000000c002200210000000000112019f000000b1011001c70000800d020000390000000103000039000000b204000041000600000005001d000500000007001d028f02850000040f0000000503000029000000060100002900000001002001900000021b0000613d000000800010043f000000a00030043f0000014000000443000001600010044300000020010000390000018000100443000001a000300443000001000010044300000002010000390000012000100443000000b301000041000002900001042e000000c9032000d100000000012300d9000000c90010009c0000016f0000c13d000600000003001d000500000002001d000000c50100004100000000001004430000000001000412000000040010044300000024000004430000000001000414000000a50010009c000000a501008041000000c001100210000000cb011001c70000800502000039028f028a0000040f00000001002001900000021d0000613d0000000602000029000000640420011a000000000201043b000000b001000041000000000501041a000000400100043d00000064031000390000000000430435000000a805500197000000440310003900000000005304350000002006100039000000cc03000041000000000036043500000064030000390000000000310435000000240310003900000000050004110000000000530435000000cd0010009c0000018e0000413d000000ca01000041000000000010043f0000004101000039000000040010043f000000c2010000410000029100010430000000c101000041000000000010043f000000040020043f000000c2010000410000029100010430000000c101000041000000000010043f000000040050043f000000c2010000410000029100010430000000d90010009c000001750000c13d0000000101000039000001870000013d000000ca01000041000000000010043f0000001101000039000000040010043f000000c2010000410000029100010430000000000010043f000000ae01000041000000200010043f0000000001000414000000a50010009c000000a501008041000000c001100210000000d3011001c70000801002000039028f028a0000040f00000001002001900000021b0000613d000000400200043d000000000101043b000000000101041a000000ff001001900000000001000039000000010100c039000000010110018f0000000000120435000000a50020009c000000a5020080410000004001200210000000da011001c7000002900001042e000400000004001d000000a003100039000000400030043f000000a50060009c000000a50600804100000040036002100000000001010433000000a50010009c000000a5010080410000006001100210000000000131019f0000000003000414000000a50030009c000000a503008041000000c003300210000000000131019f000600000002001d028f02850000040f0000006003100270000000a503300197000000200030008c000000200400003900000000040340190000001f0540018f0000002004400190000001ae0000613d000000000601034f0000000007000019000000006806043c0000000007870436000000000047004b000001aa0000c13d000000000005004b000001bb0000613d000000000641034f0000000305500210000000000704043300000000075701cf000000000757022f000000000606043b0000010005500089000000000656022f00000000055601cf000000000575019f000000000054043500000001002001900000000602000029000001d00000613d000000000003004b000001ee0000c13d000000ce010000410000000000100443000000a80120019700000004001004430000000001000414000000a50010009c000000a501008041000000c001100210000000cf011001c70000800202000039028f028a0000040f00000001002001900000021d0000613d000000000101043b0000000602000029000001f20000013d0000001f0530018f000000a706300198000000400200043d0000000004620019000001db0000613d000000000701034f0000000008020019000000007907043c0000000008980436000000000048004b000001d70000c13d000000000005004b000001e80000613d000000000161034f0000000305500210000000000604043300000000065601cf000000000656022f000000000101043b0000010005500089000000000151022f00000000015101cf000000000161019f00000000001404350000006001300210000000a50020009c000000a5020080410000004002200210000000000121019f0000029100010430000000000100043d000000010010008c00000000010000390000000101006039000000000001004b000001fa0000c13d000000d501000041000000000010043f000000a801200197000000040010043f000000c2010000410000029100010430000000c501000041000000000010044300000000010004120000000400100443000000200100003900000024001004430000000001000414000000a50010009c000000a501008041000000c001100210000000cb011001c70000800502000039028f028a0000040f00000001002001900000021d0000613d000000000101043b000000ce020000410000000000200443000000a801100197000600000001001d00000004001004430000000001000414000000a50010009c000000a501008041000000c001100210000000cf011001c70000800202000039028f028a0000040f00000001002001900000021d0000613d000000000101043b000000000001004b0000021e0000c13d00000000010000190000029100010430000000000001042f000000400300043d0000004401300039000000c3020000410000000000210435000000240130003900000005020000290000000000210435000000d0010000410000000001130436000100000001001d000000040130003900000000020004110000000000210435000000a50030009c000300000003001d000000a501000041000000000103401900020040001002180000000001000414000000a50010009c000000a501008041000000c00110021000000002011001af000000d1011001c70000000602000029028f02850000040f00000001002001900000024f0000613d0000000301000029000000d20010009c000000040200002900000000050004110000015b0000213d0000000303000029000000400030043f00000005010000290000000104000029000000000014043500000000002304350000000001000414000000a50010009c000000a501008041000000c00110021000000002011001af000000d3011001c70000800d020000390000000203000039000000d404000041000000340000013d00000060061002700000001f0460018f000000a705600198000000400200043d00000000035200190000025b0000613d000000000701034f0000000008020019000000007907043c0000000008980436000000000038004b000002570000c13d000000a506600197000000000004004b000002690000613d000000000151034f0000000304400210000000000503043300000000054501cf000000000545022f000000000101043b0000010004400089000000000141022f00000000014101cf000000000151019f00000000001304350000006001600210000001e90000013d000000000001042f00000000050100190000000000200443000000040100003900000005024002700000000002020031000000000121043a0000002004400039000000000031004b0000026f0000413d000000a50030009c000000a50300804100000060013002100000000002000414000000a50020009c000000a502008041000000c002200210000000000112019f000000dc011001c70000000002050019028f028a0000040f0000000100200190000002840000613d000000000101043b000000000001042d000000000001042f00000288002104210000000102000039000000000001042d0000000002000019000000000001042d0000028d002104230000000102000039000000000001042d0000000002000019000000000001042d0000028f00000432000002900001042e00000291000104300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffff00000000000000000000000000000000000000000000000000000001ffffffe000000000000000000000000000000000000000000000000000000000ffffffe0000000000000000000000000ffffffffffffffffffffffffffffffffffffffffc9ed16f33ab3a66c84bfd83099ccb2a8845871e2e1c1928f63797152f0fd54ccffffffffffffffffffffffff000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000008be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e07f5828d000000000000000000000000000000000000000000000000000000000ca9d3e17f264b0f3984e2634e94adb37fa3e6a8103f06aeae6fa59e21c769f5da53d5a615b1d5279f1a6647c5c0f31bf064b1e70af135676a4a96aeaeb691a2f4fb5f8cdfbce77f22b0931a39e9f476a60a0f66524b2f379e96485471eb700ad0200000000000000000000000000000000000020000000000000000000000000dd880d24a789958a6398c99c2e54ce31bc10a638cbc42f7dd34285479ae0f85500000002000000000000000000000000000000c0000001000000000000000000000000000000000000000000000000000000000000000000000000008488bb4d000000000000000000000000000000000000000000000000000000008da5cb5a000000000000000000000000000000000000000000000000000000008da5cb5b00000000000000000000000000000000000000000000000000000000f0d8727b00000000000000000000000000000000000000000000000000000000f2fde38b000000000000000000000000000000000000000000000000000000008488bb4e000000000000000000000000000000000000000000000000000000008aeede690000000000000000000000000000000000000000000000000000000014a8bd0c0000000000000000000000000000000000000000000000000000000014a8bd0d0000000000000000000000000000000000000000000000000000000053556559000000000000000000000000000000000000000000000000000000006b8f9c430000000000000000000000000000000000000000000000000000000001ffc9a700000000000000000000000000000000000000000000000000000000096e6ca52ef4875e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024000000000000000000000000ffcc520abd0da04e5b7e553245721f99c73c775d0ec4e6a8725944fa46b2fe6f0000000000000000000000000000000000000020000000800000000000000000310ab089e4439a4c15d089f94afb7896ff553aecb10793d0ab882de59d99a32e020000000000000000000000000000000000002000000080000000000000000040b539cf0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a76400004e487b7100000000000000000000000000000000000000000000000000000000020000020000000000000000000000000000004400000000000000000000000023b872dd00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffff601806aa1896bbf26568e884a7374b41e002500962caba6a15023a8d90e8508b83020000020000000000000000000000000000002400000000000000000000000026b3293f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffff020000000000000000000000000000000000004000000000000000000000000098e5a745dfe4f0a8952c2d09eeb25746ce9768004ab459c980d7ad1b5152cbd85274afe700000000000000000000000000000000000000000000000000000000b15e39140000000000000000000000000000000000000000000000000000000000000000fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffffff01ffc9a7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0002000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a26469706673582212208e31f8d1e65e9d7f27bc8cec2f00b5206589c54e5162375c678f5261239aeba364736f6c6378247a6b736f6c633a312e352e31353b736f6c633a302e382e32383b6c6c766d3a312e302e320055

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

0000000000000000000000009ebe3a824ca958e4b3da772d2065518f009cba620000000000000000000000002f65ffb50f16e3ee19a8d22606d9114d1ed66f2c0000000000000000000000009d5127495ba4eecdf5b60f9808a362404e1d39d6

-----Decoded View---------------
Arg [0] : penguToken (address): 0x9eBe3A824Ca958e4b3Da772D2065518F009CBa62
Arg [1] : pointsV2 (address): 0x2f65ffB50f16e3Ee19A8D22606D9114D1ED66f2C
Arg [2] : payoutAddress (address): 0x9d5127495ba4EEcdf5B60f9808a362404e1D39D6

-----Encoded View---------------
3 Constructor Arguments found :
Arg [0] : 0000000000000000000000009ebe3a824ca958e4b3da772d2065518f009cba62
Arg [1] : 0000000000000000000000002f65ffb50f16e3ee19a8d22606d9114d1ed66f2c
Arg [2] : 0000000000000000000000009d5127495ba4eecdf5b60f9808a362404e1d39d6


Block Transaction Gas Used Reward
view all blocks produced

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

Validator Index Block Amount
View All Withdrawals

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

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