ETH Price: $3,500.20 (+5.73%)

Contract

0x43B381975a7a7B7BCE39d22C12C4AB101C12D134
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Set Winner210377102024-10-24 20:00:1133 days ago1729800011IN
0x43B38197...01C12D134
0 ETH0.001484959.6324146
Buy Entry210376962024-10-24 19:57:2333 days ago1729799843IN
0x43B38197...01C12D134
0 ETH0.0011006911
Buy Entry210376922024-10-24 19:56:3533 days ago1729799795IN
0x43B38197...01C12D134
0 ETH0.0010960410.95357789
Buy Entry210376792024-10-24 19:53:5933 days ago1729799639IN
0x43B38197...01C12D134
0 ETH0.0010006310
Buy Entry210376672024-10-24 19:51:3533 days ago1729799495IN
0x43B38197...01C12D134
0 ETH0.0010292610.28619396
Buy Entry210376502024-10-24 19:48:1133 days ago1729799291IN
0x43B38197...01C12D134
0 ETH0.0010006310
Buy Entry210376482024-10-24 19:47:4733 days ago1729799267IN
0x43B38197...01C12D134
0 ETH0.0011275811.26877764
Buy Entry210376042024-10-24 19:38:5933 days ago1729798739IN
0x43B38197...01C12D134
0 ETH0.0010126210.11983143
Buy Entry210375642024-10-24 19:30:5933 days ago1729798259IN
0x43B38197...01C12D134
0 ETH0.000944319.43718274
Buy Entry210375232024-10-24 19:22:4733 days ago1729797767IN
0x43B38197...01C12D134
0 ETH0.000899658.99088659
Buy Entry210375232024-10-24 19:22:4733 days ago1729797767IN
0x43B38197...01C12D134
0 ETH0.001047710.47049012
Buy Entry210375162024-10-24 19:21:2333 days ago1729797683IN
0x43B38197...01C12D134
0 ETH0.0010766810.76004988
Buy Entry210374812024-10-24 19:14:2333 days ago1729797263IN
0x43B38197...01C12D134
0 ETH0.0013612613.60406038
Buy Entry210374482024-10-24 19:07:4733 days ago1729796867IN
0x43B38197...01C12D134
0 ETH0.0014429814.42080355
Buy Entry210373942024-10-24 18:56:5933 days ago1729796219IN
0x43B38197...01C12D134
0 ETH0.0010970510.96363697
Buy Entry210373222024-10-24 18:42:2333 days ago1729795343IN
0x43B38197...01C12D134
0 ETH0.0011109111.10214661
Buy Entry210372732024-10-24 18:32:3533 days ago1729794755IN
0x43B38197...01C12D134
0 ETH0.0010832910.82610148
Buy Entry210372452024-10-24 18:26:5933 days ago1729794419IN
0x43B38197...01C12D134
0 ETH0.0014167814.15888001
Buy Entry210372092024-10-24 18:19:4733 days ago1729793987IN
0x43B38197...01C12D134
0 ETH0.0011218911.21193385
Buy Entry210371982024-10-24 18:17:3533 days ago1729793855IN
0x43B38197...01C12D134
0 ETH0.0010854710.84787422
Buy Entry210371712024-10-24 18:11:5933 days ago1729793519IN
0x43B38197...01C12D134
0 ETH0.0010857910.85112596
Buy Entry210370192024-10-24 17:41:3533 days ago1729791695IN
0x43B38197...01C12D134
0 ETH0.0013931813.92309146
Buy Entry210369832024-10-24 17:34:2333 days ago1729791263IN
0x43B38197...01C12D134
0 ETH0.0015018615.00917397
Buy Entry210369652024-10-24 17:30:4733 days ago1729791047IN
0x43B38197...01C12D134
0 ETH0.0012942112.93401514
Buy Entry210369462024-10-24 17:26:5933 days ago1729790819IN
0x43B38197...01C12D134
0 ETH0.0011909911.90241954
View all transactions

Latest 2 internal transactions

Advanced mode:
Parent Transaction Hash Block From To
209844102024-10-17 9:32:1141 days ago1729157531
0x43B38197...01C12D134
0.0001 ETH
200278862024-06-05 20:01:59174 days ago1717617719
0x43B38197...01C12D134
5.956 ETH
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
ERC20Comp

Compiler Version
v0.8.24+commit.e11b9ed9

Optimization Enabled:
Yes with 200 runs

Other Settings:
paris EvmVersion
File 1 of 22 : ERC20Comp.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.22;

import "../lib/BaseERC20Extension.sol";

/// @title Raffles manager (single winner and ERC20 as prize)
/// @author Camden Grieh
/// @notice It consumes QRNG from API3. It has the role
/// "operator" that is the one used by a backend app to make some calls
/// @dev It saves in an ordered array the player wallet and the current
/// entries count. So buying entries has a complexity of O(1)
/// For calculating the winner, from the huge random number generated by Api3
/// a normalized random is generated by using the module method, adding 1 to have
/// a random from 1 to entriesCount.
/// So next step is to perform a binary search on the ordered array to get the
/// player O(log n)
/// Example:
/// 0 -> { 1, player1} as player1 buys 1 entry
/// 1 -> {51, player2} as player2 buys 50 entries
/// 2 -> {52, player3} as player3 buys 1 entry
/// 3 -> {53, player4} as player4 buys 1 entry
/// 4 -> {153, player5} as player5 buys 100 entries
/// So the setWinner method performs a binary search on that sorted array to get the upper bound.
/// If the random number generated is 150, the winner is player5. If the random number is 20, winner is player2

contract ERC20Comp is BaseERC20Extension {
    constructor(address _airnodeRrp, address _usdcAddress) BaseCompetition(_airnodeRrp) BaseERC20Extension(_usdcAddress) {}

    //////////////////////////////////////////////

    /// @param _desiredFundsInWeis the amount the seller would like to get from the raffle
    /// @param _collateralAddress The address of the NFT of the raffle
    /// @param _collateralId The id of the NFT (ERC721)
    /// @param _minimumFundsInWeis The mininum amount required for the raffle to set a winner
    /// @param _prices Array of prices and amount of entries the customer could purchase
    //   /// @param _commissionInBasicPoints commission for the platform, in basic points
    /// @notice Creates a raffle
    /// @dev creates a raffle struct and push it to the raffles array. Some data is stored in the funding data structure
    /// sends an event when finished
    /// @return raffleId
    function createRaffle(
        uint128 _desiredFundsInWeis,
        address _prizeAddress,
        uint256 _prizeNumber,
        uint128 _minimumFundsInWeis,
        PriceStructure[] calldata _prices,
        uint48 _commissionInBasicPoints,
        ENTRY_TYPE _entryType
    ) external onlyRole(OPERATOR_ROLE) returns (uint256) {
        if (_commissionInBasicPoints > 5000)
            revert CreateRaffleError("commission too high");
    
        _createRaffleActions(
            _desiredFundsInWeis,
            _prizeAddress,
            _prizeNumber,
            _minimumFundsInWeis,
            _prices,
            _commissionInBasicPoints,
            _entryType
        );
        _saveEntryInfo();

        uint256 idRaffle = raffles.length - 1;
        return idRaffle;
    }

    /*
    Callable only by the owner of the NFT
    Once the operator has created the raffle, he can stake the NFT
    At this moment, the NFT is locked and the players can buy entries
    */
    /// @param _raffleId Id of the raffle
    /// @notice The owner of the NFT can stake it on the raffle. At this moment the raffle starts and can sell entries to players
    /// @dev the owner must have approved this contract before. Otherwise will revert when transferring from the owner
    function stake(uint256 _raffleId) external payable {
        RaffleStruct storage raffle = raffles[_raffleId];
        EntryInfoStruct storage entryInfo = rafflesEntryInfo[_raffleId];
        // Check if the raffle is already created
        require(entryInfo.status == STATUS.CREATED, "Raffle not CREATED");
        // the owner of the NFT must be the current caller
        IERC20 token = IERC20(raffle.prizeAddress);
        require(
            token.allowance(msg.sender, address(this)) >= raffle.prizeNumber,
            "Allowance Error"
        );

        entryInfo.status = STATUS.ACCEPTED;
        raffle.seller = msg.sender;

        // transfer the asset to the contract
        token.transferFrom(msg.sender, address(this), raffle.prizeNumber); // transfer the token to the contract

        emit RaffleStarted(_raffleId, msg.sender);
    }

    // The operator can call this method once they receive the event "RandomNumberCreated"
    // triggered by the QRNG v1 consumer contract (RandomNumber.sol)
    /// @param _raffleId Id of the raffle
    /// @param _normalizedRandomNumber index of the array that contains the winner of the raffle. Generated by api3
    /// @notice it is the method that sets the winner and transfers funds and nft
    /// @dev called by Api3 callback
    function _transferPrizesAndFunds(
        uint256 _raffleId,
        uint256 _normalizedRandomNumber
    ) internal override {
        RaffleStruct memory raffle = _transferPrizesAndFundsActions(
            _raffleId,
            _normalizedRandomNumber
        );
        _transferERC20To(raffle.prizeAddress, raffle.prizeNumber, raffle.winner);
    }

    /// @param _raffleId Id of the raffle
    /// @dev The operator can cancel the raffle. The NFT is sent back to the seller
    /// The raised funds are send to the destination wallet. The buyers will
    /// be refunded offchain in the metawin wallet
    function cancelRaffle(
        uint256 _raffleId
    ) external override onlyRole(OPERATOR_ROLE) {
        _cancelRaffleActions(_raffleId, 2);
    }

    /// @dev callable by players. Depending on the number of entries assigned to the price structure the player buys (_id parameter)
    /// one or more entries will be assigned to the player.
    /// Also it is checked the maximum number of entries per user is not reached
    /// As the method is payable, in msg.value there will be the amount paid by the user
    /// @notice If the operator set requiredNFTs when creating the raffle, only the owners of nft on that collection can make a call to this method. This will be
    /// used for special raffles
    /// @param _raffleId: id of the raffle
    /// @param _id: id of the price structure
    function buyEntry(uint256 _raffleId, uint256 _id) external payable virtual {
        EntryInfoStruct memory entryInfo = _buyEntryActions(_raffleId, _id);
        _checkWalletsCap(entryInfo, _raffleId, _id);
    }
}

File 2 of 22 : IAirnodeRrpV0.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "./IAuthorizationUtilsV0.sol";
import "./ITemplateUtilsV0.sol";
import "./IWithdrawalUtilsV0.sol";

interface IAirnodeRrpV0 is
    IAuthorizationUtilsV0,
    ITemplateUtilsV0,
    IWithdrawalUtilsV0
{
    event SetSponsorshipStatus(
        address indexed sponsor,
        address indexed requester,
        bool sponsorshipStatus
    );

    event MadeTemplateRequest(
        address indexed airnode,
        bytes32 indexed requestId,
        uint256 requesterRequestCount,
        uint256 chainId,
        address requester,
        bytes32 templateId,
        address sponsor,
        address sponsorWallet,
        address fulfillAddress,
        bytes4 fulfillFunctionId,
        bytes parameters
    );

    event MadeFullRequest(
        address indexed airnode,
        bytes32 indexed requestId,
        uint256 requesterRequestCount,
        uint256 chainId,
        address requester,
        bytes32 endpointId,
        address sponsor,
        address sponsorWallet,
        address fulfillAddress,
        bytes4 fulfillFunctionId,
        bytes parameters
    );

    event FulfilledRequest(
        address indexed airnode,
        bytes32 indexed requestId,
        bytes data
    );

    event FailedRequest(
        address indexed airnode,
        bytes32 indexed requestId,
        string errorMessage
    );

    function setSponsorshipStatus(address requester, bool sponsorshipStatus)
        external;

    function makeTemplateRequest(
        bytes32 templateId,
        address sponsor,
        address sponsorWallet,
        address fulfillAddress,
        bytes4 fulfillFunctionId,
        bytes calldata parameters
    ) external returns (bytes32 requestId);

    function makeFullRequest(
        address airnode,
        bytes32 endpointId,
        address sponsor,
        address sponsorWallet,
        address fulfillAddress,
        bytes4 fulfillFunctionId,
        bytes calldata parameters
    ) external returns (bytes32 requestId);

    function fulfill(
        bytes32 requestId,
        address airnode,
        address fulfillAddress,
        bytes4 fulfillFunctionId,
        bytes calldata data,
        bytes calldata signature
    ) external returns (bool callSuccess, bytes memory callData);

    function fail(
        bytes32 requestId,
        address airnode,
        address fulfillAddress,
        bytes4 fulfillFunctionId,
        string calldata errorMessage
    ) external;

    function sponsorToRequesterToSponsorshipStatus(
        address sponsor,
        address requester
    ) external view returns (bool sponsorshipStatus);

    function requesterToRequestCountPlusOne(address requester)
        external
        view
        returns (uint256 requestCountPlusOne);

    function requestIsAwaitingFulfillment(bytes32 requestId)
        external
        view
        returns (bool isAwaitingFulfillment);
}

File 3 of 22 : IAuthorizationUtilsV0.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IAuthorizationUtilsV0 {
    function checkAuthorizationStatus(
        address[] calldata authorizers,
        address airnode,
        bytes32 requestId,
        bytes32 endpointId,
        address sponsor,
        address requester
    ) external view returns (bool status);

    function checkAuthorizationStatuses(
        address[] calldata authorizers,
        address airnode,
        bytes32[] calldata requestIds,
        bytes32[] calldata endpointIds,
        address[] calldata sponsors,
        address[] calldata requesters
    ) external view returns (bool[] memory statuses);
}

File 4 of 22 : ITemplateUtilsV0.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface ITemplateUtilsV0 {
    event CreatedTemplate(
        bytes32 indexed templateId,
        address airnode,
        bytes32 endpointId,
        bytes parameters
    );

    function createTemplate(
        address airnode,
        bytes32 endpointId,
        bytes calldata parameters
    ) external returns (bytes32 templateId);

    function getTemplates(bytes32[] calldata templateIds)
        external
        view
        returns (
            address[] memory airnodes,
            bytes32[] memory endpointIds,
            bytes[] memory parameters
        );

    function templates(bytes32 templateId)
        external
        view
        returns (
            address airnode,
            bytes32 endpointId,
            bytes memory parameters
        );
}

File 5 of 22 : IWithdrawalUtilsV0.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IWithdrawalUtilsV0 {
    event RequestedWithdrawal(
        address indexed airnode,
        address indexed sponsor,
        bytes32 indexed withdrawalRequestId,
        address sponsorWallet
    );

    event FulfilledWithdrawal(
        address indexed airnode,
        address indexed sponsor,
        bytes32 indexed withdrawalRequestId,
        address sponsorWallet,
        uint256 amount
    );

    function requestWithdrawal(address airnode, address sponsorWallet) external;

    function fulfillWithdrawal(
        bytes32 withdrawalRequestId,
        address airnode,
        address sponsor
    ) external payable;

    function sponsorToWithdrawalRequestCount(address sponsor)
        external
        view
        returns (uint256 withdrawalRequestCount);
}

File 6 of 22 : RrpRequesterV0.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "../interfaces/IAirnodeRrpV0.sol";

/// @title The contract to be inherited to make Airnode RRP requests
contract RrpRequesterV0 {
    IAirnodeRrpV0 public immutable airnodeRrp;

    /// @dev Reverts if the caller is not the Airnode RRP contract.
    /// Use it as a modifier for fulfill and error callback methods, but also
    /// check `requestId`.
    modifier onlyAirnodeRrp() {
        require(msg.sender == address(airnodeRrp), "Caller not Airnode RRP");
        _;
    }

    /// @dev Airnode RRP address is set at deployment and is immutable.
    /// RrpRequester is made its own sponsor by default. RrpRequester can also
    /// be sponsored by others and use these sponsorships while making
    /// requests, i.e., using this default sponsorship is optional.
    /// @param _airnodeRrp Airnode RRP contract address
    constructor(address _airnodeRrp) {
        airnodeRrp = IAirnodeRrpV0(_airnodeRrp);
        IAirnodeRrpV0(_airnodeRrp).setSponsorshipStatus(address(this), true);
    }
}

File 7 of 22 : AccessControl.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/AccessControl.sol)

pragma solidity ^0.8.20;

import {IAccessControl} from "./IAccessControl.sol";
import {Context} from "../utils/Context.sol";
import {ERC165} from "../utils/introspection/ERC165.sol";

/**
 * @dev Contract module that allows children to implement role-based access
 * control mechanisms. This is a lightweight version that doesn't allow enumerating role
 * members except through off-chain means by accessing the contract event logs. Some
 * applications may benefit from on-chain enumerability, for those cases see
 * {AccessControlEnumerable}.
 *
 * Roles are referred to by their `bytes32` identifier. These should be exposed
 * in the external API and be unique. The best way to achieve this is by
 * using `public constant` hash digests:
 *
 * ```solidity
 * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
 * ```
 *
 * Roles can be used to represent a set of permissions. To restrict access to a
 * function call, use {hasRole}:
 *
 * ```solidity
 * function foo() public {
 *     require(hasRole(MY_ROLE, msg.sender));
 *     ...
 * }
 * ```
 *
 * Roles can be granted and revoked dynamically via the {grantRole} and
 * {revokeRole} functions. Each role has an associated admin role, and only
 * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
 *
 * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
 * that only accounts with this role will be able to grant or revoke other
 * roles. More complex role relationships can be created by using
 * {_setRoleAdmin}.
 *
 * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
 * grant and revoke this role. Extra precautions should be taken to secure
 * accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}
 * to enforce additional security measures for this role.
 */
abstract contract AccessControl is Context, IAccessControl, ERC165 {
    struct RoleData {
        mapping(address account => bool) hasRole;
        bytes32 adminRole;
    }

    mapping(bytes32 role => RoleData) private _roles;

    bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;

    /**
     * @dev Modifier that checks that an account has a specific role. Reverts
     * with an {AccessControlUnauthorizedAccount} error including the required role.
     */
    modifier onlyRole(bytes32 role) {
        _checkRole(role);
        _;
    }

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
    }

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) public view virtual returns (bool) {
        return _roles[role].hasRole[account];
    }

    /**
     * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `_msgSender()`
     * is missing `role`. Overriding this function changes the behavior of the {onlyRole} modifier.
     */
    function _checkRole(bytes32 role) internal view virtual {
        _checkRole(role, _msgSender());
    }

    /**
     * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `account`
     * is missing `role`.
     */
    function _checkRole(bytes32 role, address account) internal view virtual {
        if (!hasRole(role, account)) {
            revert AccessControlUnauthorizedAccount(account, role);
        }
    }

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) public view virtual returns (bytes32) {
        return _roles[role].adminRole;
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     *
     * May emit a {RoleGranted} event.
     */
    function grantRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
        _grantRole(role, account);
    }

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     *
     * May emit a {RoleRevoked} event.
     */
    function revokeRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
        _revokeRole(role, account);
    }

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been revoked `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `callerConfirmation`.
     *
     * May emit a {RoleRevoked} event.
     */
    function renounceRole(bytes32 role, address callerConfirmation) public virtual {
        if (callerConfirmation != _msgSender()) {
            revert AccessControlBadConfirmation();
        }

        _revokeRole(role, callerConfirmation);
    }

    /**
     * @dev Sets `adminRole` as ``role``'s admin role.
     *
     * Emits a {RoleAdminChanged} event.
     */
    function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
        bytes32 previousAdminRole = getRoleAdmin(role);
        _roles[role].adminRole = adminRole;
        emit RoleAdminChanged(role, previousAdminRole, adminRole);
    }

    /**
     * @dev Attempts to grant `role` to `account` and returns a boolean indicating if `role` was granted.
     *
     * Internal function without access restriction.
     *
     * May emit a {RoleGranted} event.
     */
    function _grantRole(bytes32 role, address account) internal virtual returns (bool) {
        if (!hasRole(role, account)) {
            _roles[role].hasRole[account] = true;
            emit RoleGranted(role, account, _msgSender());
            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Attempts to revoke `role` to `account` and returns a boolean indicating if `role` was revoked.
     *
     * Internal function without access restriction.
     *
     * May emit a {RoleRevoked} event.
     */
    function _revokeRole(bytes32 role, address account) internal virtual returns (bool) {
        if (hasRole(role, account)) {
            _roles[role].hasRole[account] = false;
            emit RoleRevoked(role, account, _msgSender());
            return true;
        } else {
            return false;
        }
    }
}

File 8 of 22 : IAccessControl.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/IAccessControl.sol)

pragma solidity ^0.8.20;

/**
 * @dev External interface of AccessControl declared to support ERC165 detection.
 */
interface IAccessControl {
    /**
     * @dev The `account` is missing a role.
     */
    error AccessControlUnauthorizedAccount(address account, bytes32 neededRole);

    /**
     * @dev The caller of a function is not the expected one.
     *
     * NOTE: Don't confuse with {AccessControlUnauthorizedAccount}.
     */
    error AccessControlBadConfirmation();

    /**
     * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
     *
     * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
     * {RoleAdminChanged} not being emitted signaling this.
     */
    event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);

    /**
     * @dev Emitted when `account` is granted `role`.
     *
     * `sender` is the account that originated the contract call, an admin role
     * bearer except when using {AccessControl-_setupRole}.
     */
    event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Emitted when `account` is revoked `role`.
     *
     * `sender` is the account that originated the contract call:
     *   - if using `revokeRole`, it is the admin role bearer
     *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
     */
    event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) external view returns (bool);

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {AccessControl-_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) external view returns (bytes32);

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function grantRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function revokeRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been granted `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `callerConfirmation`.
     */
    function renounceRole(bytes32 role, address callerConfirmation) external;
}

File 9 of 22 : 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 ERC20 standard as defined in the EIP.
 */
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 10 of 22 : IERC721.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/IERC721.sol)

pragma solidity ^0.8.20;

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

/**
 * @dev Required interface of an ERC721 compliant contract.
 */
interface IERC721 is IERC165 {
    /**
     * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
     */
    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
     */
    event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
     */
    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

    /**
     * @dev Returns the number of tokens in ``owner``'s account.
     */
    function balanceOf(address owner) external view returns (uint256 balance);

    /**
     * @dev Returns the owner of the `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function ownerOf(uint256 tokenId) external view returns (address owner);

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon
     *   a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
     * are aware of the ERC721 protocol to prevent tokens from being forever locked.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or
     *   {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon
     *   a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(address from, address to, uint256 tokenId) external;

    /**
     * @dev Transfers `tokenId` token from `from` to `to`.
     *
     * WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721
     * or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
     * understand this adds an external call which potentially creates a reentrancy vulnerability.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 tokenId) external;

    /**
     * @dev Gives permission to `to` to transfer `tokenId` token to another account.
     * The approval is cleared when the token is transferred.
     *
     * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
     *
     * Requirements:
     *
     * - The caller must own the token or be an approved operator.
     * - `tokenId` must exist.
     *
     * Emits an {Approval} event.
     */
    function approve(address to, uint256 tokenId) external;

    /**
     * @dev Approve or remove `operator` as an operator for the caller.
     * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
     *
     * Requirements:
     *
     * - The `operator` cannot be the address zero.
     *
     * Emits an {ApprovalForAll} event.
     */
    function setApprovalForAll(address operator, bool approved) external;

    /**
     * @dev Returns the account approved for `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function getApproved(uint256 tokenId) external view returns (address operator);

    /**
     * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
     *
     * See {setApprovalForAll}
     */
    function isApprovedForAll(address owner, address operator) external view returns (bool);
}

File 11 of 22 : 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 12 of 22 : ERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/ERC165.sol)

pragma solidity ^0.8.20;

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

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
 * for the additional interface id that will be supported. For example:
 *
 * ```solidity
 * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
 *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
 * }
 * ```
 */
abstract contract ERC165 is IERC165 {
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
        return interfaceId == type(IERC165).interfaceId;
    }
}

File 13 of 22 : 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 ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * 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[EIP 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);
}

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

pragma solidity ^0.8.20;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    /**
     * @dev Muldiv operation overflow.
     */
    error MathOverflowedMulDiv();

    enum Rounding {
        Floor, // Toward negative infinity
        Ceil, // Toward positive infinity
        Trunc, // Toward zero
        Expand // Away from zero
    }

    /**
     * @dev Returns the addition of two unsigned integers, with an overflow flag.
     */
    function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            uint256 c = a + b;
            if (c < a) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, with an overflow flag.
     */
    function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b > a) return (false, 0);
            return (true, a - b);
        }
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
     */
    function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
            // benefit is lost if 'b' is also tested.
            // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
            if (a == 0) return (true, 0);
            uint256 c = a * b;
            if (c / a != b) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the division of two unsigned integers, with a division by zero flag.
     */
    function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a / b);
        }
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
     */
    function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a % b);
        }
    }

    /**
     * @dev Returns the largest of two numbers.
     */
    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return a > b ? a : b;
    }

    /**
     * @dev Returns the smallest of two numbers.
     */
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two numbers. The result is rounded towards
     * zero.
     */
    function average(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b) / 2 can overflow.
        return (a & b) + (a ^ b) / 2;
    }

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds towards infinity instead
     * of rounding towards zero.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        if (b == 0) {
            // Guarantee the same behavior as in a regular Solidity division.
            return a / b;
        }

        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a == 0 ? 0 : (a - 1) / b + 1;
    }

    /**
     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
     * denominator == 0.
     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by
     * Uniswap Labs also under MIT license.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
        unchecked {
            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
            // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
            // variables such that product = prod1 * 2^256 + prod0.
            uint256 prod0 = x * y; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(x, y, not(0))
                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
            }

            // Handle non-overflow cases, 256 by 256 division.
            if (prod1 == 0) {
                // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                // The surrounding unchecked block does not change this fact.
                // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                return prod0 / denominator;
            }

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            if (denominator <= prod1) {
                revert MathOverflowedMulDiv();
            }

            ///////////////////////////////////////////////
            // 512 by 256 division.
            ///////////////////////////////////////////////

            // Make division exact by subtracting the remainder from [prod1 prod0].
            uint256 remainder;
            assembly {
                // Compute remainder using mulmod.
                remainder := mulmod(x, y, denominator)

                // Subtract 256 bit number from 512 bit number.
                prod1 := sub(prod1, gt(remainder, prod0))
                prod0 := sub(prod0, remainder)
            }

            // Factor powers of two out of denominator and compute largest power of two divisor of denominator.
            // Always >= 1. See https://cs.stackexchange.com/q/138556/92363.

            uint256 twos = denominator & (0 - denominator);
            assembly {
                // Divide denominator by twos.
                denominator := div(denominator, twos)

                // Divide [prod1 prod0] by twos.
                prod0 := div(prod0, twos)

                // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                twos := add(div(sub(0, twos), twos), 1)
            }

            // Shift in bits from prod1 into prod0.
            prod0 |= prod1 * twos;

            // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
            // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
            // four bits. That is, denominator * inv = 1 mod 2^4.
            uint256 inverse = (3 * denominator) ^ 2;

            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also
            // works in modular arithmetic, doubling the correct bits in each step.
            inverse *= 2 - denominator * inverse; // inverse mod 2^8
            inverse *= 2 - denominator * inverse; // inverse mod 2^16
            inverse *= 2 - denominator * inverse; // inverse mod 2^32
            inverse *= 2 - denominator * inverse; // inverse mod 2^64
            inverse *= 2 - denominator * inverse; // inverse mod 2^128
            inverse *= 2 - denominator * inverse; // inverse mod 2^256

            // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
            // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
            // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
            // is no longer required.
            result = prod0 * inverse;
            return result;
        }
    }

    /**
     * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
        uint256 result = mulDiv(x, y, denominator);
        if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) {
            result += 1;
        }
        return result;
    }

    /**
     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
     * towards zero.
     *
     * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
     */
    function sqrt(uint256 a) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }

        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
        //
        // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
        // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
        //
        // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
        // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
        // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
        //
        // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
        uint256 result = 1 << (log2(a) >> 1);

        // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
        // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
        // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
        // into the expected uint128 result.
        unchecked {
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            return min(result, a / result);
        }
    }

    /**
     * @notice Calculates sqrt(a), following the selected rounding direction.
     */
    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = sqrt(a);
            return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 2 of a positive value rounded towards zero.
     * Returns 0 if given 0.
     */
    function log2(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 128;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 64;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 32;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 16;
            }
            if (value >> 8 > 0) {
                value >>= 8;
                result += 8;
            }
            if (value >> 4 > 0) {
                value >>= 4;
                result += 4;
            }
            if (value >> 2 > 0) {
                value >>= 2;
                result += 2;
            }
            if (value >> 1 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log2(value);
            return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 10 of a positive value rounded towards zero.
     * Returns 0 if given 0.
     */
    function log10(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >= 10 ** 64) {
                value /= 10 ** 64;
                result += 64;
            }
            if (value >= 10 ** 32) {
                value /= 10 ** 32;
                result += 32;
            }
            if (value >= 10 ** 16) {
                value /= 10 ** 16;
                result += 16;
            }
            if (value >= 10 ** 8) {
                value /= 10 ** 8;
                result += 8;
            }
            if (value >= 10 ** 4) {
                value /= 10 ** 4;
                result += 4;
            }
            if (value >= 10 ** 2) {
                value /= 10 ** 2;
                result += 2;
            }
            if (value >= 10 ** 1) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log10(value);
            return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 256 of a positive value rounded towards zero.
     * Returns 0 if given 0.
     *
     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
     */
    function log256(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 16;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 8;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 4;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 2;
            }
            if (value >> 8 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 256, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log256(value);
            return result + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0);
        }
    }

    /**
     * @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
     */
    function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
        return uint8(rounding) % 2 == 1;
    }
}

File 15 of 22 : IDelegationRegistry.sol
// SPDX-License-Identifier: CC0-1.0
pragma solidity ^0.8.17;

/**
 * @title An immutable registry contract to be deployed as a standalone primitive
 * @dev See EIP-5639, new project launches can read previous cold wallet -> hot wallet delegations
 * from here and integrate those permissions into their flow
 */
interface IDelegationRegistry {
    /// @notice Delegation type
    enum DelegationType {
        NONE,
        ALL,
        CONTRACT,
        TOKEN
    }

    /// @notice Info about a single delegation, used for onchain enumeration
    struct DelegationInfo {
        DelegationType type_;
        address vault;
        address delegate;
        address contract_;
        uint256 tokenId;
    }

    /// @notice Info about a single contract-level delegation
    struct ContractDelegation {
        address contract_;
        address delegate;
    }

    /// @notice Info about a single token-level delegation
    struct TokenDelegation {
        address contract_;
        uint256 tokenId;
        address delegate;
    }

    /// @notice Emitted when a user delegates their entire wallet
    event DelegateForAll(address vault, address delegate, bool value);

    /// @notice Emitted when a user delegates a specific contract
    event DelegateForContract(address vault, address delegate, address contract_, bool value);

    /// @notice Emitted when a user delegates a specific token
    event DelegateForToken(address vault, address delegate, address contract_, uint256 tokenId, bool value);

    /// @notice Emitted when a user revokes all delegations
    event RevokeAllDelegates(address vault);

    /// @notice Emitted when a user revoes all delegations for a given delegate
    event RevokeDelegate(address vault, address delegate);

    /**
     * -----------  WRITE -----------
     */

    /**
     * @notice Allow the delegate to act on your behalf for all contracts
     * @param delegate The hotwallet to act on your behalf
     * @param value Whether to enable or disable delegation for this address, true for setting and false for revoking
     */
    function delegateForAll(address delegate, bool value) external;

    /**
     * @notice Allow the delegate to act on your behalf for a specific contract
     * @param delegate The hotwallet to act on your behalf
     * @param contract_ The address for the contract you're delegating
     * @param value Whether to enable or disable delegation for this address, true for setting and false for revoking
     */
    function delegateForContract(address delegate, address contract_, bool value) external;

    /**
     * @notice Allow the delegate to act on your behalf for a specific token
     * @param delegate The hotwallet to act on your behalf
     * @param contract_ The address for the contract you're delegating
     * @param tokenId The token id for the token you're delegating
     * @param value Whether to enable or disable delegation for this address, true for setting and false for revoking
     */
    function delegateForToken(address delegate, address contract_, uint256 tokenId, bool value) external;

    /**
     * @notice Revoke all delegates
     */
    function revokeAllDelegates() external;

    /**
     * @notice Revoke a specific delegate for all their permissions
     * @param delegate The hotwallet to revoke
     */
    function revokeDelegate(address delegate) external;

    /**
     * @notice Remove yourself as a delegate for a specific vault
     * @param vault The vault which delegated to the msg.sender, and should be removed
     */
    function revokeSelf(address vault) external;

    /**
     * -----------  READ -----------
     */

    /**
     * @notice Returns all active delegations a given delegate is able to claim on behalf of
     * @param delegate The delegate that you would like to retrieve delegations for
     * @return info Array of DelegationInfo structs
     */
    function getDelegationsByDelegate(address delegate) external view returns (DelegationInfo[] memory);

    /**
     * @notice Returns an array of wallet-level delegates for a given vault
     * @param vault The cold wallet who issued the delegation
     * @return addresses Array of wallet-level delegates for a given vault
     */
    function getDelegatesForAll(address vault) external view returns (address[] memory);

    /**
     * @notice Returns an array of contract-level delegates for a given vault and contract
     * @param vault The cold wallet who issued the delegation
     * @param contract_ The address for the contract you're delegating
     * @return addresses Array of contract-level delegates for a given vault and contract
     */
    function getDelegatesForContract(address vault, address contract_) external view returns (address[] memory);

    /**
     * @notice Returns an array of contract-level delegates for a given vault's token
     * @param vault The cold wallet who issued the delegation
     * @param contract_ The address for the contract holding the token
     * @param tokenId The token id for the token you're delegating
     * @return addresses Array of contract-level delegates for a given vault's token
     */
    function getDelegatesForToken(address vault, address contract_, uint256 tokenId)
        external
        view
        returns (address[] memory);

    /**
     * @notice Returns all contract-level delegations for a given vault
     * @param vault The cold wallet who issued the delegations
     * @return delegations Array of ContractDelegation structs
     */
    function getContractLevelDelegations(address vault)
        external
        view
        returns (ContractDelegation[] memory delegations);

    /**
     * @notice Returns all token-level delegations for a given vault
     * @param vault The cold wallet who issued the delegations
     * @return delegations Array of TokenDelegation structs
     */
    function getTokenLevelDelegations(address vault) external view returns (TokenDelegation[] memory delegations);

    /**
     * @notice Returns true if the address is delegated to act on the entire vault
     * @param delegate The hotwallet to act on your behalf
     * @param vault The cold wallet who issued the delegation
     */
    function checkDelegateForAll(address delegate, address vault) external view returns (bool);

    /**
     * @notice Returns true if the address is delegated to act on your behalf for a token contract or an entire vault
     * @param delegate The hotwallet to act on your behalf
     * @param contract_ The address for the contract you're delegating
     * @param vault The cold wallet who issued the delegation
     */
    function checkDelegateForContract(address delegate, address vault, address contract_)
        external
        view
        returns (bool);

    /**
     * @notice Returns true if the address is delegated to act on your behalf for a specific token, the token's contract or an entire vault
     * @param delegate The hotwallet to act on your behalf
     * @param contract_ The address for the contract you're delegating
     * @param tokenId The token id for the token you're delegating
     * @param vault The cold wallet who issued the delegation
     */
    function checkDelegateForToken(address delegate, address vault, address contract_, uint256 tokenId)
        external
        view
        returns (bool);
}

File 16 of 22 : BaseCompetition.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.22;

import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

import "../lib/Constants.sol";
import "./MinimalBase.sol";

/// @title Raffles manager (lean, delegate.cash, single winner, nft gated with an nft from each whitelisted collection and a fixed amount of ETH as prize)
/// @author Camden Grieh
/// @author Nathan Goodwin - revertCloseRequested function
/// @notice It consumes QRNG from API3. It has the role
/// "operator" that is the one used by a backend app to make some calls
/// @dev It saves in an ordered array the player wallet and the current
/// entries count. So buying entries has a complexity of O(1)
/// For calculating the winner, from the huge random number generated by Api3
/// a normalized random is generated by using the module method, adding 1 to have
/// a random from 1 to entriesCount.
/// So next step is to perform a binary search on the ordered array to get the
/// player O(log n)
/// Example:
/// 0 -> { 1, player1} as player1 buys 1 entry
/// 1 -> {51, player2} as player2 buys 50 entries
/// 2 -> {52, player3} as player3 buys 1 entry
/// 3 -> {53, player4} as player4 buys 1 entry
/// 4 -> {153, player5} as player5 buys 100 entries
/// So the setWinner method performs a binary search on that sorted array to get the upper bound.
/// If the random number generated is 150, the winner is player5. If the random number is 20, winner is player2

abstract contract BaseCompetition is Constants, MinimalBase {
    
    event RaffleStatusUpdated(
        uint256 indexed raffleId, STATUS newStatus
        );

    constructor(
        address _airnodeRrp)
        payable MinimalBase(_airnodeRrp) {}

     /// @notice Called by the Airnode through the AirnodeRrp contract to
    /// fulfill the request
    function fulfillUint256(bytes32 requestId, bytes calldata data)
        external override
        onlyAirnodeRrp
    {
        require(
            expectingRequestWithIdToBeFulfilled[requestId],
            "Request ID not known"
        );
        expectingRequestWithIdToBeFulfilled[requestId] = false;
        uint256 qrngUint256 = abi.decode(data, (uint256));
        _qrngUint256 = qrngUint256;
        // Do what you want with `qrngUint256` here...
        emit ReceivedUint256(requestId, qrngUint256);

        // randomness is the actual random number. Now extract from the aux map the original param id of the call
        RaffleInfo memory raffleInfo = api3RaffleInfo[requestId];
        // save the random number on the map with the original id as key
        uint256 normalizedRandomNumber = (qrngUint256 % raffleInfo.size) + 1;

        RandomResult memory result = RandomResult({
            randomNumber: qrngUint256,
            nomalizedRandomNumber: normalizedRandomNumber
        });

        requests[raffleInfo.id] = result;

        // send the event with the original id and the random number
        emit RandomNumberCreated(
            raffleInfo.id,
            qrngUint256,
            normalizedRandomNumber
        );

        _transferPrizesAndFunds(raffleInfo.id, normalizedRandomNumber);
    }

    function _transferPrizesAndFunds(
        uint256 idRaffle,
        uint256 normalizedRandomNumber
    ) internal virtual;

    function _saveEntryInfo() internal {
        EntryInfoStruct memory entryInfo = EntryInfoStruct({
            status: STATUS.CREATED,
            amountRaised: 0,
            entriesLength: 0,
            walletsCap: 0,
            requireWhitelisting: false,
            requiredTokenAddress: currentRequiredTokenAddress,
            requiredTokenBalance: currentRequiredTokenAmount
        });
        rafflesEntryInfo.push(entryInfo);
    }

    function _saveEntryInfo(
        address[] calldata _whitelist,
        uint48 _walletsCap,
        uint256 _raffleId
    ) internal virtual {}

    function _buyEntryActions(
        uint256 _raffleId,
        uint256 _id
    ) internal returns (EntryInfoStruct memory) {

        EntryInfoStruct storage entryInfo = rafflesEntryInfo[_raffleId];

        if (entryInfo.status != STATUS.ACCEPTED)
            revert EntryNotAllowed("Not in ACCEPTED");
        // Price checks
        PriceStructure memory priceStruct = pricesList[_id];
        if (priceStruct.id != _raffleId)
            revert EntryNotAllowed("Id not in raffleId");
        uint48 numEntries = priceStruct.numEntries;
        if (msg.value != priceStruct.price)
            revert EntryNotAllowed("msg.value not the price");

        if (priceStruct.price == 0) {
            if (tx.origin != msg.sender)
                revert EntryNotAllowed("tx.origin != msg.sender");
            bytes32 hash = keccak256(abi.encode(msg.sender, _raffleId));
            if (freeEntriesPerWallet[hash] == true)
                revert EntryNotAllowed("Player already got free entry");
            freeEntriesPerWallet[hash] = true;
        }

        if (entryInfo.requiredTokenBalance != 0) {
            IERC20 token = IERC20(entryInfo.requiredTokenAddress);
            require(
                token.balanceOf(msg.sender) >= entryInfo.requiredTokenBalance,
                "Not enough tokens"
            );
        }

        // save the entries onchain
        uint48 entriesLength = entryInfo.entriesLength;
        EntriesBought memory entryBought = EntriesBought({
            player: msg.sender,
            currentEntriesLength: uint48(entriesLength + numEntries)
        });
        entriesList[_raffleId].push(entryBought);
        delete entriesList[_raffleId][0]; 
        // update raffle variables
        entryInfo.amountRaised += uint128(msg.value);
        entryInfo.entriesLength = entriesLength + numEntries;

        emit EntrySold(_raffleId, msg.sender, entryInfo.entriesLength, _id);

        return entryInfo;
    }

    //@notice internal function to check if the player can buy entries
    //@dev checks if the player has the NFT required to buy entries
    //@param col address of the collection of the NFT
    //@param id id of the NFT
    //@param _raffleId id of the raffle
    //@param sender address of the player
    function _buyEntriesRequiredNFTCheck(
        address col,
        uint256 id,
        uint256 _raffleId,
        address sender
    ) internal {
        bytes32 collectionHash = keccak256(abi.encode(col, _raffleId));
        require(
            whitelistCollections[collectionHash] == true,
            "Not in required collection"
        );
        IERC721 requiredNFT = IERC721(col);

        require(requiredNFT.ownerOf(id) == sender, "Not the owner of tokenId");
        bytes32 hashRequiredNFT = keccak256(abi.encode(col, _raffleId, id));
        // check the tokenId has not been using yet in the raffle, to avoid abuse
        if (requiredNFTWallets[hashRequiredNFT] == address(0)) {
            requiredNFTWallets[hashRequiredNFT] = sender;
        } else
            require(
                requiredNFTWallets[hashRequiredNFT] == sender,
                "tokenId used"
            );
    }

    /// @notice 
    /// @dev internal function that checks if the player has reached the max amount of entries per wallet
    /// @param entryInfo EntryInfoStruct struct that contains the info of the entry variables
    /// @param _raffleId Id of the raffle
    /// @param _id Id of the price structure
    function _checkWalletsCap(
        EntryInfoStruct memory entryInfo,
        uint256 _raffleId,
        uint256 _id
    ) internal {
        if (entryInfo.walletsCap > 0) {
            PriceStructure memory priceStruct = pricesList[_id];
            bytes32 entriesCountHash = keccak256(
                abi.encode(_raffleId, msg.sender)
            );
            uint48 entriesCount = walletsCap[entriesCountHash];
            if (entriesCount + priceStruct.numEntries > entryInfo.walletsCap)
                revert EntryNotAllowed("Wallet already used");
            // update wallet cap of current user
            walletsCap[entriesCountHash] =
                walletsCap[entriesCountHash] +
                priceStruct.numEntries;
        }
    }

    // The operator can add free entries to the raffle
    /// @param _raffleId Id of the raffle
    /// @param _freePlayers array of addresses corresponding to the wallet of the users that won a free entrie
    /// @dev only operator can make this call. Assigns a single entry per user, except if that user already reached the max limit of entries per user
    function giveBatchEntriesForFree(
        uint256 _raffleId,
        address[] memory _freePlayers
    ) external onlyRole(OPERATOR_ROLE) {
        EntryInfoStruct storage entryInfo = rafflesEntryInfo[_raffleId];
        require(
            entryInfo.status == STATUS.ACCEPTED,
            "Raffle is not in accepted"
        );

        uint256 freePlayersLength = _freePlayers.length;
        uint48 validPlayersCount;
        for (uint256 i; i < freePlayersLength; ++i) {
            address entry = _freePlayers[i];
            {
                // add a new element to the entriesBought array.
                // as this method only adds 1 entry per call, the amountbought is always 1
                EntriesBought memory entryBought = EntriesBought({
                    player: entry,
                    currentEntriesLength: uint48(
                        entryInfo.entriesLength + i + 1
                    )
                });
                entriesList[_raffleId].push(entryBought);
                unchecked {
                    ++validPlayersCount;
                }
            }
        }

        entryInfo.entriesLength = entryInfo.entriesLength + validPlayersCount;

        emit FreeEntry(
            _raffleId,
            _freePlayers,
            freePlayersLength,
            entryInfo.entriesLength
        );
    }

    /// @param _raffleId Id of the raffle
    /// @return the Entry Info of the raffle
    function _setWinnerActions(
        uint256 _raffleId
    ) internal virtual returns (EntryInfoStruct memory) {
        EntryInfoStruct storage entryInfo = rafflesEntryInfo[_raffleId];
        // RaffleStruct storage raffle = raffles[_raffleId];
        FundingStructure storage funding = fundingList[_raffleId];
        // Check if the raffle is already accepted or is called again because early cashout failed
        require(entryInfo.status == STATUS.ACCEPTED, "Raffle in wrong status");
        require(
            entryInfo.amountRaised >= funding.minimumFundsInWeis,
            "Not enough funds raised"
        );

        require(
            funding.desiredFundsInWeis <= entryInfo.amountRaised,
            "Desired funds not raised"
        );
        entryInfo.status = STATUS.CLOSING_REQUESTED;

        emit SetWinnerTriggered(_raffleId, entryInfo.amountRaised);
        return entryInfo;
    }

    /// @param _raffleId Id of the raffle
    /// @notice the operator finish the raffle, if the desired funds has been reached
    /// @dev it triggers Api3 QRNG consumer, and generates a random number that is normalized and checked that corresponds to a MW player
    function setWinner(uint256 _raffleId) external onlyRole(OPERATOR_ROLE) {
        EntryInfoStruct memory raffle = _setWinnerActions(_raffleId);
        // this call triggers the QRNG from Api3

        bytes32 requestId = _callQRNGAndGetRequestId();
        _getRandomNumber(_raffleId, raffle.entriesLength, requestId);
    }

    /// @param _newAddress new address of the platform
    /// @dev Change the wallet of the platform. The one that will receive the platform fee when the raffle is closed.
    /// Only the admin can change this
    function setDestinationAddress(
        address payable _newAddress
    ) external onlyRole(DEFAULT_ADMIN_ROLE) {
        destinationWallet = _newAddress;
    }

    /// @param _raffleId Id of the raffle
    /// @dev The operator can cancel the raffle. The NFT is sent back to the seller
    /// The raised funds are send to the destination wallet. The buyers will
    /// be refunded offchain in the metawin wallet
    function cancelRaffle(
        uint256 _raffleId
    ) external virtual onlyRole(OPERATOR_ROLE) {}

    /// @param _raffleId Id of the raffle
    function transferRemainingFunds(
        uint256 _raffleId
    ) external onlyRole(OPERATOR_ROLE) {
        EntryInfoStruct storage entryInfo = rafflesEntryInfo[_raffleId];
        require(
            entryInfo.status == STATUS.CANCEL_REQUESTED ||
                entryInfo.status == STATUS.CANCELLED,
            "Wrong status"
        );

        entryInfo.status = STATUS.CANCELLED;

        (bool sent, ) = destinationWallet.call{value: entryInfo.amountRaised}(
            ""
        );
        require(sent, "Fail send Eth to MW");

        emit RemainingFundsTransferred(_raffleId, entryInfo.amountRaised);

        entryInfo.amountRaised = 0;
    }

    /// @param _raffleId Id of the raffle
    /// @return array of entries bougth of that particular raffle
    function getEntriesBought(
        uint256 _raffleId
    ) external view returns (EntriesBought[] memory) {
        return entriesList[_raffleId];
    }

    /// @param _raffleId Id of the raffle
    function getRafflesEntryInfo(
        uint256 _raffleId
    ) public view returns (EntryInfoStruct memory) {
        return rafflesEntryInfo[_raffleId];
    }

    ///@notice transfer the prize to the winner
    ///@dev internal function to send the NFT prize to the winner
    ///@param prizeAddress address of the prize
    ///@param prizeNumber number of the prize
    ///@param receptor address of the winner
    function _transferNFTTo(
        address prizeAddress,
        uint256 prizeNumber,
        address receptor
    ) internal {
        IERC721 _asset = IERC721(prizeAddress);
        _asset.transferFrom(address(this), receptor, prizeNumber);
    }


    function _transferETHTo(uint256 prizeNumber, address receptor) internal {
        (bool sentPrize, ) = receptor.call{value: prizeNumber}("");
        require(sentPrize, "Failed to send Ether");
    }

    function _transferERC20To(
        address prizeAddress,
        uint256 prizeNumber,
        address receptor
    ) internal {
        IERC20 _asset = IERC20(prizeAddress);
        _asset.transfer(receptor, prizeNumber);
    }

    /// @param _raffleId Id of the raffle
    /// @dev The operator can cancel the raffle. The NFT is sent back to the seller
    /// The raised funds are send to the destination wallet. The buyers will
    /// be refunded offchain in the metawin wallet
    function _cancelRaffleActions(uint256 _raffleId, uint48 _type) internal {
        RaffleStruct storage raffle = raffles[_raffleId];
        EntryInfoStruct storage entryInfo = rafflesEntryInfo[_raffleId];
        // Dont cancel twice, or cancel an already ended raffle
        //TODO: To save gas, check that it is in the correct status instead of checking all the wrong ones
        require(
            entryInfo.status == STATUS.ACCEPTED || 
            entryInfo.status == STATUS.CREATED,
            "Wrong status"
        );

        // only if the raffle is in accepted status the NFT is staked and could have entries sold
        if (entryInfo.status == STATUS.ACCEPTED) {
            if (_type == 0)
                // transfer nft to the owner
                _transferNFTTo(
                    raffle.prizeAddress,
                    raffle.prizeNumber,
                    raffle.seller
                );
            if (_type == 1)
                // transfer ETH to the owner
                _transferETHTo(raffle.prizeNumber, raffle.seller);
            if (_type == 2)
                // transfer ETH to the owner
                _transferERC20To(
                    raffle.prizeAddress,
                    raffle.prizeNumber,
                    raffle.seller
                );
        }
        entryInfo.status = STATUS.CANCEL_REQUESTED;

        emit RaffleCancelled(_raffleId, entryInfo.amountRaised);
    }

    // The operator can call this method once they receive the event "RandomNumberCreated"
    // triggered by the QRNG consumer contract (RandomNumber.sol)
    /// @param _raffleId Id of the raffle
    /// @param _normalizedRandomNumber index of the array that contains the winner of the raffle. Generated by api3
    /// @notice it is the method that sets the winner and transfers funds and nft
    /// @dev called by Api3 callback
    function _transferPrizesAndFundsActions(
        uint256 _raffleId,
        uint256 _normalizedRandomNumber
    ) internal returns (RaffleStruct memory) {
        RaffleStruct storage raffle = raffles[_raffleId];
        EntryInfoStruct storage entryInfo = rafflesEntryInfo[_raffleId];
        // Only when the raffle has been asked to be closed and the platform
        require(
            entryInfo.status == STATUS.EARLY_CASHOUT ||
                entryInfo.status == STATUS.CLOSING_REQUESTED,
            "Raffle in wrong status"
        );

        raffle.randomNumber = _normalizedRandomNumber;
        raffle.winner = getWinnerAddressFromRandom(
            _raffleId,
            _normalizedRandomNumber
        );
        entryInfo.status = STATUS.ENDED;

        uint256 amountForPlatform = (entryInfo.amountRaised *
            raffle.platformPercentage) / 10000;
        uint256 amountForSeller = entryInfo.amountRaised - amountForPlatform;
        // transfer amount (75%) to the seller.
        (bool sent, ) = raffle.seller.call{value: amountForSeller}("");
        require(sent, "Failed to send Ether");
        // transfer the amount to the platform
        (bool sent2, ) = destinationWallet.call{value: amountForPlatform}("");
        require(sent2, "Failed send Eth to MW");
        emit FeeTransferredToPlatform(_raffleId, amountForPlatform);

        emit RaffleEnded(
            _raffleId,
            raffle.winner,
            entryInfo.amountRaised,
            _normalizedRandomNumber
        );
        return raffle;
    }

    function _saveEntryInfo(
        uint48 _walletsCapPerUser,
        ENTRY_TYPE _entryType,
        address[] calldata _whitelist,
        uint256 _raffleId
    ) internal virtual {}

    /// @notice Creates the raffle in the data structures of the contract
    /// @dev Saves the raffle struct. Creates the packages with the price and amount of entries in the priceStructure struct. 
    /// Saves the desired and required funds, and to avoid the first player to pay extra gas, the entries data structure is created and deleted.
    /// It is an internal method that will be called by the createRaffle method of the correspondent competition contract
    /// @param _desiredFundsInWeis Funds in wais the seller wants
    /// @param _prizeAddress if the prize is an NFT or an ERC20, the address of the prize. If the prize is ETH, will be address(0)
    /// @param _prizeNumber if the prize is an NFT, the token Id of the NFT. If the prize is an ERC20 the number of tokens of the prize. If the prize
    /// is ETH, the amount in weis of the prize.
    /// @param _minimumFundsInWeis The mininum amount required for the raffle to set a winner
    /// @param _prices array of elements of type PriceStructure that contains the packages with prize (in weis) and amount of entries used in the raffle
    /// @param _commissionInBasicPoints percentage in basic points the platform will make.
    /// @param _entryType Obsolete param (used for the hamburger system from Valerio), used only for compatibility porpouses. 
    function _createRaffleActions(
        uint128 _desiredFundsInWeis,
        address _prizeAddress,
        uint256 _prizeNumber,
        uint128 _minimumFundsInWeis,
        PriceStructure[] calldata _prices,
        uint48 _commissionInBasicPoints,
        ENTRY_TYPE _entryType
    ) internal {
        RaffleStruct memory raffle = RaffleStruct({
            prizeAddress: _prizeAddress,
            prizeNumber: _prizeNumber,
            winner: address(0),
            randomNumber: 0,
            seller: address(0),
            platformPercentage: _commissionInBasicPoints
        });

        raffles.push(raffle);

        uint256 idRaffle = raffles.length - 1;
        if (_prices.length == 0) revert CreateRaffleError("No prices");
        for (uint256 i; i < _prices.length; ++i) {
            if (_prices[i].numEntries == 0)
                revert CreateRaffleError("numEntries is 0");

            PriceStructure memory p = PriceStructure({
                id: uint48(idRaffle),
                numEntries: _prices[i].numEntries,
                price: _prices[i].price
            });
            pricesList[_prices[i].id] = p;
        }

        fundingList[raffles.length - 1] = FundingStructure({
            minimumFundsInWeis: _minimumFundsInWeis,
            desiredFundsInWeis: _desiredFundsInWeis
        });

        emit RaffleCreated(raffles.length - 1, _prizeAddress, _prizeNumber);

        // Initialize the entries list array, by adding a player and removing it
        EntriesBought memory entryBought = EntriesBought({
            player: msg.sender,
            currentEntriesLength: uint48(1)
        });
        entriesList[idRaffle].push(entryBought);
        delete entriesList[idRaffle][0];
    }

    function _saveEntryInfoForMulti(
        address[] calldata _whitelist,
        uint48 _walletsCap,
        uint256 _raffleId
    ) internal {
        bool requireWhitelisting = false;
        uint256 whitelistLength = _whitelist.length;
        if (whitelistLength > 0) {
            requireWhitelisting = true;
            for (uint256 i; i <= whitelistLength - 1; ++i) {
                bytes32 key = keccak256(abi.encode(_whitelist[i], _raffleId));
                whitelistCollections[key] = true;
            }
        }

        EntryInfoStruct memory entryInfo = EntryInfoStruct({
            status: STATUS.CREATED,
            amountRaised: 0,
            entriesLength: 0,
            requireWhitelisting: requireWhitelisting,
            walletsCap: _walletsCap,
            requiredTokenBalance: currentRequiredTokenAmount,
            requiredTokenAddress: currentRequiredTokenAddress
        });
        rafflesEntryInfo.push(entryInfo);
    }

    /**
    * @dev Reverts the status of a raffle from CLOSING_REQUESTED back to ACCEPTED.
    *      This allows the raffle to remain open for further actions such as a redraw.
    *      Useful in avoiding funds becoming stuck in the event of a failed draw.
    * @param raffleId The identifier of the raffle to revert the closing status for.
    * @notice Can only be called by users with the OPERATOR_ROLE.
    *         Emits a RaffleStatusUpdated event upon successful status update.
    * @notice Ensures the raffle is currently in CLOSING_REQUESTED status before reverting.
    */
    function revertCloseRequested(uint256 raffleId) public onlyRole(OPERATOR_ROLE) {
        require(rafflesEntryInfo[raffleId].status == STATUS.CLOSING_REQUESTED, "Raffle is not in close requested state");
        rafflesEntryInfo[raffleId].status = STATUS.ACCEPTED; 
        emit RaffleStatusUpdated(raffleId, STATUS.ACCEPTED); 
    }

    ///@dev callable by operator. It changes the token required to enter the raffle
    /// @param _tokenAddress: address of the token
    function setRequiredTokenAddress(address _tokenAddress) external onlyRole(OPERATOR_ROLE) {
        currentRequiredTokenAddress = _tokenAddress;
    }

    ///@dev callable by operator. It changes the amount of tokens required to enter the raffle
    /// @param _tokenAmount: amount of tokens
    function setRequiredTokenAmount(uint256 _tokenAmount) external onlyRole(OPERATOR_ROLE) {
        currentRequiredTokenAmount = _tokenAmount;
    } 
}

File 17 of 22 : BaseERC20Extension.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.22;

import "../lib/BaseCompetition.sol";
import { IBlacklistable } from "../lib/interfaces/IUSDCToken.sol";

/// @title BaseERC20Extension
/// @author Nathan Goodwin
/// @notice Extends BaseCompetition to support ERC20 token-based competitions with enhanced winner selection logic.
/// @dev This abstract contract integrates additional checks for ERC20 token competitions, particularly focusing on
/// ensuring that winners are not blacklisted users, especially in cases where the prize is a regulated asset like USDC.
/// It is designed to be inherited by specific competition contracts like ERC20Comp.sol, MultiNFTGatedERC20Comp.sol, etc.
/// The contract overrides the winner selection process to incorporate blacklist checks, ensuring compliance with
/// regulatory requirements and fair play in the competitions.

abstract contract BaseERC20Extension is BaseCompetition {

    /// @dev Define UsdcTokenContract
    IBlacklistable public immutable usdcTokenContract;

    constructor(address _usdcTokenContract) {
        usdcTokenContract = IBlacklistable(_usdcTokenContract);
    }

    /// @param _raffleId Id of the raffle
    /// @param _normalizedRandomNumber Generated by api3
    /// @return the wallet that won the raffle
    /// @dev Now contract received a random number from fulfillRandomness, we can use it getWinnnerAddressFuntion to select a winner. 
    // Overrides the declaration in MinimalBase.sol to ensure that the winner is not blacklisted by a centralised token
    function getWinnerAddressFromRandom(
        uint256 _raffleId,
        uint256 _normalizedRandomNumber
    ) public view virtual override returns (address) {
        uint256 position = _findUpperBound( 
            entriesList[_raffleId],
            _normalizedRandomNumber
        );
        // Potential winner is chosen here
        address candidate = entriesList[_raffleId][position].player; 

        // Get the raffle prize asset. If it is USDC, checks if the candidate is blacklisted
        // raffles[_raffleId].prizeAddress; //Returns prize token address
        // Get USDC Address on Mainnet
        if (!(raffles[_raffleId].prizeAddress == 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48)) {
            // Checks if the prize address is not USDC, if it is not, call the standard function
            // for handling winning candidates
           return _returnValidWinner(candidate, position, _raffleId);
        } else {
            // The prize is USDC, handle the winning candidate accordingly
           return _returnValidWinnerWithCircleOrMetawin(candidate, position, _raffleId);
        }
    }

    /// @notice check that the user is a valid winner - not blacklisted by Metawin
    /// @param candidate address of the potential winner
    /// @param position position of the potential winner in the entriesList
    /// @param _raffleId Id of the raffle
    /// @return the wallet that won the raffle

    function _returnValidWinner(
        address candidate, uint256 position, uint256 _raffleId
    ) internal view returns (address) {
        // General case - if the user is not blacklisted by Metawin we return the winner
        if (candidate != address(0)) return candidate;
        // Special case. The user is blacklisted, so try next on the left until we find one thats not zero
        else {
            bool ended = false;
            uint256 i = position;
            while (
                ended == false && entriesList[_raffleId][i].player == address(0)
            ) {
                if (i == 0) i = entriesList[_raffleId].length - 1;
                else i = i - 1;
                // We came to the beginning without finding a non blacklisted player
                if (i == position) {
                    ended = true;
                }
            }
            require(!ended, "All users blacklisted"); // Reverts if all users are blacklisted
            return entriesList[_raffleId][i].player; // Winner is returned
        }
    }

    /// @notice check that the user is a valid winner - not blacklisted by Metawin or Circle
    /// @param candidate address of the potential winner
    /// @param position position of the potential winner in the entriesList
    /// @param _raffleId Id of the raffle
    /// @return the wallet that won the raffle

    function _returnValidWinnerWithCircleOrMetawin(
        address candidate, uint256 position, uint256 _raffleId
    ) internal view returns (address) {
        // General case - if the user is not blacklisted by Metawin or Circle, we return the winner
        if (candidate != address(0) && !usdcTokenContract.isBlacklisted(candidate)) {
            return candidate;
        }
        // Checks if the user is blacklisted by Circle
        // Special case. The user is blacklisted, so try next on the left until we find an thats not zero  
        // nor blacklisted by Circle
        else {
            bool ended = false;
            uint256 i = position;
            while (!ended) {
                if (entriesList[_raffleId][i].player != address(0) && 
                    !usdcTokenContract.isBlacklisted(entriesList[_raffleId][i].player)) {
                    // Found a non-blacklisted player
                    return entriesList[_raffleId][i].player;
                }
                if (i == 0) i = entriesList[_raffleId].length - 1;
                else i = i - 1;
                // We came to the beginning without finding a non blacklisted player
                if (i == position) {
                    ended = true;
                }
            }
            require(!ended, "All users blacklisted"); // Reverts if all users are blacklisted
            return entriesList[_raffleId][i].player; // Winner is returned
        }
    }
}

File 18 of 22 : Constants.sol
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;

import "@openzeppelin/contracts/access/AccessControl.sol";

import "../interfaces/IDelegationRegistry.sol";
import "./MinimalConstants.sol";

abstract contract Constants is AccessControl, MinimalConstants {
    ////////// DELEGATE:CASH ///////////////
    IDelegationRegistry reg;

    address currentRequiredTokenAddress;
    uint256 currentRequiredTokenAmount;

    // Main raffle data struct
    struct RaffleStruct {
        uint256 prizeNumber; // number (can be a percentage, an id, an amount, etc. depending on the competition)
        uint48 platformPercentage; // percentage of the funds raised that goes to the platform
        address prizeAddress; // address of the prize
        address winner; // address of thed winner of the raffle. Address(0) if no winner yet
        address seller; // address of the seller of the NFT
        uint256 randomNumber; // normalized (0-Entries array size) random number generated by the VRF
    }
    // The main structure is an array of raffles
    RaffleStruct[] public raffles;

    struct EntryInfoStruct {
        bool requireWhitelisting;
        STATUS status; // status of the raffle. Can be created, accepted, ended, etc
        uint48 walletsCap;
        uint48 entriesLength; // To be used for frontend, to know how many entries were sold
        uint128 amountRaised; // funds raised so far in wei
        address requiredTokenAddress; // address of the token required to enter the raffle
        uint256 requiredTokenBalance; // amount of tokens required to enter the raffle
    }
    // The main structure is an array of raffles
    EntryInfoStruct[] public rafflesEntryInfo;

    // All the different status a rafVRFCoordinatorfle can have
    enum STATUS {
        CREATED, // the operator creates the raffle
        ACCEPTED, // the seller stakes the nft for the raffle
        EARLY_CASHOUT, // the seller wants to cashout early
        CANCELLED, // the operator cancels the raffle and transfer the remaining funds after 30 days passes
        CLOSING_REQUESTED, // the operator requests to close the raffle and start the VRF
        ENDED, // the raffle is finished, and NFT and funds were transferred
        CANCEL_REQUESTED, // operator asks to cancel the raffle. Players has 30 days to ask for a refund
        WINNER_DETERMINED // the vrf sets a winner
    }

    // Event sent when the raffle is created by the operator
    event RaffleCreated(
        uint256 indexed raffleId,
        address indexed nftAddress,
        uint256 indexed nftId
    );
    // Event sent when one or more entries are sold (info from the price structure)
    event EntrySold(
        uint256 indexed raffleId,
        address indexed buyer,
        uint256 currentSize,
        uint256 priceStructureId
    );

    constructor() {
        _grantRole(OPERATOR_ROLE, 0x13503B622abC0bD30A7e9687057DF6E8c42Fb928);
        _grantRole(DEFAULT_ADMIN_ROLE, msg.sender);

        // the delegation registry addess is the same in all networks:
        // https://github.com/delegatecash/delegate-registry
        reg = IDelegationRegistry(0x00000000000076A84feF008CDAbe6409d2FE638B);
    }
}

File 19 of 22 : IUSDCToken.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/**
 * @title IBlacklistable
 * @dev Interface for a contract that allows accounts to be blacklisted by a "blacklister" role.
 */
interface IBlacklistable {
    
    // Events
    event Blacklisted(address indexed account);
    event UnBlacklisted(address indexed account);
    event BlacklisterChanged(address indexed newBlacklister);

    // Functions
    /**
     * @dev Checks if account is blacklisted
     * @param _account The address to check
     * @return bool True if the account is blacklisted
     */
    function isBlacklisted(address _account) external view returns (bool);

    /**
     * @dev Adds account to blacklist
     * @param _account The address to blacklist
     */
    function blacklist(address _account) external;

    /**
     * @dev Removes account from blacklist
     * @param _account The address to remove from the blacklist
     */
    function unBlacklist(address _account) external;

    /**
     * @dev Updates the blacklister address
     * @param _newBlacklister The address of the new blacklister
     */
    function updateBlacklister(address _newBlacklister) external;
}

File 20 of 22 : VRFCoordinatorV2Interface.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface VRFCoordinatorV2Interface {
  /**
   * @notice Get configuration relevant for making requests
   * @return minimumRequestConfirmations global min for request confirmations
   * @return maxGasLimit global max for request gas limit
   * @return s_provingKeyHashes list of registered key hashes
   */
  function getRequestConfig() external view returns (uint16, uint32, bytes32[] memory);

  /**
   * @notice Request a set of random words.
   * @param keyHash - Corresponds to a particular oracle job which uses
   * that key for generating the VRF proof. Different keyHash's have different gas price
   * ceilings, so you can select a specific one to bound your maximum per request cost.
   * @param subId  - The ID of the VRF subscription. Must be funded
   * with the minimum subscription balance required for the selected keyHash.
   * @param minimumRequestConfirmations - How many blocks you'd like the
   * oracle to wait before responding to the request. See SECURITY CONSIDERATIONS
   * for why you may want to request more. The acceptable range is
   * [minimumRequestBlockConfirmations, 200].
   * @param callbackGasLimit - How much gas you'd like to receive in your
   * _fulfillRandomWords callback. Note that gasleft() inside _fulfillRandomWords
   * may be slightly less than this amount because of gas used calling the function
   * (argument decoding etc.), so you may need to request slightly more than you expect
   * to have inside _fulfillRandomWords. The acceptable range is
   * [0, maxGasLimit]
   * @param numWords - The number of uint256 random values you'd like to receive
   * in your _fulfillRandomWords callback. Note these numbers are expanded in a
   * secure way by the VRFCoordinator from a single random value supplied by the oracle.
   * @return requestId - A unique identifier of the request. Can be used to match
   * a request to a response in _fulfillRandomWords.
   */
  function requestRandomWords(
    bytes32 keyHash,
    uint64 subId,
    uint16 minimumRequestConfirmations,
    uint32 callbackGasLimit,
    uint32 numWords
  ) external returns (uint256 requestId);

  /**
   * @notice Create a VRF subscription.
   * @return subId - A unique subscription id.
   * @dev You can manage the consumer set dynamically with addConsumer/removeConsumer.
   * @dev Note to fund the subscription, use transferAndCall. For example
   * @dev  LINKTOKEN.transferAndCall(
   * @dev    address(COORDINATOR),
   * @dev    amount,
   * @dev    abi.encode(subId));
   */
  function createSubscription() external returns (uint64 subId);

  /**
   * @notice Get a VRF subscription.
   * @param subId - ID of the subscription
   * @return balance - LINK balance of the subscription in juels.
   * @return reqCount - number of requests for this subscription, determines fee tier.
   * @return owner - owner of the subscription.
   * @return consumers - list of consumer address which are able to use this subscription.
   */
  function getSubscription(
    uint64 subId
  ) external view returns (uint96 balance, uint64 reqCount, address owner, address[] memory consumers);

  /**
   * @notice Request subscription owner transfer.
   * @param subId - ID of the subscription
   * @param newOwner - proposed new owner of the subscription
   */
  function requestSubscriptionOwnerTransfer(uint64 subId, address newOwner) external;

  /**
   * @notice Request subscription owner transfer.
   * @param subId - ID of the subscription
   * @dev will revert if original owner of subId has
   * not requested that msg.sender become the new owner.
   */
  function acceptSubscriptionOwnerTransfer(uint64 subId) external;

  /**
   * @notice Add a consumer to a VRF subscription.
   * @param subId - ID of the subscription
   * @param consumer - New consumer which can use the subscription
   */
  function addConsumer(uint64 subId, address consumer) external;

  /**
   * @notice Remove a consumer from a VRF subscription.
   * @param subId - ID of the subscription
   * @param consumer - Consumer to remove from the subscription
   */
  function removeConsumer(uint64 subId, address consumer) external;

  /**
   * @notice Cancel a subscription
   * @param subId - ID of the subscription
   * @param to - Where to send the remaining LINK to
   */
  function cancelSubscription(uint64 subId, address to) external;

  /*
   * @notice Check to see if there exists a request commitment consumers
   * for all consumers and keyhashes for a given sub.
   * @param subId - ID of the subscription
   * @return true if there exists at least one unfulfilled request for the subscription, false
   * otherwise.
   */
  function pendingRequestExists(uint64 subId) external view returns (bool);
}

File 21 of 22 : MinimalBase.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.22;

import "../lib/MinimalConstants.sol";
import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/utils/math/Math.sol";
import "@api3/airnode-protocol/contracts/rrp/requesters/RrpRequesterV0.sol";

/// @title Raffles manager (lean, delegate.cash, single winner, nft gated with an nft from each whitelisted collection and a fixed amount of ETH as prize)
/// @author Camden Grieh
/// @notice It consumes QRNG from API3. It has the role
/// "operator" that is the one used by a backend app to make some calls
/// @dev It saves in an ordered array the player wallet and the current
/// entries count. So buying entries has a complexity of O(1)
/// For calculating the winner, from the huge random number generated by Api3
/// a normalized random is generated by using the module method, adding 1 to have
/// a random from 1 to entriesCount.
/// So next step is to perform a binary search on the ordered array to get the
/// player O(log n)
/// Example:
/// 0 -> { 1, player1} as player1 buys 1 entry
/// 1 -> {51, player2} as player2 buys 50 entries
/// 2 -> {52, player3} as player3 buys 1 entry
/// 3 -> {53, player4} as player4 buys 1 entry
/// 4 -> {153, player5} as player5 buys 100 entries
/// So the setWinner method performs a binary search on that sorted array to get the upper bound.
/// If the random number generated is 150, the winner is player5. If the random number is 20, winner is player2

abstract contract MinimalBase is
    AccessControl,
    MinimalConstants,
    RrpRequesterV0
{
    event RequestedUint256(bytes32 indexed requestId);
    event ReceivedUint256(bytes32 indexed requestId, uint256 response);
    event RequestedUint256Array(bytes32 indexed requestId, uint256 size);
    event ReceivedUint256Array(bytes32 indexed requestId, uint256[] response);
    event WithdrawalRequested(
        address indexed airnode,
        address indexed sponsorWallet
    );

    address public airnode; // The address of the QRNG Airnode
    bytes32 public endpointIdUint256; // The endpoint ID for requesting a single random number
    bytes32 public endpointIdUint256Array; // The endpoint ID for requesting an array of random numbers
    address public sponsorWallet; // The wallet that will cover the gas costs of the request
    uint256 public _qrngUint256; // The random number returned by the QRNG Airnode
    uint256[] public _qrngUint256Array; // The array of random numbers returned by the QRNG Airnode

    mapping(bytes32 => bool) public expectingRequestWithIdToBeFulfilled;

    constructor(address _airnodeRrp) RrpRequesterV0(_airnodeRrp) {}

    /// @notice Sets the parameters for making requests
    function setRequestParameters(
        address _airnode,
        bytes32 _endpointIdUint256,
        bytes32 _endpointIdUint256Array,
        address _sponsorWallet
    ) external onlyRole(DEFAULT_ADMIN_ROLE) {
        airnode = _airnode;
        endpointIdUint256 = _endpointIdUint256;
        endpointIdUint256Array = _endpointIdUint256Array;
        sponsorWallet = _sponsorWallet;
    }

    function fulfillUint256(
        bytes32 requestId,
        bytes calldata data
    ) external virtual onlyAirnodeRrp {}

    function _callQRNGAndGetRequestId() internal returns (bytes32) {
        bytes32 _requestId = airnodeRrp.makeFullRequest(
            airnode,
            endpointIdUint256,
            address(this),
            sponsorWallet,
            address(this),
            this.fulfillUint256.selector,
            ""
        );
        expectingRequestWithIdToBeFulfilled[_requestId] = true;
        emit RequestedUint256(_requestId);
        return _requestId;
    }

    /// @dev this is the method that will be called by the smart contract to get a random number
    /// @param _id Id of the raffle
    /// @param _entriesSize length of the entries array of that raffle
    /// @param _requestId id generated by Api3 QRNG
    function _getRandomNumber(
        uint256 _id,
        uint256 _entriesSize,
        bytes32 _requestId
    ) internal {
        api3RaffleInfo[_requestId] = RaffleInfo({id: _id, size: _entriesSize});
    }

    // helper method to get the winner address of a raffle
    /// @param _raffleId Id of the raffle
    /// @param _normalizedRandomNumber Generated by api3
    /// @return the wallet that won the raffle
    /// @dev Uses a binary search on the sorted array to retreive the winner
    /// but if the winner candidate is blacklisted, loop through the left looking for
    /// a candidate not blacklisted
    function getWinnerAddressFromRandom(
        uint256 _raffleId,
        uint256 _normalizedRandomNumber
    ) public view virtual returns (address) {
        uint256 position = _findUpperBound(
            entriesList[_raffleId],
            _normalizedRandomNumber
        );

        address candidate = entriesList[_raffleId][position].player;
        // general case
        if (candidate != address(0)) return candidate;
        // special case. The user is blacklisted, so try next on the left until find a non-blacklisted
        else {
            bool ended = false;
            uint256 i = position;
            while (
                ended == false && entriesList[_raffleId][i].player == address(0)
            ) {
                if (i == 0) i = entriesList[_raffleId].length - 1;
                else i = i - 1;
                // we came to the beginning without finding a non blacklisted player
                if (i == position) ended == true;
            }
            require(!ended, "All users blacklisted");
            return entriesList[_raffleId][i].player;
        }
    }

    /// @param array sorted array of EntriesBought. CurrentEntriesLength is the numeric field used to sort
    /// @param element uint256 to find. Goes from 1 to entriesLength
    /// @dev based on openzeppelin code (v4.0), modified to use an array of EntriesBought
    /// Searches a sorted array and returns the first index that contains a value greater or equal to element.
    /// If no such index exists (i.e. all values in the array are strictly less than element), the array length is returned. Time complexity O(log n).
    /// array is expected to be sorted in ascending order, and to contain no repeated elements.
    /// https://docs.openzeppelin.com/contracts/3.x/api/utils#Arrays-_findUpperBound-uint256---uint256-
    function _findUpperBound(
        EntriesBought[] storage array,
        uint256 element
    ) internal view returns (uint256) {
        if (array.length == 0) {
            return 0;
        }

        uint256 low = 0;
        uint256 high = array.length;

        while (low < high) {
            uint256 mid = Math.average(low, high);

            // Note that mid will always be strictly less than high (i.e. it will be a valid array index)
            // because Math.average rounds down (it does integer division with truncation).
            if (array[mid].currentEntriesLength > element) {
                high = mid;
            } else {
                low = mid + 1;
            }
        }

        // At this point `low` is the exclusive upper bound. We will return the inclusive upper bound.
        if (low > 0 && array[low - 1].currentEntriesLength == element) {
            return low - 1;
        } else {
            return low;
        }
    }

       /// @notice To withdraw funds from the sponsor wallet to the contract.
    function requestWithdraw() external onlyRole(DEFAULT_ADMIN_ROLE) {
        airnodeRrp.requestWithdrawal(
        airnode,
        sponsorWallet
        );
    }

    /// @notice To withdraw funds from the contract to the owner.
    function withdraw() external onlyRole(DEFAULT_ADMIN_ROLE) {
        payable(destinationWallet).transfer(address(this).balance);
    }
}

File 22 of 22 : MinimalConstants.sol
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;

import "../lib/interfaces/VRFCoordinatorV2Interface.sol";

abstract contract MinimalConstants  {
    ////////// API3 QRNG /////////////////

    struct RandomResult {
        uint256 randomNumber; // random number generated by api3.
        uint256 nomalizedRandomNumber; // random number % entriesLength + 1. So between 1 and entries.length
    }

    // event sent when the random number is generated by the VRF
    event RandomNumberCreated(
        uint256 indexed idFromMetawin,
        uint256 randomNumber,
        uint256 normalizedRandomNumber
    );

    struct RaffleInfo {
        uint256 id; // raffleId
        uint256 size; // length of the entries array of that raffle
    }

    mapping(uint256 => RandomResult) public requests;
    // map the requestId created by api3 with the raffle info passed as param when calling _getRandomNumber()
    mapping(bytes32 => RaffleInfo) public api3RaffleInfo;

    /////////////// END API3 QRNG //////////////

    error EntryNotAllowed(string errorType);
    error CreateRaffleError(string errorType);

    // Event sent when the owner of the nft stakes it for the raffle
    event RaffleStarted(uint256 indexed raffleId, address indexed seller);
    // Event sent when the raffle is finished (either early cashout or successful completion)
    event RaffleEnded(
        uint256 indexed raffleId,
        address indexed winner,
        uint256 amountRaised,
        uint256 randomNumber
    );
  
    // Event sent when a free entry is added by the operator
    event FreeEntry(
        uint256 indexed raffleId,
        address[] buyer,
        uint256 amount,
        uint256 currentSize
    );
    // Event sent when a raffle is asked to cancel by the operator
    event RaffleCancelled(uint256 indexed raffleId, uint256 amountRaised);
    // The raffle is closed successfully and the platform receives the fee
    event FeeTransferredToPlatform(
        uint256 indexed raffleId,
        uint256 amountTransferred
    );
    // When the raffle is asked to be cancelled and 30 days have passed, the operator can call a method
    // to transfer the remaining funds and this event is emitted
    event RemainingFundsTransferred(
        uint256 indexed raffleId,
        uint256 amountInWeis
    );
    // When the raffle is asked to be cancelled and 30 days have not passed yet, the players can call a
    // method to refund the amount spent on the raffle and this event is emitted
    event Refund(
        uint256 indexed raffleId,
        uint256 amountInWeis,
        address indexed player
    );
    event EarlyCashoutTriggered(uint256 indexed raffleId, uint256 amountRaised);
    event SetWinnerTriggered(uint256 indexed raffleId, uint256 amountRaised);
    // Emitted when an entry is cancelled
    event EntryCancelled(
        uint256 indexed raffleId,
        uint256 amountOfEntriesCanceled,
        address player
    );

    struct PriceStructure {
        uint48 id;
        uint48 numEntries;
        uint168 price;
    }
    mapping(uint256 => PriceStructure) public pricesList;

    // Every raffle has a funding structure.
    struct FundingStructure {
        uint128 minimumFundsInWeis;
        uint128 desiredFundsInWeis;
    }
    mapping(uint256 => FundingStructure) public fundingList;

    // In order to calculate the winner, in this struct is saved for each bought the data
    struct EntriesBought {
        uint48 currentEntriesLength; // current amount of entries bought in the raffle
        address player; // wallet address of the player
    }
    // every raffle has a sorted array of EntriesBought. Each element is created when calling
    // either buyEntry or giveBatchEntriesForFree
    mapping(uint256 => EntriesBought[]) public entriesList;


    // Map with the player wallets linked to a particular raffle + nft
    mapping(bytes32 => address) public requiredNFTWallets;

    mapping(bytes32 => uint48) public walletsCap;

    // key = collection address + raffleID
    mapping(bytes32 => bool) public whitelistCollections;

    mapping(bytes32 => bool) public freeEntriesPerWallet;

    struct NFTIdUsed {
        address collection;
        uint256 tokenId;
    }

    enum ENTRY_TYPE {
        ONLY_DIRECTLY,
        ONLY_EXTERNAL_CONTRACT,
        MIXED
    }

    // The operator role is operated by a backend application
    bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR");
    // requested by Hamburger. Role for the buy method of the hamburger (only that contract)
    bytes32 public constant MINTERCONTRACT_ROLE = keccak256("MINTERCONTRACT");

    // address of the wallet controlled by the platform that will receive the platform fee
    address payable public destinationWallet =
        payable(0x52a032cF59eA274f9D745f29b6D514fe95Ba192D);

}

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

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"_airnodeRrp","type":"address"},{"internalType":"address","name":"_usdcAddress","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AccessControlBadConfirmation","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"bytes32","name":"neededRole","type":"bytes32"}],"name":"AccessControlUnauthorizedAccount","type":"error"},{"inputs":[{"internalType":"string","name":"errorType","type":"string"}],"name":"CreateRaffleError","type":"error"},{"inputs":[{"internalType":"string","name":"errorType","type":"string"}],"name":"EntryNotAllowed","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"raffleId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountRaised","type":"uint256"}],"name":"EarlyCashoutTriggered","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"raffleId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountOfEntriesCanceled","type":"uint256"},{"indexed":false,"internalType":"address","name":"player","type":"address"}],"name":"EntryCancelled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"raffleId","type":"uint256"},{"indexed":true,"internalType":"address","name":"buyer","type":"address"},{"indexed":false,"internalType":"uint256","name":"currentSize","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"priceStructureId","type":"uint256"}],"name":"EntrySold","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"raffleId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountTransferred","type":"uint256"}],"name":"FeeTransferredToPlatform","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"raffleId","type":"uint256"},{"indexed":false,"internalType":"address[]","name":"buyer","type":"address[]"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"currentSize","type":"uint256"}],"name":"FreeEntry","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"raffleId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountRaised","type":"uint256"}],"name":"RaffleCancelled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"raffleId","type":"uint256"},{"indexed":true,"internalType":"address","name":"nftAddress","type":"address"},{"indexed":true,"internalType":"uint256","name":"nftId","type":"uint256"}],"name":"RaffleCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"raffleId","type":"uint256"},{"indexed":true,"internalType":"address","name":"winner","type":"address"},{"indexed":false,"internalType":"uint256","name":"amountRaised","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"randomNumber","type":"uint256"}],"name":"RaffleEnded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"raffleId","type":"uint256"},{"indexed":true,"internalType":"address","name":"seller","type":"address"}],"name":"RaffleStarted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"raffleId","type":"uint256"},{"indexed":false,"internalType":"enum Constants.STATUS","name":"newStatus","type":"uint8"}],"name":"RaffleStatusUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"idFromMetawin","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"randomNumber","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"normalizedRandomNumber","type":"uint256"}],"name":"RandomNumberCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"requestId","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"response","type":"uint256"}],"name":"ReceivedUint256","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"requestId","type":"bytes32"},{"indexed":false,"internalType":"uint256[]","name":"response","type":"uint256[]"}],"name":"ReceivedUint256Array","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"raffleId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountInWeis","type":"uint256"},{"indexed":true,"internalType":"address","name":"player","type":"address"}],"name":"Refund","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"raffleId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountInWeis","type":"uint256"}],"name":"RemainingFundsTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"requestId","type":"bytes32"}],"name":"RequestedUint256","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"requestId","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"size","type":"uint256"}],"name":"RequestedUint256Array","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"raffleId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountRaised","type":"uint256"}],"name":"SetWinnerTriggered","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"airnode","type":"address"},{"indexed":true,"internalType":"address","name":"sponsorWallet","type":"address"}],"name":"WithdrawalRequested","type":"event"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MINTERCONTRACT_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"OPERATOR_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_qrngUint256","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"_qrngUint256Array","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"airnode","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"airnodeRrp","outputs":[{"internalType":"contract IAirnodeRrpV0","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"api3RaffleInfo","outputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint256","name":"size","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_raffleId","type":"uint256"},{"internalType":"uint256","name":"_id","type":"uint256"}],"name":"buyEntry","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_raffleId","type":"uint256"}],"name":"cancelRaffle","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint128","name":"_desiredFundsInWeis","type":"uint128"},{"internalType":"address","name":"_prizeAddress","type":"address"},{"internalType":"uint256","name":"_prizeNumber","type":"uint256"},{"internalType":"uint128","name":"_minimumFundsInWeis","type":"uint128"},{"components":[{"internalType":"uint48","name":"id","type":"uint48"},{"internalType":"uint48","name":"numEntries","type":"uint48"},{"internalType":"uint168","name":"price","type":"uint168"}],"internalType":"struct MinimalConstants.PriceStructure[]","name":"_prices","type":"tuple[]"},{"internalType":"uint48","name":"_commissionInBasicPoints","type":"uint48"},{"internalType":"enum MinimalConstants.ENTRY_TYPE","name":"_entryType","type":"uint8"}],"name":"createRaffle","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"destinationWallet","outputs":[{"internalType":"address payable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"endpointIdUint256","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"endpointIdUint256Array","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"entriesList","outputs":[{"internalType":"uint48","name":"currentEntriesLength","type":"uint48"},{"internalType":"address","name":"player","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"expectingRequestWithIdToBeFulfilled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"freeEntriesPerWallet","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"requestId","type":"bytes32"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"fulfillUint256","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"fundingList","outputs":[{"internalType":"uint128","name":"minimumFundsInWeis","type":"uint128"},{"internalType":"uint128","name":"desiredFundsInWeis","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_raffleId","type":"uint256"}],"name":"getEntriesBought","outputs":[{"components":[{"internalType":"uint48","name":"currentEntriesLength","type":"uint48"},{"internalType":"address","name":"player","type":"address"}],"internalType":"struct MinimalConstants.EntriesBought[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_raffleId","type":"uint256"}],"name":"getRafflesEntryInfo","outputs":[{"components":[{"internalType":"bool","name":"requireWhitelisting","type":"bool"},{"internalType":"enum Constants.STATUS","name":"status","type":"uint8"},{"internalType":"uint48","name":"walletsCap","type":"uint48"},{"internalType":"uint48","name":"entriesLength","type":"uint48"},{"internalType":"uint128","name":"amountRaised","type":"uint128"},{"internalType":"address","name":"requiredTokenAddress","type":"address"},{"internalType":"uint256","name":"requiredTokenBalance","type":"uint256"}],"internalType":"struct Constants.EntryInfoStruct","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_raffleId","type":"uint256"},{"internalType":"uint256","name":"_normalizedRandomNumber","type":"uint256"}],"name":"getWinnerAddressFromRandom","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_raffleId","type":"uint256"},{"internalType":"address[]","name":"_freePlayers","type":"address[]"}],"name":"giveBatchEntriesForFree","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"pricesList","outputs":[{"internalType":"uint48","name":"id","type":"uint48"},{"internalType":"uint48","name":"numEntries","type":"uint48"},{"internalType":"uint168","name":"price","type":"uint168"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"raffles","outputs":[{"internalType":"uint256","name":"prizeNumber","type":"uint256"},{"internalType":"uint48","name":"platformPercentage","type":"uint48"},{"internalType":"address","name":"prizeAddress","type":"address"},{"internalType":"address","name":"winner","type":"address"},{"internalType":"address","name":"seller","type":"address"},{"internalType":"uint256","name":"randomNumber","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"rafflesEntryInfo","outputs":[{"internalType":"bool","name":"requireWhitelisting","type":"bool"},{"internalType":"enum Constants.STATUS","name":"status","type":"uint8"},{"internalType":"uint48","name":"walletsCap","type":"uint48"},{"internalType":"uint48","name":"entriesLength","type":"uint48"},{"internalType":"uint128","name":"amountRaised","type":"uint128"},{"internalType":"address","name":"requiredTokenAddress","type":"address"},{"internalType":"uint256","name":"requiredTokenBalance","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"callerConfirmation","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"requestWithdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"requests","outputs":[{"internalType":"uint256","name":"randomNumber","type":"uint256"},{"internalType":"uint256","name":"nomalizedRandomNumber","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"requiredNFTWallets","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"raffleId","type":"uint256"}],"name":"revertCloseRequested","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address payable","name":"_newAddress","type":"address"}],"name":"setDestinationAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_airnode","type":"address"},{"internalType":"bytes32","name":"_endpointIdUint256","type":"bytes32"},{"internalType":"bytes32","name":"_endpointIdUint256Array","type":"bytes32"},{"internalType":"address","name":"_sponsorWallet","type":"address"}],"name":"setRequestParameters","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_tokenAddress","type":"address"}],"name":"setRequiredTokenAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenAmount","type":"uint256"}],"name":"setRequiredTokenAmount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_raffleId","type":"uint256"}],"name":"setWinner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"sponsorWallet","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_raffleId","type":"uint256"}],"name":"stake","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_raffleId","type":"uint256"}],"name":"transferRemainingFunds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"usdcTokenContract","outputs":[{"internalType":"contract IBlacklistable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"walletsCap","outputs":[{"internalType":"uint48","name":"","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"whitelistCollections","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"}]

60c0604052600a80546001600160a01b0319167352a032cf59ea274f9d745f29b6d514fe95ba192d1790553480156200003757600080fd5b5060405162004303380380620043038339810160408190526200005a9162000219565b808280806200009e7f523a704056dcd17bcf83bed8b68c59416dac1119be77755efe3bde0a64e46e0c7313503b622abc0bd30a7e9687057df6e8c42fb9286200014d565b50620000ac6000336200014d565b50600b80546001600160a01b0319166d76a84fef008cdabe6409d2fe638b1790556001600160a01b0381166080819052604051632b77c09f60e21b81523060048201526001602482015263addf027c90604401600060405180830381600087803b1580156200011a57600080fd5b505af11580156200012f573d6000803e3d6000fd5b5050506001600160a01b0390941660a0525062000251945050505050565b6000828152602081815260408083206001600160a01b038516845290915281205460ff16620001f2576000838152602081815260408083206001600160a01b03861684529091529020805460ff19166001179055620001a93390565b6001600160a01b0316826001600160a01b0316847f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a4506001620001f6565b5060005b92915050565b80516001600160a01b03811681146200021457600080fd5b919050565b600080604083850312156200022d57600080fd5b6200023883620001fc565b91506200024860208401620001fc565b90509250929050565b60805160a0516140696200029a600039600081816109800152818161310a01526131eb01526000818161074401528181611117015281816119eb0152611e2f01526140696000f3fe6080604052600436106102935760003560e01c80635fba31711161015a578063a217fddf116100c1578063d547741f1161007a578063d547741f1461094e578063db848d491461096e578063ddba6e6b146109a2578063f369145514610a21578063f5b541a614610a41578063f720e70814610a6357600080fd5b8063a217fddf146108b1578063a36ff4d8146108c6578063a5d24269146108e6578063a694fc3a14610906578063b3423eec14610919578063bf90fb4e1461092e57600080fd5b806381d12c581161011357806381d12c58146107ba5780638499e1b2146107ee578063851244f71461081e57806391d148541461084e57806393a75d021461086e578063a19954af1461089b57600080fd5b80635fba3171146106b35780636d48f056146106d35780636ec3c3931461071c57806371bab666146107325780637c903fc0146107665780637fa4cacb1461079a57600080fd5b8063321bd1fd116101fe5780633a3956c2116101b75780633a3956c2146105a85780633ccfd60b146105db5780634006efe0146105f057806353b7a59b146106105780635675e4e4146106305780635d4bc0ce1461065057600080fd5b8063321bd1fd146104cb57806336568abe146104eb578063365e36581461050b57806336734e341461053857806336a418bf146105585780633718d90a1461058857600080fd5b80631a0187f5116102505780631a0187f5146103da5780632368549614610428578063248a9ca31461043b578063249aaf851461046b5780632f2ff15d1461048b578063317f3059146104ab57600080fd5b8063013805c51461029857806301ffc9a7146102e3578063039be5581461031357806306bb8b531461037457806307b9fc57146103965780630df71602146103ba575b600080fd5b3480156102a457600080fd5b506102b86102b3366004613905565b610aaf565b6040805165ffffffffffff90931683526001600160a01b039091166020830152015b60405180910390f35b3480156102ef57600080fd5b506103036102fe366004613927565b610af7565b60405190151581526020016102da565b34801561031f57600080fd5b5061035461032e366004613951565b6004602052600090815260409020546001600160801b0380821691600160801b90041682565b604080516001600160801b039384168152929091166020830152016102da565b34801561038057600080fd5b5061039461038f366004613951565b610b2e565b005b3480156103a257600080fd5b506103ac60115481565b6040519081526020016102da565b3480156103c657600080fd5b506103946103d5366004613951565b610b4c565b3480156103e657600080fd5b506104106103f5366004613951565b6006602052600090815260409020546001600160a01b031681565b6040516001600160a01b0390911681526020016102da565b610394610436366004613905565b610bc1565b34801561044757600080fd5b506103ac610456366004613951565b60009081526020819052604090206001015490565b34801561047757600080fd5b5061039461048636600461398f565b610bdf565b34801561049757600080fd5b506103946104a63660046139ac565b610c1a565b3480156104b757600080fd5b506103946104c6366004613951565b610c3f565b3480156104d757600080fd5b506103ac6104e6366004613951565b610e16565b3480156104f757600080fd5b506103946105063660046139ac565b610e37565b34801561051757600080fd5b5061052b610526366004613951565b610e6a565b6040516102da91906139dc565b34801561054457600080fd5b50610394610553366004613a52565b610ef8565b34801561056457600080fd5b50610303610573366004613951565b60086020526000908152604090205460ff1681565b34801561059457600080fd5b506103946105a3366004613b23565b61110c565b3480156105b457600080fd5b506105c86105c3366004613951565b6112ef565b6040516102da9796959493929190613bd7565b3480156105e757600080fd5b50610394611365565b3480156105fc57600080fd5b5061039461060b366004613951565b6113ad565b34801561061c57600080fd5b50600a54610410906001600160a01b031681565b34801561063c57600080fd5b5061039461064b366004613c33565b6114e3565b34801561065c57600080fd5b5061067061066b366004613951565b611529565b6040805196875265ffffffffffff90951660208701526001600160a01b0393841694860194909452908216606085015216608083015260a082015260c0016102da565b3480156106bf57600080fd5b506103946106ce366004613951565b611589565b3480156106df57600080fd5b506107076106ee366004613951565b6002602052600090815260409020805460019091015482565b604080519283526020830191909152016102da565b34801561072857600080fd5b506103ac60145481565b34801561073e57600080fd5b506104107f000000000000000000000000000000000000000000000000000000000000000081565b34801561077257600080fd5b506103ac7fde5ee446972f4e39ab62c03aa34b2096680a875c3fdb3eb2f947cbb93341c05881565b3480156107a657600080fd5b506103946107b536600461398f565b6115ac565b3480156107c657600080fd5b506107076107d5366004613951565b6001602081905260009182526040909120805491015482565b3480156107fa57600080fd5b50610303610809366004613951565b60096020526000908152604090205460ff1681565b34801561082a57600080fd5b50610303610839366004613951565b60166020526000908152604090205460ff1681565b34801561085a57600080fd5b506103036108693660046139ac565b6115da565b34801561087a57600080fd5b5061088e610889366004613951565b611603565b6040516102da9190613c7d565b3480156108a757600080fd5b506103ac60125481565b3480156108bd57600080fd5b506103ac600081565b3480156108d257600080fd5b50601054610410906001600160a01b031681565b3480156108f257600080fd5b506103ac610901366004613d2e565b6116d6565b610394610914366004613951565b61177a565b34801561092557600080fd5b506103946119b3565b34801561093a57600080fd5b50601354610410906001600160a01b031681565b34801561095a57600080fd5b506103946109693660046139ac565b611a4c565b34801561097a57600080fd5b506104107f000000000000000000000000000000000000000000000000000000000000000081565b3480156109ae57600080fd5b506109f36109bd366004613951565b6003602052600090815260409020805460019091015465ffffffffffff80831692600160301b900416906001600160a81b031683565b6040805165ffffffffffff94851681529390921660208401526001600160a81b0316908201526060016102da565b348015610a2d57600080fd5b50610410610a3c366004613905565b611a71565b348015610a4d57600080fd5b506103ac60008051602061401483398151915281565b348015610a6f57600080fd5b50610a98610a7e366004613951565b60076020526000908152604090205465ffffffffffff1681565b60405165ffffffffffff90911681526020016102da565b60056020528160005260406000208181548110610acb57600080fd5b60009182526020909120015465ffffffffffff81169250600160301b90046001600160a01b0316905082565b60006001600160e01b03198216637965db0b60e01b1480610b2857506301ffc9a760e01b6001600160e01b03198316145b92915050565b600080516020614014833981519152610b4681611b4b565b50600d55565b600080516020614014833981519152610b6481611b4b565b6000610b6f83611b58565b90506000610b7b611dca565b9050610bbb84836060015165ffffffffffff1683604080518082018252938452602080850193845260009283526002905290209151825551600190910155565b50505050565b6000610bcd8383611ee8565b9050610bda818484612460565b505050565b600080516020614014833981519152610bf781611b4b565b50600c80546001600160a01b0319166001600160a01b0392909216919091179055565b600082815260208190526040902060010154610c3581611b4b565b610bbb83836125af565b600080516020614014833981519152610c5781611b4b565b6000600f8381548110610c6c57610c6c613e02565b60009182526020909120600390910201905060068154610100900460ff166007811115610c9b57610c9b613b9f565b1480610cc1575060038154610100900460ff166007811115610cbf57610cbf613b9f565b145b610d015760405162461bcd60e51b815260206004820152600c60248201526b57726f6e672073746174757360a01b60448201526064015b60405180910390fd5b805461ff00191661030017808255600a546040516000926001600160a01b0390921691600160701b90046001600160801b0316908381818185875af1925050503d8060008114610d6d576040519150601f19603f3d011682016040523d82523d6000602084013e610d72565b606091505b5050905080610db95760405162461bcd60e51b81526020600482015260136024820152724661696c2073656e642045746820746f204d5760681b6044820152606401610cf8565b8154604051600160701b9091046001600160801b0316815284907fcdef6558dae40f2699846eedf449462daab85b1224ad7f077569ba91aaa949259060200160405180910390a2508054600160701b600160f01b03191690555050565b60158181548110610e2657600080fd5b600091825260209091200154905081565b6001600160a01b0381163314610e605760405163334bd91960e11b815260040160405180910390fd5b610bda8282612641565b606060056000838152602001908152602001600020805480602002602001604051908101604052809291908181526020016000905b82821015610eed576000848152602090819020604080518082019091529084015465ffffffffffff81168252600160301b90046001600160a01b031681830152825260019092019101610e9f565b505050509050919050565b600080516020614014833981519152610f1081611b4b565b6000600f8481548110610f2557610f25613e02565b60009182526020909120600390910201905060018154610100900460ff166007811115610f5457610f54613b9f565b14610fa15760405162461bcd60e51b815260206004820152601960248201527f526166666c65206973206e6f7420696e206163636570746564000000000000006044820152606401610cf8565b82516000805b8281101561107d576000868281518110610fc357610fc3613e02565b6020026020010151905060006040518060400160405280848860000160089054906101000a900465ffffffffffff1665ffffffffffff166110049190613e2e565b61100f906001613e2e565b65ffffffffffff90811682526001600160a01b0394851660209283015260008c81526005835260408120805460018181018355918352918490208551920180549590940151909616600160301b026001600160d01b0319909416911617919091179055509182019101610fa7565b50825461109a908290600160401b900465ffffffffffff16613e41565b835465ffffffffffff60401b1916600160401b65ffffffffffff92831681029190911780865560405189937f4da4f5fab0816c65315b6f5d15f879f96b98661133d7b3787788f291367604fb936110fc938b9389939290910490911690613e67565b60405180910390a2505050505050565b336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461117d5760405162461bcd60e51b8152602060048201526016602482015275043616c6c6572206e6f74204169726e6f6465205252560541b6044820152606401610cf8565b60008381526016602052604090205460ff166111d25760405162461bcd60e51b81526020600482015260146024820152732932b8bab2b9ba1024a2103737ba1035b737bbb760611b6044820152606401610cf8565b6000838152601660205260408120805460ff191690556111f482840184613951565b601481905560405181815290915084907f1ca47bacd454c26163f84eff4aa514e291ba9fa67ad6029e39567c122bbed30f9060200160405180910390a260008481526002602090815260408083208151808301909252805482526001015491810182905291906112649084613ee2565b61126f906001613e2e565b604080518082018252858152602080820184815286516000908152600180845290859020845181559151910155855183518881529182018590529394509092917f7c40e661b8212d0c4f60ac6e6ebed99c28680c7b3ede5b82f3b0254543f62fca910160405180910390a282516112e690836126ac565b50505050505050565b600f81815481106112ff57600080fd5b600091825260209091206003909102018054600182015460029092015460ff80831694506101008304169262010000830465ffffffffffff90811693600160401b810490911692600160701b9091046001600160801b0316916001600160a01b03169087565b600061137081611b4b565b600a546040516001600160a01b03909116904780156108fc02916000818181858888f193505050501580156113a9573d6000803e3d6000fd5b5050565b6000805160206140148339815191526113c581611b4b565b6004600f83815481106113da576113da613e02565b6000918252602090912060039091020154610100900460ff16600781111561140457611404613b9f565b146114605760405162461bcd60e51b815260206004820152602660248201527f526166666c65206973206e6f7420696e20636c6f73652072657175657374656460448201526520737461746560d01b6064820152608401610cf8565b6001600f838154811061147557611475613e02565b60009182526020909120600390910201805461ff0019166101008360078111156114a1576114a1613b9f565b0217905550817fc1191e7178b58ad510709587719f39ec315fa79e81ee7ba5c5ef3c894e94a65160016040516114d79190613ef6565b60405180910390a25050565b60006114ee81611b4b565b50601080546001600160a01b039586166001600160a01b03199182161790915560119390935560129190915560138054919093169116179055565b600e818154811061153957600080fd5b60009182526020909120600590910201805460018201546002830154600384015460049094015492945065ffffffffffff8216936001600160a01b03600160301b90930483169391831692169086565b6000805160206140148339815191526115a181611b4b565b6113a98260026126d1565b60006115b781611b4b565b50600a80546001600160a01b0319166001600160a01b0392909216919091179055565b6000918252602082815260408084206001600160a01b0393909316845291905290205460ff1690565b61160b6138c7565b600f828154811061161e5761161e613e02565b60009182526020918290206040805160e08101909152600390920201805460ff80821615158452929391929184019161010090910416600781111561166557611665613b9f565b600781111561167657611676613b9f565b8152815465ffffffffffff62010000820481166020840152600160401b82041660408301526001600160801b03600160701b90910416606082015260018201546001600160a01b0316608082015260029091015460a09091015292915050565b60006000805160206140148339815191526116f081611b4b565b6113888465ffffffffffff16111561174157604051636b221d4560e11b81526020600482015260136024820152720c6dedadad2e6e6d2dedc40e8dede40d0d2ced606b1b6044820152606401610cf8565b6117518a8a8a8a8a8a8a8a6128b4565b611759612d63565b600e5460009061176b90600190613f04565b9b9a5050505050505050505050565b6000600e828154811061178f5761178f613e02565b906000526020600020906005020190506000600f83815481106117b4576117b4613e02565b60009182526020822060039091020191508154610100900460ff1660078111156117e0576117e0613b9f565b146118225760405162461bcd60e51b8152602060048201526012602482015271149859999b19481b9bdd0810d4915055115160721b6044820152606401610cf8565b60018201548254604051636eb1769f60e11b8152336004820152306024820152600160301b9092046001600160a01b031691829063dd62ed3e90604401602060405180830381865afa15801561187c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118a09190613f17565b10156118e05760405162461bcd60e51b815260206004820152600f60248201526e20b63637bbb0b731b29022b93937b960891b6044820152606401610cf8565b815461010061ff00199091161782556003830180546001600160a01b0319163390811790915583546040516323b872dd60e01b8152600481019290925230602483015260448201526001600160a01b038216906323b872dd906064016020604051808303816000875af115801561195b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061197f9190613f30565b50604051339085907f8bb509eedfd1c4847b0a8a2b4493cf2ebb9970dc367e477cd2a8523e212dc1db90600090a350505050565b60006119be81611b4b565b601054601354604051631d414cbd60e01b81526001600160a01b03928316600482015290821660248201527f000000000000000000000000000000000000000000000000000000000000000090911690631d414cbd90604401600060405180830381600087803b158015611a3157600080fd5b505af1158015611a45573d6000803e3d6000fd5b5050505050565b600082815260208190526040902060010154611a6781611b4b565b610bbb8383612641565b60008281526005602052604081208190611a8b9084612ec4565b60008581526005602052604081208054929350909183908110611ab057611ab0613e02565b9060005260206000200160000160069054906101000a90046001600160a01b03169050600e8581548110611ae657611ae6613e02565b6000918252602090912060059091020160010154600160301b90046001600160a01b031673a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4814611b3857611b2f818387612f92565b92505050610b28565b611b2f8183876130d6565b505092915050565b611b55813361330c565b50565b611b606138c7565b6000600f8381548110611b7557611b75613e02565b60009182526020808320868452600490915260409092206003909102909101915060018254610100900460ff166007811115611bb357611bb3613b9f565b14611bf95760405162461bcd60e51b8152602060048201526016602482015275526166666c6520696e2077726f6e672073746174757360501b6044820152606401610cf8565b805482546001600160801b03918216600160701b9091049091161015611c615760405162461bcd60e51b815260206004820152601760248201527f4e6f7420656e6f7567682066756e6473207261697365640000000000000000006044820152606401610cf8565b81548154600160701b9091046001600160801b03908116600160801b909204161115611ccf5760405162461bcd60e51b815260206004820152601860248201527f446573697265642066756e6473206e6f742072616973656400000000000000006044820152606401610cf8565b815461ff0019166104001780835560408051600160701b9092046001600160801b031682525185917ff2be214756d2fbc1e781d10809ddef33000009d805be55356bb348134ce21c68919081900360200190a26040805160e08101909152825460ff8082161515835284916020840191610100909104166007811115611d5757611d57613b9f565b6007811115611d6857611d68613b9f565b8152815465ffffffffffff62010000820481166020840152600160401b82041660408301526001600160801b03600160701b90910416606082015260018201546001600160a01b0316608082015260029091015460a090910152949350505050565b601054601154601354604051636e6be03f60e01b81526001600160a01b0393841660048201526024810192909252306044830181905290831660648301526084820152631b8c6c8560e11b60a482015260e060c4820152600060e482018190529182917f000000000000000000000000000000000000000000000000000000000000000090911690636e6be03f90610104016020604051808303816000875af1158015611e7b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e9f9190613f17565b600081815260166020526040808220805460ff191660011790555191925082917fcba2da2f3c0c732a104019a3104936397dde7343964c1518ceb760052e4537b19190a2919050565b611ef06138c7565b6000600f8481548110611f0557611f05613e02565b60009182526020909120600390910201905060018154610100900460ff166007811115611f3457611f34613b9f565b14611f745760405163efeb42cf60e01b815260206004820152600f60248201526e139bdd081a5b881050d0d154151151608a1b6044820152606401610cf8565b6000838152600360209081526040918290208251606081018452815465ffffffffffff808216808452600160301b90920416938201939093526001909101546001600160a81b03169281019290925285146120075760405163efeb42cf60e01b81526020600482015260126024820152711259081b9bdd081a5b881c9859999b19525960721b6044820152606401610cf8565b602081015160408201516001600160a81b031634146120695760405163efeb42cf60e01b815260206004820152601760248201527f6d73672e76616c7565206e6f74207468652070726963650000000000000000006044820152606401610cf8565b81604001516001600160a81b031660000361217b573233146120ce5760405163efeb42cf60e01b815260206004820152601760248201527f74782e6f726967696e20213d206d73672e73656e6465720000000000000000006044820152606401610cf8565b6040805133602082015290810187905260009060600160408051601f1981840301815291815281516020928301206000818152600990935291205490915060ff1615156001036121615760405163efeb42cf60e01b815260206004820152601d60248201527f506c6179657220616c726561647920676f74206672656520656e7472790000006044820152606401610cf8565b6000908152600960205260409020805460ff191660011790555b60028301541561223e57600183015460028401546040516370a0823160e01b81523360048201526001600160a01b039092169182906370a0823190602401602060405180830381865afa1580156121d6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121fa9190613f17565b101561223c5760405162461bcd60e51b81526020600482015260116024820152704e6f7420656e6f75676820746f6b656e7360781b6044820152606401610cf8565b505b825460408051808201909152600160401b90910465ffffffffffff1690600090806122698585613e41565b65ffffffffffff90811682523360209283015260008b815260058352604081208054600181018255818352848320865191018054958701516001600160a01b0316600160301b026001600160d01b03199096169190941617939093179091558a8152815492935090916122de576122de613e02565b600091825260209091200180546001600160d01b0319169055845434908690600e9061231b908490600160701b90046001600160801b0316613f52565b92506101000a8154816001600160801b0302191690836001600160801b03160217905550828261234b9190613e41565b855465ffffffffffff60401b1916600160401b65ffffffffffff928316810291909117808855604080519290910490921681526020810189905233918a917fd746af8dc82f9bed98cea0fe0264eb1c3d2e5f7bcc77fc5efb429c79df407887910160405180910390a36040805160e08101909152855460ff80821615158352879160208401916101009091041660078111156123e9576123e9613b9f565b60078111156123fa576123fa613b9f565b8152815462010000810465ffffffffffff9081166020840152600160401b8204166040830152600160701b90046001600160801b0316606082015260018201546001600160a01b0316608082015260029091015460a09091015298975050505050505050565b604083015165ffffffffffff1615610bda576000818152600360209081526040808320815160608082018452825465ffffffffffff8082168452600160301b90910481168387019081526001909401546001600160a81b03168386015284518087018a90523381870152855180820387018152920185528151918601919091208087526007909552948390205492880151915190949283169291909116906125089083613e41565b65ffffffffffff1611156125555760405163efeb42cf60e01b815260206004820152601360248201527215d85b1b195d08185b1c9958591e481d5cd959606a1b6044820152606401610cf8565b6020808401516000848152600790925260409091205461257d919065ffffffffffff16613e41565b600092835260076020526040909220805465ffffffffffff191665ffffffffffff909316929092179091555050505050565b60006125bb83836115da565b612639576000838152602081815260408083206001600160a01b03861684529091529020805460ff191660011790556125f13390565b6001600160a01b0316826001600160a01b0316847f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a4506001610b28565b506000610b28565b600061264d83836115da565b15612639576000838152602081815260408083206001600160a01b0386168085529252808320805460ff1916905551339286917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a4506001610b28565b60006126b88383613345565b9050610bda81604001518260000151836060015161372b565b6000600e83815481106126e6576126e6613e02565b906000526020600020906005020190506000600f848154811061270b5761270b613e02565b60009182526020909120600390910201905060018154610100900460ff16600781111561273a5761273a613b9f565b1480612760575060008154610100900460ff16600781111561275e5761275e613b9f565b145b61279b5760405162461bcd60e51b815260206004820152600c60248201526b57726f6e672073746174757360a01b6044820152606401610cf8565b60018154610100900460ff1660078111156127b8576127b8613b9f565b0361285b578265ffffffffffff166000036127f7576001820154825460038401546127f7926001600160a01b03600160301b90910481169291166137a1565b8265ffffffffffff16600103612821578154600383015461282191906001600160a01b0316613812565b8265ffffffffffff1660020361285b5760018201548254600384015461285b926001600160a01b03600160301b909104811692911661372b565b805461ff0019166106001780825560408051600160701b9092046001600160801b031682525185917fd512a34b0f0618078770fcd85d974df1ab46a7882e8b3d45aa91764f4961aed2919081900360200190a250505050565b6040805160c08101825287815265ffffffffffff808516602083019081526001600160a01b03808c169484019485526000606085018181526080860182815260a08701838152600e8054600181810183558287528a5160059092027fbb7b4a454dc3493923482f07822329ed19e8244eff582cc204f8554c3620c3fd81019290925597517fbb7b4a454dc3493923482f07822329ed19e8244eff582cc204f8554c3620c3fe820180549c518916600160301b026001600160d01b0319909d1691909a16179a909a1790975591517fbb7b4a454dc3493923482f07822329ed19e8244eff582cc204f8554c3620c3ff890180549186166001600160a01b031992831617905590517fbb7b4a454dc3493923482f07822329ed19e8244eff582cc204f8554c3620c400890180549190951691161790925590517fbb7b4a454dc3493923482f07822329ed19e8244eff582cc204f8554c3620c401909501949094559054919291612a229190613f04565b90506000859003612a6257604051636b221d4560e11b81526020600482015260096024820152684e6f2070726963657360b81b6044820152606401610cf8565b60005b85811015612c1557868682818110612a7f57612a7f613e02565b9050606002016020016020810190612a979190613f72565b65ffffffffffff16600003612ae157604051636b221d4560e11b815260206004820152600f60248201526e06e756d456e7472696573206973203608c1b6044820152606401610cf8565b600060405180606001604052808465ffffffffffff168152602001898985818110612b0e57612b0e613e02565b9050606002016020016020810190612b269190613f72565b65ffffffffffff168152602001898985818110612b4557612b45613e02565b9050606002016040016020810190612b5d9190613f8d565b6001600160a81b03169052905080600360008a8a86818110612b8157612b81613e02565b612b979260206060909202019081019150613f72565b65ffffffffffff9081168252602080830193909352604091820160002084518154948601518316600160301b026bffffffffffffffffffffffff199095169216919091179290921782559190910151600191820180546001600160a81b039092166001600160a81b0319909216919091179055919091019050612a65565b50604080518082019091526001600160801b0380891682528b166020820152600e54600490600090612c4990600190613f04565b8152602080820192909252604001600020825192909101516001600160801b03908116600160801b029216919091179055600e5488906001600160a01b038b1690612c9690600190613f04565b6040517f81781e053ec72aa8731479536c4da8f819ef3283d2c0dea5c4f0d938bed8489590600090a460408051808201825260018082523360208084019182526000868152600582529485208054938401815580865290852084519301805492516001600160a01b0316600160301b026001600160d01b031990931665ffffffffffff949094169390931791909117909155838352805491929091612d3d57612d3d613e02565b600091825260209091200180546001600160d01b03191690555050505050505050505050565b6040805160e0810182526000808252602082018181529282018190526060820181905260808201819052600c546001600160a01b031660a0830152600d5460c0830152600f80546001810182559152815160039091027f8d1108e10bcb7c27dddfc02ed9d693a074039d026cf4ea4240b40f7d581ac80201805491151560ff198316811782559351929384939192839161ff00191661ffff1990911617610100836007811115612e1557612e15613b9f565b021790555060408201518154606084015160808501516dffffffffffffffffffffffff0000199092166201000065ffffffffffff9485160265ffffffffffff60401b191617600160401b939091169290920291909117600160701b600160f01b031916600160701b6001600160801b039092169190910217815560a08201516001820180546001600160a01b0319166001600160a01b0390921691909117905560c09091015160029091015550565b81546000908103612ed757506000610b28565b82546000905b80821015612f3c576000612ef183836138ac565b905084868281548110612f0657612f06613e02565b60009182526020909120015465ffffffffffff161115612f2857809150612f36565b612f33816001613e2e565b92505b50612edd565b600082118015612f7a57508385612f54600185613f04565b81548110612f6457612f64613e02565b60009182526020909120015465ffffffffffff16145b15612f8a57611b2f600183613f04565b509050610b28565b60006001600160a01b03841615612faa5750826130cf565b6000835b81158015612ff557506000848152600560205260408120805483908110612fd757612fd7613e02565b600091825260209091200154600160301b90046001600160a01b0316145b1561304357806000036130245760008481526005602052604090205461301d90600190613f04565b9050613032565b61302f600182613f04565b90505b84810361303e57600191505b612fae565b81156130895760405162461bcd60e51b8152602060048201526015602482015274105b1b081d5cd95c9cc8189b1858dadb1a5cdd1959605a1b6044820152606401610cf8565b60008481526005602052604090208054829081106130a9576130a9613e02565b600091825260209091200154600160301b90046001600160a01b031692506130cf915050565b9392505050565b60006001600160a01b03841615801590613177575060405163fe575a8760e01b81526001600160a01b0385811660048301527f0000000000000000000000000000000000000000000000000000000000000000169063fe575a8790602401602060405180830381865afa158015613151573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131759190613f30565b155b156131835750826130cf565b6000835b816130435760008481526005602052604081208054839081106131ac576131ac613e02565b600091825260209091200154600160301b90046001600160a01b03161480159061329e5750600084815260056020526040902080546001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169163fe575a87918490811061322257613222613e02565b60009182526020909120015460405160e083901b6001600160e01b0319168152600160301b9091046001600160a01b03166004820152602401602060405180830381865afa158015613278573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061329c9190613f30565b155b156132c35760008481526005602052604090208054829081106130a9576130a9613e02565b806000036132ed576000848152600560205260409020546132e690600190613f04565b90506132fb565b6132f8600182613f04565b90505b84810361330757600191505b613187565b61331682826115da565b6113a95760405163e2517d3f60e01b81526001600160a01b038216600482015260248101839052604401610cf8565b6040805160c081018252600080825260208201819052918101829052606081018290526080810182905260a08101919091526000600e848154811061338c5761338c613e02565b906000526020600020906005020190506000600f85815481106133b1576133b1613e02565b60009182526020909120600390910201905060028154610100900460ff1660078111156133e0576133e0613b9f565b1480613406575060048154610100900460ff16600781111561340457613404613b9f565b145b61344b5760405162461bcd60e51b8152602060048201526016602482015275526166666c6520696e2077726f6e672073746174757360501b6044820152606401610cf8565b6004820184905561345c8585611a71565b6002830180546001600160a01b0319166001600160a01b0392909216919091179055805461ff001916610500178082556001830154600091612710916134bc9165ffffffffffff90911690600160701b90046001600160801b0316613fb6565b6134c69190613fd9565b82546001600160801b0391821692506000916134ec918491600160701b90910416613f04565b60038501546040519192506000916001600160a01b039091169083908381818185875af1925050503d8060008114613540576040519150601f19603f3d011682016040523d82523d6000602084013e613545565b606091505b505090508061358d5760405162461bcd60e51b81526020600482015260146024820152732330b4b632b2103a379039b2b7321022ba3432b960611b6044820152606401610cf8565b600a546040516000916001600160a01b03169085908381818185875af1925050503d80600081146135da576040519150601f19603f3d011682016040523d82523d6000602084013e6135df565b606091505b50509050806136285760405162461bcd60e51b81526020600482015260156024820152744661696c65642073656e642045746820746f204d5760581b6044820152606401610cf8565b887f7378e11c2b0ec7514bbf7ba369980eedcba0bca03e116dc9e7138f7748e211d68560405161365a91815260200190565b60405180910390a26002860154855460408051600160701b9092046001600160801b03168252602082018b90526001600160a01b03909216918b917fe0b2a72a0644b093aac275024c05c7c28851a0b572557a32241d13634a0f3e08910160405180910390a350506040805160c08101825285548152600186015465ffffffffffff81166020830152600160301b90046001600160a01b03908116928201929092526002860154821660608201526003860154909116608082015260049094015460a0850152509195945050505050565b60405163a9059cbb60e01b81526001600160a01b0382811660048301526024820184905284919082169063a9059cbb906044016020604051808303816000875af115801561377d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a459190613f30565b6040516323b872dd60e01b81523060048201526001600160a01b038281166024830152604482018490528491908216906323b872dd90606401600060405180830381600087803b1580156137f457600080fd5b505af1158015613808573d6000803e3d6000fd5b5050505050505050565b6000816001600160a01b03168360405160006040518083038185875af1925050503d806000811461385f576040519150601f19603f3d011682016040523d82523d6000602084013e613864565b606091505b5050905080610bda5760405162461bcd60e51b81526020600482015260146024820152732330b4b632b2103a379039b2b7321022ba3432b960611b6044820152606401610cf8565b60006138bb6002848418613fff565b6130cf90848416613e2e565b6040805160e0810190915260008082526020820190815260006020820181905260408201819052606082018190526080820181905260a09091015290565b6000806040838503121561391857600080fd5b50508035926020909101359150565b60006020828403121561393957600080fd5b81356001600160e01b0319811681146130cf57600080fd5b60006020828403121561396357600080fd5b5035919050565b6001600160a01b0381168114611b5557600080fd5b803561398a8161396a565b919050565b6000602082840312156139a157600080fd5b81356130cf8161396a565b600080604083850312156139bf57600080fd5b8235915060208301356139d18161396a565b809150509250929050565b602080825282518282018190526000919060409081850190868401855b82811015613a2f578151805165ffffffffffff1685528601516001600160a01b03168685015292840192908501906001016139f9565b5091979650505050505050565b634e487b7160e01b600052604160045260246000fd5b60008060408385031215613a6557600080fd5b8235915060208084013567ffffffffffffffff80821115613a8557600080fd5b818601915086601f830112613a9957600080fd5b813581811115613aab57613aab613a3c565b8060051b604051601f19603f83011681018181108582111715613ad057613ad0613a3c565b604052918252848201925083810185019189831115613aee57600080fd5b938501935b82851015613b1357613b048561397f565b84529385019392850192613af3565b8096505050505050509250929050565b600080600060408486031215613b3857600080fd5b83359250602084013567ffffffffffffffff80821115613b5757600080fd5b818601915086601f830112613b6b57600080fd5b813581811115613b7a57600080fd5b876020828501011115613b8c57600080fd5b6020830194508093505050509250925092565b634e487b7160e01b600052602160045260246000fd5b60088110613bd357634e487b7160e01b600052602160045260246000fd5b9052565b871515815260e08101613bed6020830189613bb5565b65ffffffffffff96871660408301529490951660608601526001600160801b039290921660808501526001600160a01b031660a084015260c09092019190915292915050565b60008060008060808587031215613c4957600080fd5b8435613c548161396a565b935060208501359250604085013591506060850135613c728161396a565b939692955090935050565b81511515815260208083015160e0830191613c9a90840182613bb5565b50604083015165ffffffffffff808216604085015280606086015116606085015250506001600160801b03608084015116608083015260018060a01b0360a08401511660a083015260c083015160c083015292915050565b80356001600160801b038116811461398a57600080fd5b803565ffffffffffff8116811461398a57600080fd5b80356003811061398a57600080fd5b60008060008060008060008060e0898b031215613d4a57600080fd5b613d5389613cf2565b97506020890135613d638161396a565b965060408901359550613d7860608a01613cf2565b9450608089013567ffffffffffffffff80821115613d9557600080fd5b818b0191508b601f830112613da957600080fd5b813581811115613db857600080fd5b8c6020606083028501011115613dcd57600080fd5b602083019650809550505050613de560a08a01613d09565b9150613df360c08a01613d1f565b90509295985092959890939650565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b80820180821115610b2857610b28613e18565b65ffffffffffff818116838216019080821115613e6057613e60613e18565b5092915050565b606080825284519082018190526000906020906080840190828801845b82811015613ea95781516001600160a01b031684529284019290840190600101613e84565b5050506020840195909552505065ffffffffffff91909116604090910152919050565b634e487b7160e01b600052601260045260246000fd5b600082613ef157613ef1613ecc565b500690565b60208101610b288284613bb5565b81810381811115610b2857610b28613e18565b600060208284031215613f2957600080fd5b5051919050565b600060208284031215613f4257600080fd5b815180151581146130cf57600080fd5b6001600160801b03818116838216019080821115613e6057613e60613e18565b600060208284031215613f8457600080fd5b6130cf82613d09565b600060208284031215613f9f57600080fd5b81356001600160a81b03811681146130cf57600080fd5b6001600160801b03818116838216028082169190828114611b4357611b43613e18565b60006001600160801b0380841680613ff357613ff3613ecc565b92169190910492915050565b60008261400e5761400e613ecc565b50049056fe523a704056dcd17bcf83bed8b68c59416dac1119be77755efe3bde0a64e46e0ca2646970667358221220a2553f65afcd566c299120d42df15f78ad86283c8b8161371a9e6cbef3b03c2064736f6c63430008180033000000000000000000000000a0ad79d995ddeeb18a14eaef56a549a04e3aa1bd000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48

Deployed Bytecode

0x6080604052600436106102935760003560e01c80635fba31711161015a578063a217fddf116100c1578063d547741f1161007a578063d547741f1461094e578063db848d491461096e578063ddba6e6b146109a2578063f369145514610a21578063f5b541a614610a41578063f720e70814610a6357600080fd5b8063a217fddf146108b1578063a36ff4d8146108c6578063a5d24269146108e6578063a694fc3a14610906578063b3423eec14610919578063bf90fb4e1461092e57600080fd5b806381d12c581161011357806381d12c58146107ba5780638499e1b2146107ee578063851244f71461081e57806391d148541461084e57806393a75d021461086e578063a19954af1461089b57600080fd5b80635fba3171146106b35780636d48f056146106d35780636ec3c3931461071c57806371bab666146107325780637c903fc0146107665780637fa4cacb1461079a57600080fd5b8063321bd1fd116101fe5780633a3956c2116101b75780633a3956c2146105a85780633ccfd60b146105db5780634006efe0146105f057806353b7a59b146106105780635675e4e4146106305780635d4bc0ce1461065057600080fd5b8063321bd1fd146104cb57806336568abe146104eb578063365e36581461050b57806336734e341461053857806336a418bf146105585780633718d90a1461058857600080fd5b80631a0187f5116102505780631a0187f5146103da5780632368549614610428578063248a9ca31461043b578063249aaf851461046b5780632f2ff15d1461048b578063317f3059146104ab57600080fd5b8063013805c51461029857806301ffc9a7146102e3578063039be5581461031357806306bb8b531461037457806307b9fc57146103965780630df71602146103ba575b600080fd5b3480156102a457600080fd5b506102b86102b3366004613905565b610aaf565b6040805165ffffffffffff90931683526001600160a01b039091166020830152015b60405180910390f35b3480156102ef57600080fd5b506103036102fe366004613927565b610af7565b60405190151581526020016102da565b34801561031f57600080fd5b5061035461032e366004613951565b6004602052600090815260409020546001600160801b0380821691600160801b90041682565b604080516001600160801b039384168152929091166020830152016102da565b34801561038057600080fd5b5061039461038f366004613951565b610b2e565b005b3480156103a257600080fd5b506103ac60115481565b6040519081526020016102da565b3480156103c657600080fd5b506103946103d5366004613951565b610b4c565b3480156103e657600080fd5b506104106103f5366004613951565b6006602052600090815260409020546001600160a01b031681565b6040516001600160a01b0390911681526020016102da565b610394610436366004613905565b610bc1565b34801561044757600080fd5b506103ac610456366004613951565b60009081526020819052604090206001015490565b34801561047757600080fd5b5061039461048636600461398f565b610bdf565b34801561049757600080fd5b506103946104a63660046139ac565b610c1a565b3480156104b757600080fd5b506103946104c6366004613951565b610c3f565b3480156104d757600080fd5b506103ac6104e6366004613951565b610e16565b3480156104f757600080fd5b506103946105063660046139ac565b610e37565b34801561051757600080fd5b5061052b610526366004613951565b610e6a565b6040516102da91906139dc565b34801561054457600080fd5b50610394610553366004613a52565b610ef8565b34801561056457600080fd5b50610303610573366004613951565b60086020526000908152604090205460ff1681565b34801561059457600080fd5b506103946105a3366004613b23565b61110c565b3480156105b457600080fd5b506105c86105c3366004613951565b6112ef565b6040516102da9796959493929190613bd7565b3480156105e757600080fd5b50610394611365565b3480156105fc57600080fd5b5061039461060b366004613951565b6113ad565b34801561061c57600080fd5b50600a54610410906001600160a01b031681565b34801561063c57600080fd5b5061039461064b366004613c33565b6114e3565b34801561065c57600080fd5b5061067061066b366004613951565b611529565b6040805196875265ffffffffffff90951660208701526001600160a01b0393841694860194909452908216606085015216608083015260a082015260c0016102da565b3480156106bf57600080fd5b506103946106ce366004613951565b611589565b3480156106df57600080fd5b506107076106ee366004613951565b6002602052600090815260409020805460019091015482565b604080519283526020830191909152016102da565b34801561072857600080fd5b506103ac60145481565b34801561073e57600080fd5b506104107f000000000000000000000000a0ad79d995ddeeb18a14eaef56a549a04e3aa1bd81565b34801561077257600080fd5b506103ac7fde5ee446972f4e39ab62c03aa34b2096680a875c3fdb3eb2f947cbb93341c05881565b3480156107a657600080fd5b506103946107b536600461398f565b6115ac565b3480156107c657600080fd5b506107076107d5366004613951565b6001602081905260009182526040909120805491015482565b3480156107fa57600080fd5b50610303610809366004613951565b60096020526000908152604090205460ff1681565b34801561082a57600080fd5b50610303610839366004613951565b60166020526000908152604090205460ff1681565b34801561085a57600080fd5b506103036108693660046139ac565b6115da565b34801561087a57600080fd5b5061088e610889366004613951565b611603565b6040516102da9190613c7d565b3480156108a757600080fd5b506103ac60125481565b3480156108bd57600080fd5b506103ac600081565b3480156108d257600080fd5b50601054610410906001600160a01b031681565b3480156108f257600080fd5b506103ac610901366004613d2e565b6116d6565b610394610914366004613951565b61177a565b34801561092557600080fd5b506103946119b3565b34801561093a57600080fd5b50601354610410906001600160a01b031681565b34801561095a57600080fd5b506103946109693660046139ac565b611a4c565b34801561097a57600080fd5b506104107f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4881565b3480156109ae57600080fd5b506109f36109bd366004613951565b6003602052600090815260409020805460019091015465ffffffffffff80831692600160301b900416906001600160a81b031683565b6040805165ffffffffffff94851681529390921660208401526001600160a81b0316908201526060016102da565b348015610a2d57600080fd5b50610410610a3c366004613905565b611a71565b348015610a4d57600080fd5b506103ac60008051602061401483398151915281565b348015610a6f57600080fd5b50610a98610a7e366004613951565b60076020526000908152604090205465ffffffffffff1681565b60405165ffffffffffff90911681526020016102da565b60056020528160005260406000208181548110610acb57600080fd5b60009182526020909120015465ffffffffffff81169250600160301b90046001600160a01b0316905082565b60006001600160e01b03198216637965db0b60e01b1480610b2857506301ffc9a760e01b6001600160e01b03198316145b92915050565b600080516020614014833981519152610b4681611b4b565b50600d55565b600080516020614014833981519152610b6481611b4b565b6000610b6f83611b58565b90506000610b7b611dca565b9050610bbb84836060015165ffffffffffff1683604080518082018252938452602080850193845260009283526002905290209151825551600190910155565b50505050565b6000610bcd8383611ee8565b9050610bda818484612460565b505050565b600080516020614014833981519152610bf781611b4b565b50600c80546001600160a01b0319166001600160a01b0392909216919091179055565b600082815260208190526040902060010154610c3581611b4b565b610bbb83836125af565b600080516020614014833981519152610c5781611b4b565b6000600f8381548110610c6c57610c6c613e02565b60009182526020909120600390910201905060068154610100900460ff166007811115610c9b57610c9b613b9f565b1480610cc1575060038154610100900460ff166007811115610cbf57610cbf613b9f565b145b610d015760405162461bcd60e51b815260206004820152600c60248201526b57726f6e672073746174757360a01b60448201526064015b60405180910390fd5b805461ff00191661030017808255600a546040516000926001600160a01b0390921691600160701b90046001600160801b0316908381818185875af1925050503d8060008114610d6d576040519150601f19603f3d011682016040523d82523d6000602084013e610d72565b606091505b5050905080610db95760405162461bcd60e51b81526020600482015260136024820152724661696c2073656e642045746820746f204d5760681b6044820152606401610cf8565b8154604051600160701b9091046001600160801b0316815284907fcdef6558dae40f2699846eedf449462daab85b1224ad7f077569ba91aaa949259060200160405180910390a2508054600160701b600160f01b03191690555050565b60158181548110610e2657600080fd5b600091825260209091200154905081565b6001600160a01b0381163314610e605760405163334bd91960e11b815260040160405180910390fd5b610bda8282612641565b606060056000838152602001908152602001600020805480602002602001604051908101604052809291908181526020016000905b82821015610eed576000848152602090819020604080518082019091529084015465ffffffffffff81168252600160301b90046001600160a01b031681830152825260019092019101610e9f565b505050509050919050565b600080516020614014833981519152610f1081611b4b565b6000600f8481548110610f2557610f25613e02565b60009182526020909120600390910201905060018154610100900460ff166007811115610f5457610f54613b9f565b14610fa15760405162461bcd60e51b815260206004820152601960248201527f526166666c65206973206e6f7420696e206163636570746564000000000000006044820152606401610cf8565b82516000805b8281101561107d576000868281518110610fc357610fc3613e02565b6020026020010151905060006040518060400160405280848860000160089054906101000a900465ffffffffffff1665ffffffffffff166110049190613e2e565b61100f906001613e2e565b65ffffffffffff90811682526001600160a01b0394851660209283015260008c81526005835260408120805460018181018355918352918490208551920180549590940151909616600160301b026001600160d01b0319909416911617919091179055509182019101610fa7565b50825461109a908290600160401b900465ffffffffffff16613e41565b835465ffffffffffff60401b1916600160401b65ffffffffffff92831681029190911780865560405189937f4da4f5fab0816c65315b6f5d15f879f96b98661133d7b3787788f291367604fb936110fc938b9389939290910490911690613e67565b60405180910390a2505050505050565b336001600160a01b037f000000000000000000000000a0ad79d995ddeeb18a14eaef56a549a04e3aa1bd161461117d5760405162461bcd60e51b8152602060048201526016602482015275043616c6c6572206e6f74204169726e6f6465205252560541b6044820152606401610cf8565b60008381526016602052604090205460ff166111d25760405162461bcd60e51b81526020600482015260146024820152732932b8bab2b9ba1024a2103737ba1035b737bbb760611b6044820152606401610cf8565b6000838152601660205260408120805460ff191690556111f482840184613951565b601481905560405181815290915084907f1ca47bacd454c26163f84eff4aa514e291ba9fa67ad6029e39567c122bbed30f9060200160405180910390a260008481526002602090815260408083208151808301909252805482526001015491810182905291906112649084613ee2565b61126f906001613e2e565b604080518082018252858152602080820184815286516000908152600180845290859020845181559151910155855183518881529182018590529394509092917f7c40e661b8212d0c4f60ac6e6ebed99c28680c7b3ede5b82f3b0254543f62fca910160405180910390a282516112e690836126ac565b50505050505050565b600f81815481106112ff57600080fd5b600091825260209091206003909102018054600182015460029092015460ff80831694506101008304169262010000830465ffffffffffff90811693600160401b810490911692600160701b9091046001600160801b0316916001600160a01b03169087565b600061137081611b4b565b600a546040516001600160a01b03909116904780156108fc02916000818181858888f193505050501580156113a9573d6000803e3d6000fd5b5050565b6000805160206140148339815191526113c581611b4b565b6004600f83815481106113da576113da613e02565b6000918252602090912060039091020154610100900460ff16600781111561140457611404613b9f565b146114605760405162461bcd60e51b815260206004820152602660248201527f526166666c65206973206e6f7420696e20636c6f73652072657175657374656460448201526520737461746560d01b6064820152608401610cf8565b6001600f838154811061147557611475613e02565b60009182526020909120600390910201805461ff0019166101008360078111156114a1576114a1613b9f565b0217905550817fc1191e7178b58ad510709587719f39ec315fa79e81ee7ba5c5ef3c894e94a65160016040516114d79190613ef6565b60405180910390a25050565b60006114ee81611b4b565b50601080546001600160a01b039586166001600160a01b03199182161790915560119390935560129190915560138054919093169116179055565b600e818154811061153957600080fd5b60009182526020909120600590910201805460018201546002830154600384015460049094015492945065ffffffffffff8216936001600160a01b03600160301b90930483169391831692169086565b6000805160206140148339815191526115a181611b4b565b6113a98260026126d1565b60006115b781611b4b565b50600a80546001600160a01b0319166001600160a01b0392909216919091179055565b6000918252602082815260408084206001600160a01b0393909316845291905290205460ff1690565b61160b6138c7565b600f828154811061161e5761161e613e02565b60009182526020918290206040805160e08101909152600390920201805460ff80821615158452929391929184019161010090910416600781111561166557611665613b9f565b600781111561167657611676613b9f565b8152815465ffffffffffff62010000820481166020840152600160401b82041660408301526001600160801b03600160701b90910416606082015260018201546001600160a01b0316608082015260029091015460a09091015292915050565b60006000805160206140148339815191526116f081611b4b565b6113888465ffffffffffff16111561174157604051636b221d4560e11b81526020600482015260136024820152720c6dedadad2e6e6d2dedc40e8dede40d0d2ced606b1b6044820152606401610cf8565b6117518a8a8a8a8a8a8a8a6128b4565b611759612d63565b600e5460009061176b90600190613f04565b9b9a5050505050505050505050565b6000600e828154811061178f5761178f613e02565b906000526020600020906005020190506000600f83815481106117b4576117b4613e02565b60009182526020822060039091020191508154610100900460ff1660078111156117e0576117e0613b9f565b146118225760405162461bcd60e51b8152602060048201526012602482015271149859999b19481b9bdd0810d4915055115160721b6044820152606401610cf8565b60018201548254604051636eb1769f60e11b8152336004820152306024820152600160301b9092046001600160a01b031691829063dd62ed3e90604401602060405180830381865afa15801561187c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118a09190613f17565b10156118e05760405162461bcd60e51b815260206004820152600f60248201526e20b63637bbb0b731b29022b93937b960891b6044820152606401610cf8565b815461010061ff00199091161782556003830180546001600160a01b0319163390811790915583546040516323b872dd60e01b8152600481019290925230602483015260448201526001600160a01b038216906323b872dd906064016020604051808303816000875af115801561195b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061197f9190613f30565b50604051339085907f8bb509eedfd1c4847b0a8a2b4493cf2ebb9970dc367e477cd2a8523e212dc1db90600090a350505050565b60006119be81611b4b565b601054601354604051631d414cbd60e01b81526001600160a01b03928316600482015290821660248201527f000000000000000000000000a0ad79d995ddeeb18a14eaef56a549a04e3aa1bd90911690631d414cbd90604401600060405180830381600087803b158015611a3157600080fd5b505af1158015611a45573d6000803e3d6000fd5b5050505050565b600082815260208190526040902060010154611a6781611b4b565b610bbb8383612641565b60008281526005602052604081208190611a8b9084612ec4565b60008581526005602052604081208054929350909183908110611ab057611ab0613e02565b9060005260206000200160000160069054906101000a90046001600160a01b03169050600e8581548110611ae657611ae6613e02565b6000918252602090912060059091020160010154600160301b90046001600160a01b031673a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4814611b3857611b2f818387612f92565b92505050610b28565b611b2f8183876130d6565b505092915050565b611b55813361330c565b50565b611b606138c7565b6000600f8381548110611b7557611b75613e02565b60009182526020808320868452600490915260409092206003909102909101915060018254610100900460ff166007811115611bb357611bb3613b9f565b14611bf95760405162461bcd60e51b8152602060048201526016602482015275526166666c6520696e2077726f6e672073746174757360501b6044820152606401610cf8565b805482546001600160801b03918216600160701b9091049091161015611c615760405162461bcd60e51b815260206004820152601760248201527f4e6f7420656e6f7567682066756e6473207261697365640000000000000000006044820152606401610cf8565b81548154600160701b9091046001600160801b03908116600160801b909204161115611ccf5760405162461bcd60e51b815260206004820152601860248201527f446573697265642066756e6473206e6f742072616973656400000000000000006044820152606401610cf8565b815461ff0019166104001780835560408051600160701b9092046001600160801b031682525185917ff2be214756d2fbc1e781d10809ddef33000009d805be55356bb348134ce21c68919081900360200190a26040805160e08101909152825460ff8082161515835284916020840191610100909104166007811115611d5757611d57613b9f565b6007811115611d6857611d68613b9f565b8152815465ffffffffffff62010000820481166020840152600160401b82041660408301526001600160801b03600160701b90910416606082015260018201546001600160a01b0316608082015260029091015460a090910152949350505050565b601054601154601354604051636e6be03f60e01b81526001600160a01b0393841660048201526024810192909252306044830181905290831660648301526084820152631b8c6c8560e11b60a482015260e060c4820152600060e482018190529182917f000000000000000000000000a0ad79d995ddeeb18a14eaef56a549a04e3aa1bd90911690636e6be03f90610104016020604051808303816000875af1158015611e7b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e9f9190613f17565b600081815260166020526040808220805460ff191660011790555191925082917fcba2da2f3c0c732a104019a3104936397dde7343964c1518ceb760052e4537b19190a2919050565b611ef06138c7565b6000600f8481548110611f0557611f05613e02565b60009182526020909120600390910201905060018154610100900460ff166007811115611f3457611f34613b9f565b14611f745760405163efeb42cf60e01b815260206004820152600f60248201526e139bdd081a5b881050d0d154151151608a1b6044820152606401610cf8565b6000838152600360209081526040918290208251606081018452815465ffffffffffff808216808452600160301b90920416938201939093526001909101546001600160a81b03169281019290925285146120075760405163efeb42cf60e01b81526020600482015260126024820152711259081b9bdd081a5b881c9859999b19525960721b6044820152606401610cf8565b602081015160408201516001600160a81b031634146120695760405163efeb42cf60e01b815260206004820152601760248201527f6d73672e76616c7565206e6f74207468652070726963650000000000000000006044820152606401610cf8565b81604001516001600160a81b031660000361217b573233146120ce5760405163efeb42cf60e01b815260206004820152601760248201527f74782e6f726967696e20213d206d73672e73656e6465720000000000000000006044820152606401610cf8565b6040805133602082015290810187905260009060600160408051601f1981840301815291815281516020928301206000818152600990935291205490915060ff1615156001036121615760405163efeb42cf60e01b815260206004820152601d60248201527f506c6179657220616c726561647920676f74206672656520656e7472790000006044820152606401610cf8565b6000908152600960205260409020805460ff191660011790555b60028301541561223e57600183015460028401546040516370a0823160e01b81523360048201526001600160a01b039092169182906370a0823190602401602060405180830381865afa1580156121d6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121fa9190613f17565b101561223c5760405162461bcd60e51b81526020600482015260116024820152704e6f7420656e6f75676820746f6b656e7360781b6044820152606401610cf8565b505b825460408051808201909152600160401b90910465ffffffffffff1690600090806122698585613e41565b65ffffffffffff90811682523360209283015260008b815260058352604081208054600181018255818352848320865191018054958701516001600160a01b0316600160301b026001600160d01b03199096169190941617939093179091558a8152815492935090916122de576122de613e02565b600091825260209091200180546001600160d01b0319169055845434908690600e9061231b908490600160701b90046001600160801b0316613f52565b92506101000a8154816001600160801b0302191690836001600160801b03160217905550828261234b9190613e41565b855465ffffffffffff60401b1916600160401b65ffffffffffff928316810291909117808855604080519290910490921681526020810189905233918a917fd746af8dc82f9bed98cea0fe0264eb1c3d2e5f7bcc77fc5efb429c79df407887910160405180910390a36040805160e08101909152855460ff80821615158352879160208401916101009091041660078111156123e9576123e9613b9f565b60078111156123fa576123fa613b9f565b8152815462010000810465ffffffffffff9081166020840152600160401b8204166040830152600160701b90046001600160801b0316606082015260018201546001600160a01b0316608082015260029091015460a09091015298975050505050505050565b604083015165ffffffffffff1615610bda576000818152600360209081526040808320815160608082018452825465ffffffffffff8082168452600160301b90910481168387019081526001909401546001600160a81b03168386015284518087018a90523381870152855180820387018152920185528151918601919091208087526007909552948390205492880151915190949283169291909116906125089083613e41565b65ffffffffffff1611156125555760405163efeb42cf60e01b815260206004820152601360248201527215d85b1b195d08185b1c9958591e481d5cd959606a1b6044820152606401610cf8565b6020808401516000848152600790925260409091205461257d919065ffffffffffff16613e41565b600092835260076020526040909220805465ffffffffffff191665ffffffffffff909316929092179091555050505050565b60006125bb83836115da565b612639576000838152602081815260408083206001600160a01b03861684529091529020805460ff191660011790556125f13390565b6001600160a01b0316826001600160a01b0316847f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a4506001610b28565b506000610b28565b600061264d83836115da565b15612639576000838152602081815260408083206001600160a01b0386168085529252808320805460ff1916905551339286917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a4506001610b28565b60006126b88383613345565b9050610bda81604001518260000151836060015161372b565b6000600e83815481106126e6576126e6613e02565b906000526020600020906005020190506000600f848154811061270b5761270b613e02565b60009182526020909120600390910201905060018154610100900460ff16600781111561273a5761273a613b9f565b1480612760575060008154610100900460ff16600781111561275e5761275e613b9f565b145b61279b5760405162461bcd60e51b815260206004820152600c60248201526b57726f6e672073746174757360a01b6044820152606401610cf8565b60018154610100900460ff1660078111156127b8576127b8613b9f565b0361285b578265ffffffffffff166000036127f7576001820154825460038401546127f7926001600160a01b03600160301b90910481169291166137a1565b8265ffffffffffff16600103612821578154600383015461282191906001600160a01b0316613812565b8265ffffffffffff1660020361285b5760018201548254600384015461285b926001600160a01b03600160301b909104811692911661372b565b805461ff0019166106001780825560408051600160701b9092046001600160801b031682525185917fd512a34b0f0618078770fcd85d974df1ab46a7882e8b3d45aa91764f4961aed2919081900360200190a250505050565b6040805160c08101825287815265ffffffffffff808516602083019081526001600160a01b03808c169484019485526000606085018181526080860182815260a08701838152600e8054600181810183558287528a5160059092027fbb7b4a454dc3493923482f07822329ed19e8244eff582cc204f8554c3620c3fd81019290925597517fbb7b4a454dc3493923482f07822329ed19e8244eff582cc204f8554c3620c3fe820180549c518916600160301b026001600160d01b0319909d1691909a16179a909a1790975591517fbb7b4a454dc3493923482f07822329ed19e8244eff582cc204f8554c3620c3ff890180549186166001600160a01b031992831617905590517fbb7b4a454dc3493923482f07822329ed19e8244eff582cc204f8554c3620c400890180549190951691161790925590517fbb7b4a454dc3493923482f07822329ed19e8244eff582cc204f8554c3620c401909501949094559054919291612a229190613f04565b90506000859003612a6257604051636b221d4560e11b81526020600482015260096024820152684e6f2070726963657360b81b6044820152606401610cf8565b60005b85811015612c1557868682818110612a7f57612a7f613e02565b9050606002016020016020810190612a979190613f72565b65ffffffffffff16600003612ae157604051636b221d4560e11b815260206004820152600f60248201526e06e756d456e7472696573206973203608c1b6044820152606401610cf8565b600060405180606001604052808465ffffffffffff168152602001898985818110612b0e57612b0e613e02565b9050606002016020016020810190612b269190613f72565b65ffffffffffff168152602001898985818110612b4557612b45613e02565b9050606002016040016020810190612b5d9190613f8d565b6001600160a81b03169052905080600360008a8a86818110612b8157612b81613e02565b612b979260206060909202019081019150613f72565b65ffffffffffff9081168252602080830193909352604091820160002084518154948601518316600160301b026bffffffffffffffffffffffff199095169216919091179290921782559190910151600191820180546001600160a81b039092166001600160a81b0319909216919091179055919091019050612a65565b50604080518082019091526001600160801b0380891682528b166020820152600e54600490600090612c4990600190613f04565b8152602080820192909252604001600020825192909101516001600160801b03908116600160801b029216919091179055600e5488906001600160a01b038b1690612c9690600190613f04565b6040517f81781e053ec72aa8731479536c4da8f819ef3283d2c0dea5c4f0d938bed8489590600090a460408051808201825260018082523360208084019182526000868152600582529485208054938401815580865290852084519301805492516001600160a01b0316600160301b026001600160d01b031990931665ffffffffffff949094169390931791909117909155838352805491929091612d3d57612d3d613e02565b600091825260209091200180546001600160d01b03191690555050505050505050505050565b6040805160e0810182526000808252602082018181529282018190526060820181905260808201819052600c546001600160a01b031660a0830152600d5460c0830152600f80546001810182559152815160039091027f8d1108e10bcb7c27dddfc02ed9d693a074039d026cf4ea4240b40f7d581ac80201805491151560ff198316811782559351929384939192839161ff00191661ffff1990911617610100836007811115612e1557612e15613b9f565b021790555060408201518154606084015160808501516dffffffffffffffffffffffff0000199092166201000065ffffffffffff9485160265ffffffffffff60401b191617600160401b939091169290920291909117600160701b600160f01b031916600160701b6001600160801b039092169190910217815560a08201516001820180546001600160a01b0319166001600160a01b0390921691909117905560c09091015160029091015550565b81546000908103612ed757506000610b28565b82546000905b80821015612f3c576000612ef183836138ac565b905084868281548110612f0657612f06613e02565b60009182526020909120015465ffffffffffff161115612f2857809150612f36565b612f33816001613e2e565b92505b50612edd565b600082118015612f7a57508385612f54600185613f04565b81548110612f6457612f64613e02565b60009182526020909120015465ffffffffffff16145b15612f8a57611b2f600183613f04565b509050610b28565b60006001600160a01b03841615612faa5750826130cf565b6000835b81158015612ff557506000848152600560205260408120805483908110612fd757612fd7613e02565b600091825260209091200154600160301b90046001600160a01b0316145b1561304357806000036130245760008481526005602052604090205461301d90600190613f04565b9050613032565b61302f600182613f04565b90505b84810361303e57600191505b612fae565b81156130895760405162461bcd60e51b8152602060048201526015602482015274105b1b081d5cd95c9cc8189b1858dadb1a5cdd1959605a1b6044820152606401610cf8565b60008481526005602052604090208054829081106130a9576130a9613e02565b600091825260209091200154600160301b90046001600160a01b031692506130cf915050565b9392505050565b60006001600160a01b03841615801590613177575060405163fe575a8760e01b81526001600160a01b0385811660048301527f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48169063fe575a8790602401602060405180830381865afa158015613151573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131759190613f30565b155b156131835750826130cf565b6000835b816130435760008481526005602052604081208054839081106131ac576131ac613e02565b600091825260209091200154600160301b90046001600160a01b03161480159061329e5750600084815260056020526040902080546001600160a01b037f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48169163fe575a87918490811061322257613222613e02565b60009182526020909120015460405160e083901b6001600160e01b0319168152600160301b9091046001600160a01b03166004820152602401602060405180830381865afa158015613278573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061329c9190613f30565b155b156132c35760008481526005602052604090208054829081106130a9576130a9613e02565b806000036132ed576000848152600560205260409020546132e690600190613f04565b90506132fb565b6132f8600182613f04565b90505b84810361330757600191505b613187565b61331682826115da565b6113a95760405163e2517d3f60e01b81526001600160a01b038216600482015260248101839052604401610cf8565b6040805160c081018252600080825260208201819052918101829052606081018290526080810182905260a08101919091526000600e848154811061338c5761338c613e02565b906000526020600020906005020190506000600f85815481106133b1576133b1613e02565b60009182526020909120600390910201905060028154610100900460ff1660078111156133e0576133e0613b9f565b1480613406575060048154610100900460ff16600781111561340457613404613b9f565b145b61344b5760405162461bcd60e51b8152602060048201526016602482015275526166666c6520696e2077726f6e672073746174757360501b6044820152606401610cf8565b6004820184905561345c8585611a71565b6002830180546001600160a01b0319166001600160a01b0392909216919091179055805461ff001916610500178082556001830154600091612710916134bc9165ffffffffffff90911690600160701b90046001600160801b0316613fb6565b6134c69190613fd9565b82546001600160801b0391821692506000916134ec918491600160701b90910416613f04565b60038501546040519192506000916001600160a01b039091169083908381818185875af1925050503d8060008114613540576040519150601f19603f3d011682016040523d82523d6000602084013e613545565b606091505b505090508061358d5760405162461bcd60e51b81526020600482015260146024820152732330b4b632b2103a379039b2b7321022ba3432b960611b6044820152606401610cf8565b600a546040516000916001600160a01b03169085908381818185875af1925050503d80600081146135da576040519150601f19603f3d011682016040523d82523d6000602084013e6135df565b606091505b50509050806136285760405162461bcd60e51b81526020600482015260156024820152744661696c65642073656e642045746820746f204d5760581b6044820152606401610cf8565b887f7378e11c2b0ec7514bbf7ba369980eedcba0bca03e116dc9e7138f7748e211d68560405161365a91815260200190565b60405180910390a26002860154855460408051600160701b9092046001600160801b03168252602082018b90526001600160a01b03909216918b917fe0b2a72a0644b093aac275024c05c7c28851a0b572557a32241d13634a0f3e08910160405180910390a350506040805160c08101825285548152600186015465ffffffffffff81166020830152600160301b90046001600160a01b03908116928201929092526002860154821660608201526003860154909116608082015260049094015460a0850152509195945050505050565b60405163a9059cbb60e01b81526001600160a01b0382811660048301526024820184905284919082169063a9059cbb906044016020604051808303816000875af115801561377d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a459190613f30565b6040516323b872dd60e01b81523060048201526001600160a01b038281166024830152604482018490528491908216906323b872dd90606401600060405180830381600087803b1580156137f457600080fd5b505af1158015613808573d6000803e3d6000fd5b5050505050505050565b6000816001600160a01b03168360405160006040518083038185875af1925050503d806000811461385f576040519150601f19603f3d011682016040523d82523d6000602084013e613864565b606091505b5050905080610bda5760405162461bcd60e51b81526020600482015260146024820152732330b4b632b2103a379039b2b7321022ba3432b960611b6044820152606401610cf8565b60006138bb6002848418613fff565b6130cf90848416613e2e565b6040805160e0810190915260008082526020820190815260006020820181905260408201819052606082018190526080820181905260a09091015290565b6000806040838503121561391857600080fd5b50508035926020909101359150565b60006020828403121561393957600080fd5b81356001600160e01b0319811681146130cf57600080fd5b60006020828403121561396357600080fd5b5035919050565b6001600160a01b0381168114611b5557600080fd5b803561398a8161396a565b919050565b6000602082840312156139a157600080fd5b81356130cf8161396a565b600080604083850312156139bf57600080fd5b8235915060208301356139d18161396a565b809150509250929050565b602080825282518282018190526000919060409081850190868401855b82811015613a2f578151805165ffffffffffff1685528601516001600160a01b03168685015292840192908501906001016139f9565b5091979650505050505050565b634e487b7160e01b600052604160045260246000fd5b60008060408385031215613a6557600080fd5b8235915060208084013567ffffffffffffffff80821115613a8557600080fd5b818601915086601f830112613a9957600080fd5b813581811115613aab57613aab613a3c565b8060051b604051601f19603f83011681018181108582111715613ad057613ad0613a3c565b604052918252848201925083810185019189831115613aee57600080fd5b938501935b82851015613b1357613b048561397f565b84529385019392850192613af3565b8096505050505050509250929050565b600080600060408486031215613b3857600080fd5b83359250602084013567ffffffffffffffff80821115613b5757600080fd5b818601915086601f830112613b6b57600080fd5b813581811115613b7a57600080fd5b876020828501011115613b8c57600080fd5b6020830194508093505050509250925092565b634e487b7160e01b600052602160045260246000fd5b60088110613bd357634e487b7160e01b600052602160045260246000fd5b9052565b871515815260e08101613bed6020830189613bb5565b65ffffffffffff96871660408301529490951660608601526001600160801b039290921660808501526001600160a01b031660a084015260c09092019190915292915050565b60008060008060808587031215613c4957600080fd5b8435613c548161396a565b935060208501359250604085013591506060850135613c728161396a565b939692955090935050565b81511515815260208083015160e0830191613c9a90840182613bb5565b50604083015165ffffffffffff808216604085015280606086015116606085015250506001600160801b03608084015116608083015260018060a01b0360a08401511660a083015260c083015160c083015292915050565b80356001600160801b038116811461398a57600080fd5b803565ffffffffffff8116811461398a57600080fd5b80356003811061398a57600080fd5b60008060008060008060008060e0898b031215613d4a57600080fd5b613d5389613cf2565b97506020890135613d638161396a565b965060408901359550613d7860608a01613cf2565b9450608089013567ffffffffffffffff80821115613d9557600080fd5b818b0191508b601f830112613da957600080fd5b813581811115613db857600080fd5b8c6020606083028501011115613dcd57600080fd5b602083019650809550505050613de560a08a01613d09565b9150613df360c08a01613d1f565b90509295985092959890939650565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b80820180821115610b2857610b28613e18565b65ffffffffffff818116838216019080821115613e6057613e60613e18565b5092915050565b606080825284519082018190526000906020906080840190828801845b82811015613ea95781516001600160a01b031684529284019290840190600101613e84565b5050506020840195909552505065ffffffffffff91909116604090910152919050565b634e487b7160e01b600052601260045260246000fd5b600082613ef157613ef1613ecc565b500690565b60208101610b288284613bb5565b81810381811115610b2857610b28613e18565b600060208284031215613f2957600080fd5b5051919050565b600060208284031215613f4257600080fd5b815180151581146130cf57600080fd5b6001600160801b03818116838216019080821115613e6057613e60613e18565b600060208284031215613f8457600080fd5b6130cf82613d09565b600060208284031215613f9f57600080fd5b81356001600160a81b03811681146130cf57600080fd5b6001600160801b03818116838216028082169190828114611b4357611b43613e18565b60006001600160801b0380841680613ff357613ff3613ecc565b92169190910492915050565b60008261400e5761400e613ecc565b50049056fe523a704056dcd17bcf83bed8b68c59416dac1119be77755efe3bde0a64e46e0ca2646970667358221220a2553f65afcd566c299120d42df15f78ad86283c8b8161371a9e6cbef3b03c2064736f6c63430008180033

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

000000000000000000000000a0ad79d995ddeeb18a14eaef56a549a04e3aa1bd000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48

-----Decoded View---------------
Arg [0] : _airnodeRrp (address): 0xa0AD79D995DdeeB18a14eAef56A549A04e3Aa1Bd
Arg [1] : _usdcAddress (address): 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48

-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 000000000000000000000000a0ad79d995ddeeb18a14eaef56a549a04e3aa1bd
Arg [1] : 000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

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

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