ETH Price: $1,976.20 (+0.12%)
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Submit Score To ...243658152026-02-02 1:12:5920 days ago1769994779IN
0x640EE707...21907972e
0 ETH0.001315121.12022555
Submit Score To ...243658082026-02-02 1:11:3520 days ago1769994695IN
0x640EE707...21907972e
0 ETH0.000193810.57884313
Submit Score To ...243657972026-02-02 1:09:2320 days ago1769994563IN
0x640EE707...21907972e
0 ETH0.000032580.21184406
Submit Score To ...243657912026-02-02 1:08:1120 days ago1769994491IN
0x640EE707...21907972e
0 ETH0.00002450.13155217
Emergency Set Nu...243657642026-02-02 1:02:3520 days ago1769994155IN
0x640EE707...21907972e
0 ETH0.000051480.18795432
Trigger VRF For ...243657092026-02-02 0:51:3520 days ago1769993495IN
0x640EE707...21907972e
0 ETH0.000038090.13926424
Set Pool Creatio...243657062026-02-02 0:50:5920 days ago1769993459IN
0x640EE707...21907972e
0 ETH0.000006210.20964718
Create Pool243656802026-02-02 0:45:4720 days ago1769993147IN
0x640EE707...21907972e
0.005 ETH0.000543170.14018633
Create Pool243656722026-02-02 0:44:1120 days ago1769993051IN
0x640EE707...21907972e
0.005 ETH0.000700640.17940379
Transfer Admin243656552026-02-02 0:40:4720 days ago1769992847IN
0x640EE707...21907972e
0 ETH0.000003740.13090598
Set Score Admin243656552026-02-02 0:40:4720 days ago1769992847IN
0x640EE707...21907972e
0 ETH0.000004020.13090598
Set Aave Address...243656482026-02-02 0:39:2320 days ago1769992763IN
0x640EE707...21907972e
0 ETH0.000018310.15817599
Set VRF Funding ...243656472026-02-02 0:39:1120 days ago1769992751IN
0x640EE707...21907972e
0 ETH0.000003860.14215442

Latest 4 internal transactions

Advanced mode:
Parent Transaction Hash Method Block
From
To
Fund Subscriptio...243656802026-02-02 0:45:4720 days ago1769993147
0x640EE707...21907972e
0.005 ETH
0x60e03462243656802026-02-02 0:45:4720 days ago1769993147
0x640EE707...21907972e
 Contract Creation0 ETH
Fund Subscriptio...243656722026-02-02 0:44:1120 days ago1769993051
0x640EE707...21907972e
0.005 ETH
0x60e03462243656722026-02-02 0:44:1120 days ago1769993051
0x640EE707...21907972e
 Contract Creation0 ETH
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading

Similar Match Source Code
This contract matches the deployed Bytecode of the Source Code for Contract 0xd573508f...2EeFb5eA3
The constructor portion of the code might be different and could alter the actual behaviour of the contract

Contract Name:
SquaresFactory

Compiler Version
v0.8.24+commit.e11b9ed9

Optimization Enabled:
Yes with 1 runs

Other Settings:
cancun EvmVersion

Contract Source Code (Solidity Standard Json-Input format)

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

import {SquaresPool} from "./SquaresPool.sol";
import {ISquaresPool} from "./interfaces/ISquaresPool.sol";
import {IVRFCoordinatorV2Plus} from "./interfaces/IVRFCoordinatorV2Plus.sol";
import {SquaresLib} from "./libraries/SquaresLib.sol";

/// @title SquaresFactory
/// @notice Factory for deploying Super Bowl Squares pools with Chainlink VRF
contract SquaresFactory {
    // ============ Events ============
    event PoolCreated(
        address indexed pool,
        address indexed creator,
        string name,
        uint256 squarePrice,
        address paymentToken
    );
    event VRFTriggeredForAllPools(uint256 poolsTriggered);
    event EmergencyNumbersSetForAllPools(uint256 poolsSet);
    event CreationFeeUpdated(uint256 oldFee, uint256 newFee);
    event FeesWithdrawn(address indexed to, uint256 amount);
    event AdminTransferred(address indexed oldAdmin, address indexed newAdmin);
    event ScoreAdminUpdated(address indexed oldAdmin, address indexed newAdmin);
    event VRFSubscriptionCreated(uint256 indexed subscriptionId);
    event VRFConsumerAdded(uint256 indexed subscriptionId, address indexed consumer);
    event VRFSubscriptionFunded(uint256 indexed subscriptionId, uint256 amount);
    event VRFSubscriptionCancelled(uint256 indexed subscriptionId, address indexed fundsRecipient);
    event ScoreSubmittedToAllPools(uint8 indexed quarter, uint8 teamAScore, uint8 teamBScore);
    event PoolCreationPaused(bool paused);
    event AaveAddressesUpdated(address pool, address gateway, address aWETH, address aUSDC);
    event YieldWithdrawnFromAllPools(uint256 poolsWithdrawn);

    // ============ State ============
    address[] public allPools;
    mapping(address => address[]) public poolsByCreator;
    mapping(bytes32 => bool) public poolNameExists;

    // External contract addresses (immutable per chain)
    address public immutable vrfCoordinator;

    // Default Chainlink VRF configuration
    uint256 public defaultVRFSubscriptionId;
    bytes32 public defaultVRFKeyHash;

    // Pool creation fee (covers VRF costs)
    uint256 public creationFee;

    // VRF funding amount (ETH to fund VRF per pool)
    uint96 public vrfFundingAmount;

    // Admin
    address public admin;

    // Score Admin - can submit scores to all pools
    address public scoreAdmin;

    // Pool creation pause state
    bool public poolCreationPaused;

    // Aave V3 configuration
    address public aavePool;
    address public wethGateway;
    address public aWETH;
    address public aUSDC;

    // ============ Errors ============
    error OnlyAdmin();
    error Unauthorized();
    error InsufficientCreationFee(uint256 sent, uint256 required);
    error TransferFailed();
    error InvalidAddress();
    error PoolCreationIsPaused();
    error PoolNameAlreadyExists(string name);

    // ============ Modifiers ============
    modifier onlyAdmin() {
        if (msg.sender != admin) revert OnlyAdmin();
        _;
    }

    // ============ Constructor ============
    constructor(
        address _vrfCoordinator,
        bytes32 _vrfKeyHash,
        uint256 _creationFee
    ) {
        if (_vrfCoordinator == address(0)) revert InvalidAddress();

        vrfCoordinator = _vrfCoordinator;
        defaultVRFKeyHash = _vrfKeyHash;
        creationFee = _creationFee;
        admin = msg.sender;
        scoreAdmin = msg.sender; // Initially deployer is score admin

        // Create VRF subscription - factory becomes the owner
        defaultVRFSubscriptionId = IVRFCoordinatorV2Plus(_vrfCoordinator).createSubscription();
        emit VRFSubscriptionCreated(defaultVRFSubscriptionId);
    }

    // ============ Admin Functions ============

    /// @notice Update VRF subscription
    function setVRFSubscription(uint256 subscriptionId) external onlyAdmin {
        defaultVRFSubscriptionId = subscriptionId;
    }

    /// @notice Update VRF key hash
    function setVRFKeyHash(bytes32 keyHash) external onlyAdmin {
        defaultVRFKeyHash = keyHash;
    }

    /// @notice Update creation fee
    function setCreationFee(uint256 _fee) external onlyAdmin {
        creationFee = _fee;
    }

    /// @notice Update VRF funding amount per pool
    function setVRFFundingAmount(uint96 _amount) external onlyAdmin {
        vrfFundingAmount = _amount;
    }

    /// @notice Fund the VRF subscription with additional ETH
    /// @dev Use this if the subscription balance is running low
    function fundVRFSubscription() external payable onlyAdmin {
        if (msg.value == 0) revert InsufficientCreationFee(0, 1);
        IVRFCoordinatorV2Plus(vrfCoordinator).fundSubscriptionWithNative{value: msg.value}(
            defaultVRFSubscriptionId
        );
        emit VRFSubscriptionFunded(defaultVRFSubscriptionId, msg.value);
    }

    /// @notice Cancel the VRF subscription and withdraw remaining funds
    function cancelAndWithdrawVRFSubscription(address to) external onlyAdmin {
        if (to == address(0)) revert InvalidAddress();
        IVRFCoordinatorV2Plus(vrfCoordinator).cancelSubscription(defaultVRFSubscriptionId, to);
        emit VRFSubscriptionCancelled(defaultVRFSubscriptionId, to);
        defaultVRFSubscriptionId = 0;
    }

    /// @notice Withdraw accumulated fees
    function withdrawFees(address to) external onlyAdmin {
        if (to == address(0)) revert InvalidAddress();
        uint256 balance = address(this).balance;
        (bool success,) = to.call{value: balance}("");
        if (!success) revert TransferFailed();
        emit FeesWithdrawn(to, balance);
    }

    /// @notice Transfer admin role
    function transferAdmin(address newAdmin) external onlyAdmin {
        if (newAdmin == address(0)) revert InvalidAddress();
        emit AdminTransferred(admin, newAdmin);
        admin = newAdmin;
    }

    /// @notice Set the score admin who can submit scores to all pools
    function setScoreAdmin(address _scoreAdmin) external onlyAdmin {
        if (_scoreAdmin == address(0)) revert InvalidAddress();
        emit ScoreAdminUpdated(scoreAdmin, _scoreAdmin);
        scoreAdmin = _scoreAdmin;
    }

    /// @notice Pause or unpause pool creation
    function setPoolCreationPaused(bool _paused) external onlyAdmin {
        poolCreationPaused = _paused;
        emit PoolCreationPaused(_paused);
    }

    /// @notice Set Aave V3 addresses for yield generation
    /// @param _pool Aave Pool contract address
    /// @param _gateway WETH Gateway contract address
    /// @param _aWETH aWETH token address
    /// @param _aUSDC aUSDC token address
    function setAaveAddresses(
        address _pool,
        address _gateway,
        address _aWETH,
        address _aUSDC
    ) external onlyAdmin {
        aavePool = _pool;
        wethGateway = _gateway;
        aWETH = _aWETH;
        aUSDC = _aUSDC;
        emit AaveAddressesUpdated(_pool, _gateway, _aWETH, _aUSDC);
    }

    /// @notice Withdraw yield from all finished pools
    function withdrawYieldFromAllPools() external onlyAdmin {
        uint256 withdrawn;
        uint256 len = allPools.length;
        unchecked {
            for (uint256 i; i < len; ++i) {
                try SquaresPool(payable(allPools[i])).withdrawYieldFromFactory() { ++withdrawn; } catch {}
            }
        }
        emit YieldWithdrawnFromAllPools(withdrawn);
    }

    /// @notice Update Aave configuration for an existing pool
    /// @dev Useful for fixing misconfigured pools or updating to new Aave addresses
    /// @param pool Address of the pool to update
    /// @param _aavePool Aave Pool contract address
    /// @param _wethGateway WETH Gateway contract address
    /// @param _aToken aToken address (aWETH for ETH pools, aUSDC for USDC pools)
    function updatePoolAaveConfig(
        address pool,
        address _aavePool,
        address _wethGateway,
        address _aToken
    ) external onlyAdmin {
        SquaresPool(payable(pool)).setAaveConfig(_aavePool, _wethGateway, _aToken);
    }

    // ============ Score Admin Functions ============

    /// @notice Submit score to all pools that are ready for this quarter
    /// @param quarter The quarter to submit (0=Q1, 1=Q2, 2=Q3, 3=Final)
    /// @param teamAScore Team A's score
    /// @param teamBScore Team B's score
    function submitScoreToAllPools(uint8 quarter, uint8 teamAScore, uint8 teamBScore) external {
        if (msg.sender != scoreAdmin && msg.sender != admin) revert Unauthorized();
        uint256 len = allPools.length;
        unchecked {
            for (uint256 i; i < len; ++i) {
                try SquaresPool(payable(allPools[i])).submitScoreFromFactory(quarter, teamAScore, teamBScore) {} catch {}
            }
        }
        emit ScoreSubmittedToAllPools(quarter, teamAScore, teamBScore);
    }

    /// @notice Trigger VRF for all pools that are ready
    function triggerVRFForAllPools() external {
        if (msg.sender != scoreAdmin && msg.sender != admin) revert Unauthorized();
        uint256 triggered;
        uint256 len = allPools.length;
        unchecked {
            for (uint256 i; i < len; ++i) {
                try SquaresPool(payable(allPools[i])).closePoolAndRequestVRFFromFactory() { ++triggered; } catch {}
            }
        }
        emit VRFTriggeredForAllPools(triggered);
    }

    /// @notice Emergency: Set numbers for all pools stuck waiting for VRF
    function emergencySetNumbersForAllPools() external onlyAdmin {
        uint256 set;
        uint256 len = allPools.length;
        unchecked {
            for (uint256 i; i < len; ++i) {
                SquaresPool pool = SquaresPool(payable(allPools[i]));
                try pool.state() returns (ISquaresPool.PoolState state) {
                    if (state == ISquaresPool.PoolState.CLOSED && pool.vrfRequested()) {
                        uint256 seed = uint256(keccak256(abi.encodePacked(block.timestamp, block.prevrandao, allPools[i], i)));
                        try pool.emergencySetNumbers(
                            SquaresLib.fisherYatesShuffle(seed),
                            SquaresLib.fisherYatesShuffle(uint256(keccak256(abi.encodePacked(seed, uint256(1)))))
                        ) { ++set; } catch {}
                    }
                } catch {}
            }
        }
        emit EmergencyNumbersSetForAllPools(set);
    }

    // ============ Factory Functions ============

    /// @notice Create a new Super Bowl Squares pool
    /// @param params Pool configuration parameters
    /// @return pool Address of the newly created pool contract
    function createPool(ISquaresPool.PoolParams calldata params) external payable returns (address pool) {
        if (poolCreationPaused) revert PoolCreationIsPaused();

        // Check for duplicate pool name
        bytes32 nameHash = keccak256(bytes(params.name));
        if (poolNameExists[nameHash]) revert PoolNameAlreadyExists(params.name);
        poolNameExists[nameHash] = true;

        // Calculate total required (creation fee + VRF funding)
        uint256 totalRequired = creationFee + vrfFundingAmount;
        if (msg.value < totalRequired) {
            revert InsufficientCreationFee(msg.value, totalRequired);
        }

        // Deploy new pool contract
        SquaresPool newPool = new SquaresPool(
            vrfCoordinator,
            msg.sender // operator
        );

        // Initialize with parameters
        newPool.initialize(params);

        // Set VRF config
        newPool.setVRFConfig(
            defaultVRFSubscriptionId,
            defaultVRFKeyHash
        );

        // Set Aave config if configured
        if (aavePool != address(0)) {
            // Determine correct aToken based on payment token
            address poolAToken = params.paymentToken == address(0) ? aWETH : aUSDC;
            newPool.setAaveConfig(aavePool, wethGateway, poolAToken);
        }

        pool = address(newPool);

        // Add pool as VRF consumer (factory is subscription owner, so this works)
        IVRFCoordinatorV2Plus(vrfCoordinator).addConsumer(defaultVRFSubscriptionId, pool);
        emit VRFConsumerAdded(defaultVRFSubscriptionId, pool);

        // Fund the VRF subscription with native ETH
        if (vrfFundingAmount > 0) {
            IVRFCoordinatorV2Plus(vrfCoordinator).fundSubscriptionWithNative{value: vrfFundingAmount}(
                defaultVRFSubscriptionId
            );
            emit VRFSubscriptionFunded(defaultVRFSubscriptionId, vrfFundingAmount);
        }

        // Track pool
        allPools.push(pool);
        poolsByCreator[msg.sender].push(pool);

        emit PoolCreated(pool, msg.sender, params.name, params.squarePrice, params.paymentToken);

        // Refund excess payment
        if (msg.value > totalRequired) {
            (bool success,) = msg.sender.call{value: msg.value - totalRequired}("");
            if (!success) revert TransferFailed();
        }

        return pool;
    }

    // ============ View Functions ============

    /// @notice Get all pools created by a specific address
    function getPoolsByCreator(address creator) external view returns (address[] memory) {
        return poolsByCreator[creator];
    }

    /// @notice Get all pools with pagination
    function getAllPools(uint256 offset, uint256 limit) external view returns (address[] memory pools, uint256 total) {
        total = allPools.length;
        if (offset >= total) return (new address[](0), total);
        uint256 end = offset + limit > total ? total : offset + limit;
        pools = new address[](end - offset);
        unchecked {
            for (uint256 i = offset; i < end; ++i) pools[i - offset] = allPools[i];
        }
    }

    /// @notice Get the total number of pools
    function getPoolCount() external view returns (uint256) {
        return allPools.length;
    }

    // ============ Receive ETH ============
    receive() external payable {}
}

File 2 of 7 : SquaresPool.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import {ISquaresPool} from "./interfaces/ISquaresPool.sol";
import {IERC20} from "./interfaces/IERC20.sol";
import {IVRFCoordinatorV2Plus, VRFConsumerBaseV2Plus, VRFV2PlusClient} from "./interfaces/IVRFCoordinatorV2Plus.sol";
import {IPool, IWrappedTokenGatewayV3, IAToken} from "./interfaces/IAaveV3.sol";
import {SquaresLib} from "./libraries/SquaresLib.sol";

/// @notice Minimal interface for getting admin from factory
interface ISquaresFactory {
    function admin() external view returns (address);
}

/// @title SquaresPool
/// @notice Super Bowl Squares with Chainlink VRF for randomness and admin score submission
contract SquaresPool is ISquaresPool, VRFConsumerBaseV2Plus {
    // ============ Constants ============
    uint16 private constant VRF_REQUEST_CONFIRMATIONS = 3;
    uint32 private constant VRF_NUM_WORDS = 1;
    uint32 private constant VRF_CALLBACK_GAS_LIMIT = 500000;

    // ============ Immutables ============
    address public immutable factory;
    address public immutable operator;

    // Aave integration
    IPool public aavePool;
    IWrappedTokenGatewayV3 public wethGateway;
    address public aToken;  // aWETH or aUSDC depending on paymentToken
    uint256 public totalPrincipalDeposited;  // Track principal separately from totalPot

    // ============ Pool Configuration ============
    string public name;
    uint256 public squarePrice;
    address public paymentToken;
    uint8 public maxSquaresPerUser;
    uint8[4] public payoutPercentages;
    string public teamAName;
    string public teamBName;
    uint256 public purchaseDeadline;
    uint256 public vrfTriggerTime;

    // Chainlink VRF Configuration
    uint256 public vrfSubscriptionId;
    bytes32 public vrfKeyHash;

    // ============ State ============
    PoolState public state;
    bytes32 public passwordHash;
    address[100] public grid;
    uint8[10] public rowNumbers;
    uint8[10] public colNumbers;
    bool public numbersSet;
    uint256 public totalPot;
    uint256 public squaresSold;

    // VRF state
    uint256 public vrfRequestId;
    bool public vrfRequested;

    // Score tracking
    mapping(Quarter => Score) public scores;

    // User tracking
    mapping(address => uint8) public userSquareCount;
    mapping(address => mapping(Quarter => bool)) public payoutClaimed;

    // Unclaimed winnings tracking
    uint256 public unclaimedRolledAmount;
    uint256 public finalDistributionPool;
    bool public finalDistributionCalculated;
    mapping(address => bool) public finalDistributionClaimed;

    // ============ Errors ============
    error InvalidState(PoolState current, PoolState required);
    error SquareAlreadyOwned(uint8 position);
    error InvalidPosition(uint8 position);
    error MaxSquaresExceeded(address user, uint8 current, uint8 max);
    error InsufficientPayment(uint256 sent, uint256 required);
    error TransferFailed();
    error PurchaseDeadlinePassed();
    error VRFTriggerTimeNotReached();
    error VRFAlreadyRequested();
    error OnlyOperator();
    error OnlyFactory();
    error PayoutAlreadyClaimed();
    error NotWinner();
    error ScoreNotSettled();
    error InvalidPayoutPercentages();
    error InvalidQuarterProgression();
    error InvalidPassword();
    error NoSquaresSold();
    error GameNotFinished();
    error NoYieldToWithdraw();
    error AaveNotConfigured();

    // ============ Modifiers ============
    modifier onlyOperator() {
        if (msg.sender != operator) revert OnlyOperator();
        _;
    }

    modifier onlyFactory() {
        if (msg.sender != factory) revert OnlyFactory();
        _;
    }

    modifier inState(PoolState required) {
        if (state != required) revert InvalidState(state, required);
        _;
    }

    // ============ Constructor ============
    constructor(
        address _vrfCoordinator,
        address _operator
    ) VRFConsumerBaseV2Plus(_vrfCoordinator) {
        factory = msg.sender;
        operator = _operator;
        state = PoolState.OPEN;
    }

    // ============ Initialization ============
    function initialize(PoolParams calldata params) external onlyFactory {
        if (!SquaresLib.validatePayoutPercentages(params.payoutPercentages)) {
            revert InvalidPayoutPercentages();
        }

        name = params.name;
        squarePrice = params.squarePrice;
        paymentToken = params.paymentToken;
        maxSquaresPerUser = params.maxSquaresPerUser;
        payoutPercentages = params.payoutPercentages;
        teamAName = params.teamAName;
        teamBName = params.teamBName;
        purchaseDeadline = params.purchaseDeadline;
        vrfTriggerTime = params.vrfTriggerTime;
        passwordHash = params.passwordHash;
    }

    /// @notice Set Chainlink VRF configuration (called by factory)
    function setVRFConfig(
        uint256 _subscriptionId,
        bytes32 _keyHash
    ) external onlyFactory {
        vrfSubscriptionId = _subscriptionId;
        vrfKeyHash = _keyHash;
    }

    /// @notice Set Aave V3 configuration (called by factory)
    function setAaveConfig(
        address _aavePool,
        address _wethGateway,
        address _aToken
    ) external onlyFactory {
        aavePool = IPool(_aavePool);
        wethGateway = IWrappedTokenGatewayV3(_wethGateway);
        aToken = _aToken;
    }

    // ============ Player Functions ============

    function buySquares(uint8[] calldata positions, string calldata password) external payable inState(PoolState.OPEN) {
        if (block.timestamp > purchaseDeadline) revert PurchaseDeadlinePassed();

        // Verify password for private pools
        if (passwordHash != bytes32(0)) {
            if (keccak256(bytes(password)) != passwordHash) revert InvalidPassword();
        }

        uint256 totalCost = squarePrice * positions.length;

        // Always track user square count for final distribution
        uint8 newCount = userSquareCount[msg.sender] + uint8(positions.length);

        // Check max squares limit if set
        if (maxSquaresPerUser > 0 && newCount > maxSquaresPerUser) {
            revert MaxSquaresExceeded(msg.sender, userSquareCount[msg.sender], maxSquaresPerUser);
        }
        userSquareCount[msg.sender] = newCount;

        if (paymentToken == address(0)) {
            if (msg.value < totalCost) revert InsufficientPayment(msg.value, totalCost);
            if (msg.value > totalCost) {
                (bool success,) = msg.sender.call{value: msg.value - totalCost}("");
                if (!success) revert TransferFailed();
            }
        } else {
            bool success = IERC20(paymentToken).transferFrom(msg.sender, address(this), totalCost);
            if (!success) revert TransferFailed();
        }

        // Deposit to Aave if configured
        if (address(aavePool) != address(0)) {
            _depositToAave(totalCost);
        }

        for (uint256 i = 0; i < positions.length; i++) {
            uint8 pos = positions[i];
            if (pos >= 100) revert InvalidPosition(pos);
            if (grid[pos] != address(0)) revert SquareAlreadyOwned(pos);

            grid[pos] = msg.sender;
            emit SquarePurchased(msg.sender, pos, squarePrice);
        }

        totalPot += totalCost;
        squaresSold += positions.length;
    }

    function claimPayout(Quarter quarter) external {
        if (uint8(quarter) == 0 && state < PoolState.Q1_SCORED) revert ScoreNotSettled();
        if (uint8(quarter) == 1 && state < PoolState.Q2_SCORED) revert ScoreNotSettled();
        if (uint8(quarter) == 2 && state < PoolState.Q3_SCORED) revert ScoreNotSettled();
        if (uint8(quarter) == 3 && state < PoolState.FINAL_SCORED) revert ScoreNotSettled();

        if (payoutClaimed[msg.sender][quarter]) revert PayoutAlreadyClaimed();

        (address winner, uint256 payout) = getWinner(quarter);
        if (msg.sender != winner) revert NotWinner();

        payoutClaimed[msg.sender][quarter] = true;

        if (paymentToken == address(0)) {
            (bool success,) = msg.sender.call{value: payout}("");
            if (!success) revert TransferFailed();
        } else {
            bool success = IERC20(paymentToken).transfer(msg.sender, payout);
            if (!success) revert TransferFailed();
        }

        emit PayoutClaimed(msg.sender, quarter, payout);
    }


    // ============ VRF Trigger Functions ============

    /// @notice Close pool and request VRF (called by factory for batch trigger)
    /// @dev Only the factory can call this (triggered by admin/scoreAdmin via triggerVRFForAllPools)
    function closePoolAndRequestVRFFromFactory() external onlyFactory inState(PoolState.OPEN) {
        if (squaresSold == 0) revert NoSquaresSold();
        if (vrfRequested) revert VRFAlreadyRequested();
        _closeAndRequestVRF();
    }

    /// @notice Internal function to close pool and request VRF
    function _closeAndRequestVRF() internal {
        state = PoolState.CLOSED;
        emit PoolClosed(block.timestamp);

        // Request VRF randomness with native payment
        vrfRequestId = vrfCoordinator.requestRandomWords(
            VRFV2PlusClient.RandomWordsRequest({
                keyHash: vrfKeyHash,
                subId: vrfSubscriptionId,
                requestConfirmations: VRF_REQUEST_CONFIRMATIONS,
                callbackGasLimit: VRF_CALLBACK_GAS_LIMIT,
                numWords: VRF_NUM_WORDS,
                extraArgs: VRFV2PlusClient._argsToBytes(
                    VRFV2PlusClient.ExtraArgsV1({nativePayment: true})
                )
            })
        );

        vrfRequested = true;
        emit VRFRequested(vrfRequestId);
    }

    // ============ Chainlink VRF Callback ============

    /// @notice VRF callback to assign random numbers
    /// @param requestId The VRF request ID
    /// @param randomWords The random words from VRF
    function fulfillRandomWords(uint256 requestId, uint256[] calldata randomWords) internal override {
        if (requestId != vrfRequestId) return;
        if (state != PoolState.CLOSED) return;

        uint256 randomness = randomWords[0];

        // Use randomness for row/column assignment
        rowNumbers = SquaresLib.fisherYatesShuffle(randomness);
        colNumbers = SquaresLib.fisherYatesShuffle(uint256(keccak256(abi.encodePacked(randomness, uint256(1)))));
        numbersSet = true;

        state = PoolState.NUMBERS_ASSIGNED;
        emit NumbersAssigned(rowNumbers, colNumbers);
    }

    // ============ Score Submission (Factory Only) ============

    /// @notice Submit score from factory (admin score submission)
    /// @param quarter The quarter to submit score for (0=Q1, 1=Q2, 2=Q3, 3=Final)
    /// @param teamAScore Team A's score
    /// @param teamBScore Team B's score
    function submitScoreFromFactory(
        uint8 quarter,
        uint8 teamAScore,
        uint8 teamBScore
    ) external onlyFactory {
        // Validate state - must have numbers assigned
        if (state < PoolState.NUMBERS_ASSIGNED) revert InvalidState(state, PoolState.NUMBERS_ASSIGNED);

        // Validate quarter progression
        if (quarter == 0 && state != PoolState.NUMBERS_ASSIGNED) revert InvalidQuarterProgression();
        if (quarter == 1 && state != PoolState.Q1_SCORED) revert InvalidQuarterProgression();
        if (quarter == 2 && state != PoolState.Q2_SCORED) revert InvalidQuarterProgression();
        if (quarter == 3 && state != PoolState.Q3_SCORED) revert InvalidQuarterProgression();

        Quarter q = Quarter(quarter);

        // Store score
        scores[q] = Score({
            teamAScore: teamAScore,
            teamBScore: teamBScore,
            submitted: true,
            settled: true,
            requestId: bytes32(0)
        });

        // Update state
        if (quarter == 0) state = PoolState.Q1_SCORED;
        else if (quarter == 1) state = PoolState.Q2_SCORED;
        else if (quarter == 2) state = PoolState.Q3_SCORED;
        else if (quarter == 3) state = PoolState.FINAL_SCORED;

        // Auto-payout the winner
        _settleQuarter(q);

        emit ScoreSubmitted(q, teamAScore, teamBScore, bytes32(0));
    }

    /// @notice Operator can manually submit scores (fallback)
    function submitScore(Quarter quarter, uint8 teamAScore, uint8 teamBScore) external {
        // Only operator can manually submit (fallback if APIs fail)
        if (msg.sender != operator) revert OnlyOperator();
        _validateQuarterProgression(quarter);

        Score storage score = scores[quarter];
        score.teamAScore = teamAScore;
        score.teamBScore = teamBScore;
        score.submitted = true;
        score.settled = true;

        _advanceState(quarter);

        // Auto-payout or roll forward unclaimed winnings
        _settleQuarter(quarter);

        emit ScoreSubmitted(quarter, teamAScore, teamBScore, bytes32(0));
    }

    // ============ Internal Functions ============

    function _validateQuarterProgression(Quarter quarter) internal view {
        if (quarter == Quarter.Q1 && state != PoolState.NUMBERS_ASSIGNED) {
            revert InvalidState(state, PoolState.NUMBERS_ASSIGNED);
        }
        if (quarter == Quarter.Q2 && state != PoolState.Q1_SCORED) {
            revert InvalidState(state, PoolState.Q1_SCORED);
        }
        if (quarter == Quarter.Q3 && state != PoolState.Q2_SCORED) {
            revert InvalidState(state, PoolState.Q2_SCORED);
        }
        if (quarter == Quarter.FINAL && state != PoolState.Q3_SCORED) {
            revert InvalidState(state, PoolState.Q3_SCORED);
        }
    }

    function _advanceState(Quarter quarter) internal {
        if (quarter == Quarter.Q1) state = PoolState.Q1_SCORED;
        else if (quarter == Quarter.Q2) state = PoolState.Q2_SCORED;
        else if (quarter == Quarter.Q3) state = PoolState.Q3_SCORED;
        else state = PoolState.FINAL_SCORED;
    }

    function _settleQuarter(Quarter quarter) internal {
        (address winner, uint256 payout) = getWinner(quarter);

        // Add any rolled amount from previous quarters
        uint256 effectivePayout = payout + unclaimedRolledAmount;

        if (winner != address(0) && effectivePayout > 0) {
            // Winner exists - pay them base payout + any rolled amount
            payoutClaimed[winner][quarter] = true;
            unclaimedRolledAmount = 0;

            // Determine actual payout amount
            uint256 actualPayout = effectivePayout;

            // Withdraw from Aave if configured (may return less due to rounding)
            if (address(aavePool) != address(0)) {
                actualPayout = _withdrawFromAave(effectivePayout);
            }

            // Only transfer if we have funds
            if (actualPayout > 0) {
                if (paymentToken == address(0)) {
                    (bool success,) = winner.call{value: actualPayout}("");
                    if (!success) revert TransferFailed();
                } else {
                    bool success = IERC20(paymentToken).transfer(winner, actualPayout);
                    if (!success) revert TransferFailed();
                }
            }

            emit PayoutClaimed(winner, quarter, actualPayout);
        } else if (payout > 0) {
            // No winner - roll this quarter's payout forward
            unclaimedRolledAmount += payout;
            emit UnclaimedWinningsRolled(quarter, payout, unclaimedRolledAmount);
        }

        // After FINAL, any remaining rolled amount gets auto-distributed to all square owners
        if (quarter == Quarter.FINAL && unclaimedRolledAmount > 0) {
            uint256 totalToDistribute = unclaimedRolledAmount;
            finalDistributionPool = totalToDistribute;
            finalDistributionCalculated = true;
            unclaimedRolledAmount = 0;

            // Withdraw from Aave if configured (may return less due to rounding)
            uint256 actualDistribute = totalToDistribute;
            if (address(aavePool) != address(0)) {
                actualDistribute = _withdrawFromAave(totalToDistribute);
            }

            emit FinalDistributionCalculated(actualDistribute, squaresSold);

            // Auto-distribute to all square owners (use actual withdrawn amount)
            if (actualDistribute > 0) {
                for (uint256 i = 0; i < 100; i++) {
                    address owner = grid[i];
                    if (owner != address(0) && !finalDistributionClaimed[owner]) {
                        uint256 ownerSquares = userSquareCount[owner];
                        uint256 ownerShare = (actualDistribute * ownerSquares) / squaresSold;

                        bool success;
                        if (paymentToken == address(0)) {
                            (success,) = owner.call{value: ownerShare}("");
                        } else {
                            success = IERC20(paymentToken).transfer(owner, ownerShare);
                        }

                        // Only mark as claimed if transfer succeeded
                        if (success) {
                            finalDistributionClaimed[owner] = true;
                            emit FinalDistributionClaimed(owner, ownerShare, ownerSquares);
                        }
                        // If transfer fails, user can still claim later via claimFinalDistribution
                    }
                }
            }
        }

        emit ScoreSettled(quarter, winner, payout);
    }

    /// @notice Deposit funds to Aave
    function _depositToAave(uint256 amount) internal {
        if (paymentToken == address(0)) {
            // ETH: deposit via WETHGateway
            wethGateway.depositETH{value: amount}(address(aavePool), address(this), 0);
        } else {
            // ERC20 (USDC): approve and supply to Aave
            IERC20(paymentToken).approve(address(aavePool), amount);
            aavePool.supply(paymentToken, amount, address(this), 0);
        }
        totalPrincipalDeposited += amount;
        emit DepositedToAave(amount);
    }

    /// @notice Withdraw funds from Aave
    /// @dev Uses min(amount, aTokenBalance) to handle Aave's interest rounding
    /// @return actualAmount The actual amount withdrawn (may be less than requested)
    function _withdrawFromAave(uint256 amount) internal returns (uint256 actualAmount) {
        // Get actual aToken balance to handle Aave's interest rounding
        uint256 aTokenBalance = IAToken(aToken).balanceOf(address(this));
        actualAmount = amount < aTokenBalance ? amount : aTokenBalance;

        if (actualAmount == 0) return 0;

        if (paymentToken == address(0)) {
            // For ETH, we need to approve the aWETH to the gateway first
            IAToken(aToken).approve(address(wethGateway), actualAmount);
            wethGateway.withdrawETH(address(aavePool), actualAmount, address(this));
        } else {
            aavePool.withdraw(paymentToken, actualAmount, address(this));
        }

        // Update principal tracking, capped to avoid underflow
        if (actualAmount >= totalPrincipalDeposited) {
            totalPrincipalDeposited = 0;
        } else {
            totalPrincipalDeposited -= actualAmount;
        }
        emit WithdrawnFromAave(actualAmount);
    }

    // ============ Admin Functions ============

    /// @notice Withdraw accrued yield (admin only, after game is finished)
    function withdrawYield() external {
        // Only factory admin can withdraw yield
        require(msg.sender == ISquaresFactory(factory).admin(), "Only admin");
        _withdrawYieldToAdmin();
    }

    /// @notice Withdraw accrued yield (called by factory for batch withdrawal)
    function withdrawYieldFromFactory() external onlyFactory {
        _withdrawYieldToAdmin();
    }

    /// @notice Emergency function to recover stuck ERC20 tokens (admin only)
    /// @dev Used to recover aTokens when wrong aToken address was configured
    /// @param token The ERC20 token to recover
    /// @param amount Amount to recover (use type(uint256).max for full balance)
    function emergencyRecoverToken(address token, uint256 amount) external {
        require(msg.sender == ISquaresFactory(factory).admin(), "Only admin");
        require(state == PoolState.FINAL_SCORED, "Game not finished");

        uint256 balance = IERC20(token).balanceOf(address(this));
        uint256 toRecover = amount == type(uint256).max ? balance : amount;
        require(toRecover <= balance, "Insufficient balance");

        address admin = ISquaresFactory(factory).admin();
        bool success = IERC20(token).transfer(admin, toRecover);
        require(success, "Transfer failed");
    }

    /// @notice Update aToken address (admin only)
    /// @dev Used to fix misconfigured aToken addresses
    function setAToken(address _aToken) external {
        require(msg.sender == ISquaresFactory(factory).admin(), "Only admin");
        address oldAToken = aToken;
        aToken = _aToken;
        emit ATokenUpdated(oldAToken, _aToken);
    }

    /// @notice Emergency function to manually assign numbers if VRF fails (admin only)
    /// @dev Only callable when pool is CLOSED and VRF was requested but never fulfilled
    /// @param rows Array of 10 unique digits (0-9) for row numbers
    /// @param cols Array of 10 unique digits (0-9) for column numbers
    function emergencySetNumbers(uint8[10] calldata rows, uint8[10] calldata cols) external {
        require(msg.sender == ISquaresFactory(factory).admin() || msg.sender == factory, "Only admin or factory");
        require(state == PoolState.CLOSED, "Pool must be CLOSED");
        require(vrfRequested, "VRF must have been requested");

        // Validate rows - must contain exactly digits 0-9 with no duplicates
        uint256 rowBitmap;
        for (uint8 i = 0; i < 10; i++) {
            require(rows[i] < 10, "Row digit must be 0-9");
            uint256 bit = 1 << rows[i];
            require(rowBitmap & bit == 0, "Duplicate row digit");
            rowBitmap |= bit;
        }

        // Validate cols - must contain exactly digits 0-9 with no duplicates
        uint256 colBitmap;
        for (uint8 i = 0; i < 10; i++) {
            require(cols[i] < 10, "Col digit must be 0-9");
            uint256 bit = 1 << cols[i];
            require(colBitmap & bit == 0, "Duplicate col digit");
            colBitmap |= bit;
        }

        // Assign the numbers
        rowNumbers = rows;
        colNumbers = cols;
        numbersSet = true;

        state = PoolState.NUMBERS_ASSIGNED;
        emit EmergencyNumbersAssigned(rows, cols);
    }

    /// @notice Internal yield withdrawal implementation
    function _withdrawYieldToAdmin() internal {
        address admin = ISquaresFactory(factory).admin();

        // Game must be finished
        if (state != PoolState.FINAL_SCORED) revert GameNotFinished();

        // Must have Aave configured
        if (address(aavePool) == address(0)) revert AaveNotConfigured();

        uint256 aTokenBalance = IAToken(aToken).balanceOf(address(this));
        if (aTokenBalance == 0) revert NoYieldToWithdraw();

        // Withdraw all remaining aTokens (yield) to admin
        if (paymentToken == address(0)) {
            IAToken(aToken).approve(address(wethGateway), aTokenBalance);
            wethGateway.withdrawETH(address(aavePool), aTokenBalance, admin);
        } else {
            aavePool.withdraw(paymentToken, aTokenBalance, admin);
        }

        emit YieldWithdrawn(admin, aTokenBalance);
    }

    // ============ View Functions ============

    function getGrid() external view returns (address[100] memory) {
        return grid;
    }

    function getNumbers() external view returns (uint8[10] memory rows, uint8[10] memory cols) {
        return (rowNumbers, colNumbers);
    }

    function getWinner(Quarter quarter) public view returns (address winner, uint256 payout) {
        Score storage score = scores[quarter];
        if (!score.settled) return (address(0), 0);

        uint8 winningPosition = SquaresLib.getWinningPosition(
            score.teamAScore,
            score.teamBScore,
            rowNumbers,
            colNumbers
        );

        winner = grid[winningPosition];
        payout = SquaresLib.calculatePayout(totalPot, payoutPercentages[uint8(quarter)]);
    }

    function getPoolInfo()
        external
        view
        returns (
            string memory _name,
            PoolState _state,
            uint256 _squarePrice,
            address _paymentToken,
            uint256 _totalPot,
            uint256 _squaresSold,
            string memory _teamAName,
            string memory _teamBName
        )
    {
        return (name, state, squarePrice, paymentToken, totalPot, squaresSold, teamAName, teamBName);
    }

    function getScore(Quarter quarter) external view returns (Score memory) {
        return scores[quarter];
    }

    function getPayoutPercentages() external view returns (uint8[4] memory) {
        return payoutPercentages;
    }

    function hasClaimed(address user, Quarter quarter) external view returns (bool) {
        return payoutClaimed[user][quarter];
    }

    function isPrivate() external view returns (bool) {
        return passwordHash != bytes32(0);
    }

    function getVRFStatus() external view returns (
        uint256 _vrfTriggerTime,
        bool _vrfRequested,
        uint256 _vrfRequestId,
        bool _numbersAssigned
    ) {
        return (vrfTriggerTime, vrfRequested, vrfRequestId, numbersSet);
    }

    /// @notice Get user's share of final distribution
    function getFinalDistributionShare(address user) external view returns (uint256 share, bool claimed) {
        claimed = finalDistributionClaimed[user];
        if (!finalDistributionCalculated || finalDistributionPool == 0) return (0, claimed);
        uint256 userSquares = userSquareCount[user];
        if (userSquares == 0) return (0, claimed);
        share = (finalDistributionPool * userSquares) / squaresSold;
    }

    /// @notice Get unclaimed winnings info
    function getUnclaimedInfo() external view returns (
        uint256 rolledAmount,
        uint256 distributionPool,
        bool distributionReady
    ) {
        rolledAmount = unclaimedRolledAmount;
        distributionPool = finalDistributionPool;
        distributionReady = finalDistributionCalculated && finalDistributionPool > 0;
    }

    /// @notice Get Aave yield info
    function getYieldInfo() external view returns (
        uint256 principal,
        uint256 aTokenBalance,
        uint256 yield,
        bool aaveConfigured
    ) {
        principal = totalPrincipalDeposited;
        aaveConfigured = address(aavePool) != address(0);
        if (aaveConfigured && aToken != address(0)) {
            aTokenBalance = IAToken(aToken).balanceOf(address(this));
            yield = aTokenBalance > principal ? aTokenBalance - principal : 0;
        }
    }

    // ============ Receive ETH ============
    receive() external payable {}
}

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

interface ISquaresPool {
    // Enums
    enum PoolState {
        OPEN,           // Squares can be purchased
        CLOSED,         // No more purchases, awaiting VRF randomness
        NUMBERS_ASSIGNED, // VRF fulfilled, game in progress
        Q1_SCORED,      // Quarter 1 score settled
        Q2_SCORED,      // Quarter 2 score settled
        Q3_SCORED,      // Quarter 3 score settled
        FINAL_SCORED    // Final score settled, game complete
    }

    enum Quarter {
        Q1,
        Q2,
        Q3,
        FINAL
    }

    // Structs
    struct PoolParams {
        string name;
        uint256 squarePrice;
        address paymentToken;       // address(0) for ETH
        uint8 maxSquaresPerUser;    // 0 = unlimited
        uint8[4] payoutPercentages; // [Q1, Q2, Q3, Final] must sum to 100
        string teamAName;
        string teamBName;
        uint256 purchaseDeadline;
        uint256 vrfTriggerTime;     // When VRF should be triggered
        bytes32 passwordHash;       // keccak256(password) for private pools, bytes32(0) for public
    }

    struct Score {
        uint8 teamAScore;
        uint8 teamBScore;
        bool submitted;
        bool settled;
        bytes32 requestId;  // Reserved for compatibility
    }

    // Events
    event SquarePurchased(address indexed buyer, uint8 indexed position, uint256 price);
    event PoolClosed(uint256 timestamp);
    event VRFRequested(uint256 requestId);
    event NumbersAssigned(uint8[10] rowNumbers, uint8[10] colNumbers);
    event ScoreSubmitted(Quarter indexed quarter, uint8 teamAScore, uint8 teamBScore, bytes32 requestId);
    event ScoreSettled(Quarter indexed quarter, address winner, uint256 payout);
    event PayoutClaimed(address indexed winner, Quarter indexed quarter, uint256 amount);
    event UnclaimedWinningsRolled(Quarter indexed quarter, uint256 amount, uint256 newTotalRolled);
    event FinalDistributionCalculated(uint256 totalAmount, uint256 squaresSold);
    event FinalDistributionClaimed(address indexed user, uint256 amount, uint256 squaresOwned);
    event YieldWithdrawn(address indexed admin, uint256 amount);
    event DepositedToAave(uint256 amount);
    event WithdrawnFromAave(uint256 amount);
    event ATokenUpdated(address indexed oldAToken, address indexed newAToken);
    event EmergencyNumbersAssigned(uint8[10] rowNumbers, uint8[10] colNumbers);

    // Player functions
    function buySquares(uint8[] calldata positions, string calldata password) external payable;
    function claimPayout(Quarter quarter) external;

    // VRF functions
    function closePoolAndRequestVRFFromFactory() external;

    // Score functions
    function submitScore(Quarter quarter, uint8 teamAScore, uint8 teamBScore) external;
    function submitScoreFromFactory(uint8 quarter, uint8 teamAScore, uint8 teamBScore) external;

    // View functions
    function getGrid() external view returns (address[100] memory);
    function getNumbers() external view returns (uint8[10] memory rows, uint8[10] memory cols);
    function getWinner(Quarter quarter) external view returns (address winner, uint256 payout);
    function getPoolInfo() external view returns (
        string memory name,
        PoolState state,
        uint256 squarePrice,
        address paymentToken,
        uint256 totalPot,
        uint256 squaresSold,
        string memory teamAName,
        string memory teamBName
    );
    function getScore(Quarter quarter) external view returns (Score memory);
    function getVRFStatus() external view returns (
        uint256 vrfTriggerTime,
        bool vrfRequested,
        uint256 vrfRequestId,
        bool numbersAssigned
    );
    function getFinalDistributionShare(address user) external view returns (uint256 share, bool claimed);
    function getUnclaimedInfo() external view returns (
        uint256 rolledAmount,
        uint256 distributionPool,
        bool distributionReady
    );
}

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

/// @title VRFV2PlusClient
/// @notice Library for VRF V2.5 request encoding
library VRFV2PlusClient {
    // extraArgs bytes prefix for V1
    bytes4 public constant EXTRA_ARGS_V1_TAG = bytes4(keccak256("VRF ExtraArgsV1"));

    struct ExtraArgsV1 {
        bool nativePayment;
    }

    struct RandomWordsRequest {
        bytes32 keyHash;
        uint256 subId;
        uint16 requestConfirmations;
        uint32 callbackGasLimit;
        uint32 numWords;
        bytes extraArgs;
    }

    function _argsToBytes(ExtraArgsV1 memory extraArgs) internal pure returns (bytes memory bts) {
        return abi.encodeWithSelector(EXTRA_ARGS_V1_TAG, extraArgs);
    }
}

/// @title IVRFCoordinatorV2Plus
/// @notice Interface for VRF Coordinator V2.5
interface IVRFCoordinatorV2Plus {
    /// @notice Request random words
    /// @param req The request parameters
    /// @return requestId The request ID
    function requestRandomWords(
        VRFV2PlusClient.RandomWordsRequest calldata req
    ) external returns (uint256 requestId);

    /// @notice Add a consumer to a subscription
    /// @param subId The subscription ID
    /// @param consumer The consumer address to add
    function addConsumer(uint256 subId, address consumer) external;

    /// @notice Remove a consumer from a subscription
    /// @param subId The subscription ID
    /// @param consumer The consumer address to remove
    function removeConsumer(uint256 subId, address consumer) external;

    /// @notice Get subscription details
    /// @param subId The subscription ID
    /// @return balance The subscription balance
    /// @return nativeBalance The native token balance
    /// @return reqCount The request count
    /// @return owner The subscription owner
    /// @return consumers The consumer addresses
    function getSubscription(
        uint256 subId
    )
        external
        view
        returns (
            uint96 balance,
            uint96 nativeBalance,
            uint64 reqCount,
            address owner,
            address[] memory consumers
        );

    /// @notice Check if pending request exists for a consumer
    function pendingRequestExists(uint256 subId) external view returns (bool);

    /// @notice Create a new subscription
    /// @return subId The new subscription ID
    function createSubscription() external returns (uint256 subId);

    /// @notice Fund a subscription with native tokens (ETH)
    /// @param subId The subscription ID to fund
    function fundSubscriptionWithNative(uint256 subId) external payable;

    /// @notice Cancel a subscription and send remaining funds to a recipient
    /// @param subId The subscription ID to cancel
    /// @param to The address to send remaining funds to
    function cancelSubscription(uint256 subId, address to) external;
}

/// @title VRFConsumerBaseV2Plus
/// @notice Abstract base contract for VRF V2.5 consumers
abstract contract VRFConsumerBaseV2Plus {
    error OnlyCoordinatorCanFulfill(address have, address want);

    IVRFCoordinatorV2Plus public immutable vrfCoordinator;

    constructor(address _vrfCoordinator) {
        vrfCoordinator = IVRFCoordinatorV2Plus(_vrfCoordinator);
    }

    /// @notice Callback function for VRF coordinator to call with random words
    /// @param requestId The request ID
    /// @param randomWords The array of random words
    function rawFulfillRandomWords(uint256 requestId, uint256[] calldata randomWords) external {
        if (msg.sender != address(vrfCoordinator)) {
            revert OnlyCoordinatorCanFulfill(msg.sender, address(vrfCoordinator));
        }
        fulfillRandomWords(requestId, randomWords);
    }

    /// @notice Internal function to be overridden by consumer contracts
    /// @param requestId The request ID
    /// @param randomWords The array of random words
    function fulfillRandomWords(uint256 requestId, uint256[] calldata randomWords) internal virtual;
}

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

/// @title SquaresLib
/// @notice Library for Super Bowl Squares game utilities
library SquaresLib {
    /// @notice Performs Fisher-Yates shuffle to assign random numbers 0-9 to positions
    /// @param seed Random seed from VRF
    /// @return numbers Array of 10 numbers (0-9) in shuffled order
    function fisherYatesShuffle(uint256 seed) internal pure returns (uint8[10] memory numbers) {
        // Initialize array with 0-9
        for (uint8 i = 0; i < 10; i++) {
            numbers[i] = i;
        }

        // Fisher-Yates shuffle
        for (uint256 i = 9; i > 0; i--) {
            // Generate random index from 0 to i
            uint256 j = uint256(keccak256(abi.encodePacked(seed, i))) % (i + 1);

            // Swap numbers[i] and numbers[j]
            uint8 temp = numbers[i];
            numbers[i] = numbers[uint8(j)];
            numbers[uint8(j)] = temp;
        }

        return numbers;
    }

    /// @notice Get the winning square position for a given score
    /// @param teamAScore Team A's score (full score, will be modulo 10)
    /// @param teamBScore Team B's score (full score, will be modulo 10)
    /// @param rowNumbers The random row number assignments (Team A)
    /// @param colNumbers The random column number assignments (Team B)
    /// @return position The winning square position (0-99)
    function getWinningPosition(
        uint8 teamAScore,
        uint8 teamBScore,
        uint8[10] memory rowNumbers,
        uint8[10] memory colNumbers
    ) internal pure returns (uint8) {
        uint8 teamALastDigit = teamAScore % 10;
        uint8 teamBLastDigit = teamBScore % 10;

        // Find row index for Team A's last digit
        uint8 rowIndex;
        for (uint8 i = 0; i < 10; i++) {
            if (rowNumbers[i] == teamALastDigit) {
                rowIndex = i;
                break;
            }
        }

        // Find column index for Team B's last digit
        uint8 colIndex;
        for (uint8 i = 0; i < 10; i++) {
            if (colNumbers[i] == teamBLastDigit) {
                colIndex = i;
                break;
            }
        }

        // Position = row * 10 + col
        return rowIndex * 10 + colIndex;
    }

    /// @notice Convert position (0-99) to row and column indices
    /// @param position Square position
    /// @return row Row index (0-9)
    /// @return col Column index (0-9)
    function positionToCoords(uint8 position) internal pure returns (uint8 row, uint8 col) {
        require(position < 100, "Invalid position");
        row = position / 10;
        col = position % 10;
    }

    /// @notice Convert row and column to position
    /// @param row Row index (0-9)
    /// @param col Column index (0-9)
    /// @return position Square position (0-99)
    function coordsToPosition(uint8 row, uint8 col) internal pure returns (uint8) {
        require(row < 10 && col < 10, "Invalid coordinates");
        return row * 10 + col;
    }

    /// @notice Validate payout percentages sum to 100
    /// @param percentages Array of 4 percentages [Q1, Q2, Q3, Final]
    /// @return valid True if percentages sum to 100
    function validatePayoutPercentages(uint8[4] memory percentages) internal pure returns (bool) {
        uint16 sum = uint16(percentages[0]) + uint16(percentages[1]) + uint16(percentages[2]) + uint16(percentages[3]);
        return sum == 100;
    }

    /// @notice Calculate payout amount for a quarter
    /// @param totalPot Total pot amount
    /// @param percentage Percentage for this quarter (0-100)
    /// @return payout Amount to pay out
    function calculatePayout(uint256 totalPot, uint8 percentage) internal pure returns (uint256) {
        return (totalPot * percentage) / 100;
    }

    /// @notice Build UMA assertion claim string for a score
    /// @param poolName Name of the pool
    /// @param quarter Quarter number (1-4)
    /// @param teamAName Team A name
    /// @param teamBName Team B name
    /// @param teamAScore Team A score
    /// @param teamBScore Team B score
    /// @return claim The assertion claim as bytes
    function buildScoreClaim(
        string memory poolName,
        uint8 quarter,
        string memory teamAName,
        string memory teamBName,
        uint8 teamAScore,
        uint8 teamBScore
    ) internal pure returns (bytes memory) {
        string memory quarterName;
        if (quarter == 1) quarterName = "Q1";
        else if (quarter == 2) quarterName = "Q2";
        else if (quarter == 3) quarterName = "Q3";
        else quarterName = "Final";

        return abi.encodePacked(
            "SuperBowl Squares Pool '",
            poolName,
            "' ",
            quarterName,
            " score: ",
            teamAName,
            "=",
            uint8ToString(teamAScore),
            ", ",
            teamBName,
            "=",
            uint8ToString(teamBScore)
        );
    }

    /// @notice Convert uint8 to string
    function uint8ToString(uint8 value) internal pure returns (string memory) {
        if (value == 0) {
            return "0";
        }
        uint8 temp = value;
        uint8 digits;
        while (temp != 0) {
            digits++;
            temp /= 10;
        }
        bytes memory buffer = new bytes(digits);
        while (value != 0) {
            digits -= 1;
            buffer[digits] = bytes1(uint8(48 + uint8(value % 10)));
            value /= 10;
        }
        return string(buffer);
    }
}

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

/// @notice Minimal ERC20 interface for payment tokens
interface IERC20 {
    function totalSupply() external view returns (uint256);
    function balanceOf(address account) external view returns (uint256);
    function transfer(address to, uint256 amount) external returns (bool);
    function allowance(address owner, address spender) external view returns (uint256);
    function approve(address spender, uint256 amount) external returns (bool);
    function transferFrom(address from, address to, uint256 amount) external returns (bool);

    event Transfer(address indexed from, address indexed to, uint256 value);
    event Approval(address indexed owner, address indexed spender, uint256 value);
}

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

/// @title IAaveV3
/// @notice Interfaces for Aave V3 protocol integration

interface IPool {
    /// @notice Supplies an `amount` of underlying asset into the reserve, receiving in return overlying aTokens.
    /// @param asset The address of the underlying asset to supply
    /// @param amount The amount to be supplied
    /// @param onBehalfOf The address that will receive the aTokens
    /// @param referralCode Code used to register the integrator originating the operation
    function supply(address asset, uint256 amount, address onBehalfOf, uint16 referralCode) external;

    /// @notice Withdraws an `amount` of underlying asset from the reserve, burning the equivalent aTokens owned
    /// @param asset The address of the underlying asset to withdraw
    /// @param amount The underlying amount to be withdrawn (use type(uint256).max to withdraw all)
    /// @param to The address that will receive the underlying
    /// @return The final amount withdrawn
    function withdraw(address asset, uint256 amount, address to) external returns (uint256);
}

interface IWrappedTokenGatewayV3 {
    /// @notice Deposits WETH into the reserve, using native ETH. A corresponding amount of the overlying aWETH is minted.
    /// @param pool The address of the Pool contract
    /// @param onBehalfOf The address that will receive the aWETH
    /// @param referralCode Code used to register the integrator originating the operation
    function depositETH(address pool, address onBehalfOf, uint16 referralCode) external payable;

    /// @notice Withdraws the WETH _reserves of msg.sender, sending the resulting ETH to the recipient
    /// @param pool The address of the Pool contract
    /// @param amount The amount of aWETH to withdraw and receive native ETH
    /// @param to The address that will receive the native ETH
    function withdrawETH(address pool, uint256 amount, address to) external;
}

interface IAToken {
    /// @notice Returns the scaled balance of the user
    function balanceOf(address account) external view returns (uint256);

    /// @notice Returns the address of the underlying asset
    function UNDERLYING_ASSET_ADDRESS() external view returns (address);

    /// @notice Approve spender to transfer tokens
    function approve(address spender, uint256 amount) external returns (bool);

    /// @notice Transfer tokens to recipient
    function transfer(address to, uint256 amount) external returns (bool);
}

Settings
{
  "remappings": [
    "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
    "@chainlink/contracts/=lib/chainlink/contracts/",
    "forge-std/=lib/forge-std/src/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 1
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "cancun",
  "viaIR": true
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"address","name":"_vrfCoordinator","type":"address"},{"internalType":"bytes32","name":"_vrfKeyHash","type":"bytes32"},{"internalType":"uint256","name":"_creationFee","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"sent","type":"uint256"},{"internalType":"uint256","name":"required","type":"uint256"}],"name":"InsufficientCreationFee","type":"error"},{"inputs":[],"name":"InvalidAddress","type":"error"},{"inputs":[],"name":"OnlyAdmin","type":"error"},{"inputs":[],"name":"PoolCreationIsPaused","type":"error"},{"inputs":[{"internalType":"string","name":"name","type":"string"}],"name":"PoolNameAlreadyExists","type":"error"},{"inputs":[],"name":"TransferFailed","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"pool","type":"address"},{"indexed":false,"internalType":"address","name":"gateway","type":"address"},{"indexed":false,"internalType":"address","name":"aWETH","type":"address"},{"indexed":false,"internalType":"address","name":"aUSDC","type":"address"}],"name":"AaveAddressesUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldAdmin","type":"address"},{"indexed":true,"internalType":"address","name":"newAdmin","type":"address"}],"name":"AdminTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newFee","type":"uint256"}],"name":"CreationFeeUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"poolsSet","type":"uint256"}],"name":"EmergencyNumbersSetForAllPools","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"FeesWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pool","type":"address"},{"indexed":true,"internalType":"address","name":"creator","type":"address"},{"indexed":false,"internalType":"string","name":"name","type":"string"},{"indexed":false,"internalType":"uint256","name":"squarePrice","type":"uint256"},{"indexed":false,"internalType":"address","name":"paymentToken","type":"address"}],"name":"PoolCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"paused","type":"bool"}],"name":"PoolCreationPaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldAdmin","type":"address"},{"indexed":true,"internalType":"address","name":"newAdmin","type":"address"}],"name":"ScoreAdminUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint8","name":"quarter","type":"uint8"},{"indexed":false,"internalType":"uint8","name":"teamAScore","type":"uint8"},{"indexed":false,"internalType":"uint8","name":"teamBScore","type":"uint8"}],"name":"ScoreSubmittedToAllPools","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"subscriptionId","type":"uint256"},{"indexed":true,"internalType":"address","name":"consumer","type":"address"}],"name":"VRFConsumerAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"subscriptionId","type":"uint256"},{"indexed":true,"internalType":"address","name":"fundsRecipient","type":"address"}],"name":"VRFSubscriptionCancelled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"subscriptionId","type":"uint256"}],"name":"VRFSubscriptionCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"subscriptionId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"VRFSubscriptionFunded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"poolsTriggered","type":"uint256"}],"name":"VRFTriggeredForAllPools","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"poolsWithdrawn","type":"uint256"}],"name":"YieldWithdrawnFromAllPools","type":"event"},{"inputs":[],"name":"aUSDC","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"aWETH","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"aavePool","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"admin","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"allPools","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"cancelAndWithdrawVRFSubscription","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"string","name":"name","type":"string"},{"internalType":"uint256","name":"squarePrice","type":"uint256"},{"internalType":"address","name":"paymentToken","type":"address"},{"internalType":"uint8","name":"maxSquaresPerUser","type":"uint8"},{"internalType":"uint8[4]","name":"payoutPercentages","type":"uint8[4]"},{"internalType":"string","name":"teamAName","type":"string"},{"internalType":"string","name":"teamBName","type":"string"},{"internalType":"uint256","name":"purchaseDeadline","type":"uint256"},{"internalType":"uint256","name":"vrfTriggerTime","type":"uint256"},{"internalType":"bytes32","name":"passwordHash","type":"bytes32"}],"internalType":"struct ISquaresPool.PoolParams","name":"params","type":"tuple"}],"name":"createPool","outputs":[{"internalType":"address","name":"pool","type":"address"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"creationFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"defaultVRFKeyHash","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"defaultVRFSubscriptionId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"emergencySetNumbersForAllPools","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"fundVRFSubscription","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"offset","type":"uint256"},{"internalType":"uint256","name":"limit","type":"uint256"}],"name":"getAllPools","outputs":[{"internalType":"address[]","name":"pools","type":"address[]"},{"internalType":"uint256","name":"total","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPoolCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"creator","type":"address"}],"name":"getPoolsByCreator","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"poolCreationPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"poolNameExists","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"poolsByCreator","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"scoreAdmin","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_pool","type":"address"},{"internalType":"address","name":"_gateway","type":"address"},{"internalType":"address","name":"_aWETH","type":"address"},{"internalType":"address","name":"_aUSDC","type":"address"}],"name":"setAaveAddresses","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_fee","type":"uint256"}],"name":"setCreationFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_paused","type":"bool"}],"name":"setPoolCreationPaused","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_scoreAdmin","type":"address"}],"name":"setScoreAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint96","name":"_amount","type":"uint96"}],"name":"setVRFFundingAmount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"keyHash","type":"bytes32"}],"name":"setVRFKeyHash","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"subscriptionId","type":"uint256"}],"name":"setVRFSubscription","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"quarter","type":"uint8"},{"internalType":"uint8","name":"teamAScore","type":"uint8"},{"internalType":"uint8","name":"teamBScore","type":"uint8"}],"name":"submitScoreToAllPools","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newAdmin","type":"address"}],"name":"transferAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"triggerVRFForAllPools","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"pool","type":"address"},{"internalType":"address","name":"_aavePool","type":"address"},{"internalType":"address","name":"_wethGateway","type":"address"},{"internalType":"address","name":"_aToken","type":"address"}],"name":"updatePoolAaveConfig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"vrfCoordinator","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vrfFundingAmount","outputs":[{"internalType":"uint96","name":"","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"wethGateway","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"withdrawFees","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawYieldFromAllPools","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]

0x60a060408181523462000165576060826200619e80380380916200002482856200018d565b833981010312620001655781516001600160a01b0381169283820362000165578260208201519101519184156200017c576080526004908155600591909155600680546001600160601b031633606081901b6001600160601b03191691909117909155600780546001600160a01b0319169091179055815163288688f960e21b815292602091849182905f905af191821562000172575f9262000134575b508160035551907fe636347194f3a03c799ea1e7db7e51cb4d89391b1091798a8c87e4f2636e4e465f80a2615fd89081620001c6823960805181818161098801528181610c4c01528181610ed8015281816110c3015281816112cb0152818161132d0152818161151801526115610152f35b9091506020813d60201162000169575b8162000153602093836200018d565b81010312620001655751905f620000c2565b5f80fd5b3d915062000144565b50513d5f823e3d90fd5b835163e6c4247b60e01b8152600490fd5b601f909101601f19168101906001600160401b03821190821017620001b157604052565b634e487b7160e01b5f52604160045260245ffdfe60808060405260043610156200001e575b5036156200001c575f80fd5b005b5f905f3560e01c90816303067a6c14620018f55750806311d9c46c14620018d6578063164e68de146200185357806319d625d114620017de5780631da7d7d114620017b4578063291a749e146200178a57806335a512d71462000fc657806341d1de971462000f7e5780634b884f061462000e93578063501c3fa91462000e6b578063559fda331462000e405780636d2a583a1462000e1557806370c8ea691462000d9457806375829def1462000d0d5780637d03d5ff1462000c245780637e9e0cd21462000b3057806381471bd21462000aa45780638dc44b2b1462000a015780638eec5d7014620009e2578063a03e4bc314620009b7578063a3e56fa81462000970578063a7753f091462000929578063b219762714620007e8578063b7d8622514620007be578063c038bedf146200079e578063c5e10eef1462000773578063c84728ef14620006d6578063dce0b4e414620006b6578063dfe44c4314620003e6578063e054921114620003bc578063e12f454214620002e1578063f5a1b8fb1462000282578063f631b0171462000251578063f851a440146200022e5763fcbf570b036200001057346200022b5760403660031901126200022b57620001e76200192c565b6001600160a01b039081168252600160205260408220805460243593908410156200022b57506020926200021b916200196e565b9190546040519260031b1c168152f35b80fd5b50346200022b57806003193601126200022b57602060065460601c604051908152f35b50346200022b5760203660031901126200022b5760ff60406020926004358152600284522054166040519015158152f35b50346200022b5760203660031901126200022b576004356001600160601b03811690819003620002dd576006548060601c3303620002cb576001600160601b0319161760065580f35b604051634755657960e01b8152600490fd5b5080fd5b50346200022b57806003193601126200022b5760065460601c3303620002cb57808154825b8181106200033d57837f6681ef9bb1d7e9f0d5fb6afdf11a8305d120fde4d23a961fd220bcee0108d8ee602085604051908152a180f35b83620003498262001943565b905460039190911b1c6001600160a01b0316803b15620002dd5781809160046040518095819363b8aa053760e01b83525af19182620003a0575b505062000394575b60010162000306565b6001909201916200038b565b620003ab9062001a21565b620003b857845f62000383565b8480fd5b50346200022b5760203660031901126200022b5760065460601c3303620002cb5760043560045580f35b50346200022b57806003193601126200022b5760065460609060601c3303620002cb5781548291825b8281106200044657847fa83a05390e989a35528cd2943a7571e710c21f546a19e2f314c1feda41ddcf80602086604051908152a180f35b620004518162001943565b905460405163c19d93fb60e01b815291602091600391821b1c6001600160a01b0316908284600481855afa89948162000677575b5062000499575b505050506001016200040f565b60078410156200066357600180941480620005ea575b156200048c57620004c08562001943565b9054604080514287820190815244928201929092529290931b1c871b6001600160601b0319168782015260748082018790528152919060a08301906001600160401b03821184831017620005d6578a936200054e92604052519020620005268162001d1c565b9460405190810191825286604082015260408152620005458162001a35565b51902062001d1c565b92813b15620005d25761028462000583918480946200058f604051988996879563afa3a4bb60e01b8752600487019062001cde565b61014485019062001cde565b5af19182620005b6575b5050620005aa575b8080806200048c565b909301926001620005a1565b620005c19062001a21565b620005ce57865f62000599565b8680fd5b8280fd5b634e487b7160e01b5f52604160045260245ffd5b506040516345366c4360e11b81528381600481865afa90811562000658578a9162000617575b50620004af565b90508381813d831162000650575b62000631818362001a51565b810103126200064c575180151581036200064c575f62000610565b8980fd5b503d62000625565b6040513d8c823e3d90fd5b634e487b7160e01b89526021600452602489fd5b9094508381813d8311620006ae575b62000692818362001a51565b810103126200064c575160078110156200064c57935f62000485565b503d62000686565b50346200022b57806003193601126200022b576020600554604051908152f35b50346200022b57602080600319360112620002dd576001600160a01b039182620006ff6200192c565b168152600192600183526040822060405194858693868454928381520193865286862095905b87838310620007585786906200073e8288038362001a51565b62000754604051928284938452830190620019d4565b0390f35b87548216865296840196899650909401939083019062000725565b50346200022b57806003193601126200022b576009546040516001600160a01b039091168152602090f35b50346200022b57806003193601126200022b576020600354604051908152f35b50346200022b5760203660031901126200022b5760065460601c3303620002cb5760043560055580f35b50346200022b5760603660031901126200022b5760043560ff81168091036200092557602480359060ff821680920362000925576044906044359160ff831680930362000925576007546001600160a01b03929083163314158062000915575b62000904578691825493835b858110620008915784897f4b1986dcbcb78b682e65a229e18c43d8e4a2080518b306cdb8f85b2ee1b14ba160408b8b82519182526020820152a280f35b816200089d8262001943565b90549060031b1c16803b15620009005787868a60648d8380966040519687958694637f92f43160e11b865260048601528d8501528b8401525af1620008e8575b505060010162000854565b620008f39062001a21565b620003b857845f620008dd565b8580fd5b6040516282b42960e81b8152600490fd5b5060065460601c33141562000848565b5f80fd5b50346200022b5760403660031901126200022b57620009666200095160243560043562001be8565b604051928392604084526040840190620019d4565b9060208301520390f35b50346200022b57806003193601126200022b576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b50346200022b57806003193601126200022b576008546040516001600160a01b039091168152602090f35b50346200022b57806003193601126200022b5760209054604051908152f35b50346200022b5762000a133662001984565b92909160065460601c3303620002cb577f1fea32c5c9d09eaea79b4999b471368cb3f599c79e5b4b7174655bd5ff3cbc1d9360809360018060a01b0380941693808060018060a01b0319958787600854161760085516928386600954161760095516928385600a541617600a55168093600b541617600b55604051938452602084015260408301526060820152a180f35b50346200022b5760203660031901126200022b5762000ac26200192c565b60065460601c3303620002cb576001600160a01b0390811690811562000b1e57816007549182167f05c46174528c76376ba492f660f23858b8a0294bfb60484c50afa4cb1863019d8580a36001600160a01b0319161760075580f35b60405163e6c4247b60e01b8152600490fd5b50346200022b57806003193601126200022b576007546001600160a01b039081163314158062000c14575b62000904578182805492815b84811062000b9e57827fef068e541d93ebd27fb844bdc1349aec687f85bc25f0306bf70767ae71df8f74602086604051908152a180f35b8162000baa8262001943565b90549060031b1c16803b1562000c105783808092600460405180958193635793980560e01b83525af1918262000bf8575b505062000bec575b60010162000b67565b60019093019262000be3565b62000c039062001a21565b62000c1057835f62000bdb565b8380fd5b5060065460601c33141562000b5b565b50806003193601126200022b5760065460601c3303620002cb57341562000cee5760035481907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690813b1562000cea57829060246040518094819363256d573f60e21b8352600483015234905af1801562000cdf5762000cc7575b506003545f8051602062005f838339815191526020604051348152a280f35b62000cd29062001a21565b6200022b57805f62000ca8565b6040513d84823e3d90fd5b5050fd5b6044906040519063273cc57560e01b8252600482015260016024820152fd5b50346200022b5760203660031901126200022b5762000d2b6200192c565b600654908160601c803303620002cb576001600160a01b03821690811562000b1e577ff8ccb027dfcd135e000e9d45e6cc2d662578a8825d4c45b5e32e0adf67e79ec68580a36001600160601b039190911660609190911b6001600160601b0319161760065580f35b50346200022b5762000da63662001984565b60069291925460601c3303620002cb5784936001600160a01b0316803b15620003b85762000def938580946040519687958694859363806b456d60e01b85526004850162001b86565b03925af1801562000cdf5762000e025750f35b62000e0d9062001a21565b6200022b5780f35b50346200022b57806003193601126200022b57600a546040516001600160a01b039091168152602090f35b50346200022b57806003193601126200022b57600b546040516001600160a01b039091168152602090f35b50346200022b57806003193601126200022b57602060ff60075460a01c166040519015158152f35b50346200022b5760203660031901126200022b5762000eb16200192c565b60065460601c3303620002cb576001600160a01b0381811691821562000b1e5760035484927f00000000000000000000000000000000000000000000000000000000000000001691823b1562000c105762000f2592849283604051809681958294622b825560e61b84526004840162001ba9565b03925af1801562000cdf5762000f66575b50506003547f834b5a3c46f0f4ca691c9fe03583b86e9f00d43fcfb141642dd9e1674a5c25908380a38060035580f35b62000f719062001a21565b620002dd57815f62000f36565b50346200022b5760203660031901126200022b576004359080548210156200022b57602062000fad8362001943565b905460405160039290921b1c6001600160a01b03168152f35b506003199060203683011262000925576001600160401b03600435811062000925576101a0809360043536030112620009255760ff60075460a01c1662001778576200101760048035018062001ac5565b620010228162001a75565b9062001032604051928362001a51565b808252602082019236828201116200092557815f92602092863783010152519020805f52600260205260ff60405f2054166200173e575f52600260205260405f20600160ff198254161790556200109760055460018060601b03600654169062001b1a565b908134106200171f576040519061416b80830191821183831017620005d657604091839162001e1883397f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681523360208201520301905ff0928315620016ed576001600160a01b0384163b15620009255762001153604051916315f9796d60e21b8352602060048401526200114060043560040160043560040162001b3c565b909160248501526101c484019162001afa565b600435602481013560448481019190915201356001600160a01b0381168103620009255760018060a01b0316606483015260ff6200119660646004350162001a12565b1660848301528160a48101608460043501905f905b60048210620016f85750505080620011f76200121b5f9462001208620011dd6101046004350160043560040162001b3c565b94909260231995610124948789840301868a015262001afa565b916004350160043560040162001b3c565b9061014494868403018587015262001afa565b90610164906004350135818401526101849060043501358184015260043501356101a483015203818360018060a01b0389165af18015620016ed57620016d7575b50600380546004546001600160a01b0386163b15620003b85760405163c1e4996b60e01b8152600481019290925260248201528381604481836001600160a01b038a165af18015620015e357908491620016bf575b50506008546001600160a01b03168062001606575b5080547f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03163b1562000c1057604051632fb1302360e21b81529084908290819062001328906001600160a01b038a16906004840162001ba9565b0381837f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165af18015620015e357908491620015ee575b50508054604051906001600160a01b038616817fcf382112f05f221144590c76e493ae0dd402c6d5a5556248c342e32023655dea8780a36006546001600160601b0316908162001516575b50505f54600160401b915081811015620005d657806001620013d792015f5562001943565b81546001600160a01b0388811692861b92831b921b191617905533845260016020526040842080549091811015620005d6576200141a916001820181556200196e565b81546001600160a01b038781169290941b91821b9390911b19169190911790556200144a60048035018062001ac5565b90620014736200145f60446004350162001b71565b916040519360608552606085019162001afa565b6004356024013560208401526001600160a01b0391821660408401523392918616917f0c5a5c56d638e96912bd0138c6b9e4c76e13383df2aa09d18bc936c4966c4b449181900390a3803411620014d9575b6040516001600160a01b0384168152602090f35b8180620014e881933462001bc2565b335af1620014f562001a91565b501562001504575f80620014c5565b6040516312171d8360e31b8152600490fd5b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03163b15620009005763256d573f60e21b835260048301528490829060249082907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165af18015620015e357908491620015cb575b505080545f8051602062005f83833981519152602060018060601b0360065416604051908152a25f8080620013b2565b620015d69062001a21565b620005d257825f6200159b565b6040513d86823e3d90fd5b620015f99062001a21565b620005d257825f62001367565b6001600160a01b036200161e60043560440162001b71565b16620016ac57600a546001600160a01b0316905b6009546001600160a01b03908116919087163b1562000900579085916200166e604051948593849363806b456d60e01b85526004850162001b86565b0381836001600160a01b038a165af18015620015e35790849162001694575b50620012c6565b6200169f9062001a21565b620005d257825f6200168d565b600b546001600160a01b03169062001632565b620016ca9062001a21565b620005d257825f620012b1565b620016e491925062001a21565b5f905f6200125c565b6040513d5f823e3d90fd5b8293506020809160ff6200170f6001959662001a12565b16815201930191018492620011ab565b60405163273cc57560e01b815234600482015260248101839052604490fd5b6200174e60048035018062001ac5565b620017746040519283926315c2072160e11b845260206004850152602484019162001afa565b0390fd5b604051639556375560e01b8152600490fd5b3462000925575f36600319011262000925576006546040516001600160601b039091168152602090f35b3462000925575f36600319011262000925576007546040516001600160a01b039091168152602090f35b34620009255760203660031901126200092557600435801515809103620009255760065460601c3303620002cb576007805460ff60a01b191660a083901b60ff60a01b161790556040519081527f3387d2b55c2294aa390fd99c3e1429775aa0e41d3da9f40db18bb3668d2e7c3390602090a1005b34620009255760203660031901126200092557620018706200192c565b60065460601c3303620002cb576001600160a01b03811690811562000b1e575f8080804780955af1620018a262001a91565b5015620015045760207fc0819c13be868895eb93e40eaceb96de976442fa1d404e5c55f14bb65a8c489a91604051908152a2005b3462000925575f36600319011262000925576020600454604051908152f35b3462000925576020366003190112620009255760065460601c33036200191d57600435600355005b634755657960e01b8152600490fd5b600435906001600160a01b03821682036200092557565b5f548110156200195a575f805260205f2001905f90565b634e487b7160e01b5f52603260045260245ffd5b80548210156200195a575f5260205f2001905f90565b608090600319011262000925576001600160a01b03906004358281168103620009255791602435818116810362000925579160443582811681036200092557916064359081168103620009255790565b9081518082526020808093019301915f5b828110620019f4575050505090565b83516001600160a01b031685529381019392810192600101620019e5565b359060ff821682036200092557565b6001600160401b038111620005d657604052565b606081019081106001600160401b03821117620005d657604052565b601f909101601f19168101906001600160401b03821190821017620005d657604052565b6001600160401b038111620005d657601f01601f191660200190565b3d1562001ac0573d9062001aa58262001a75565b9162001ab5604051938462001a51565b82523d5f602084013e565b606090565b903590601e19813603018212156200092557018035906001600160401b03821162000925576020019181360383136200092557565b908060209392818452848401375f828201840152601f01601f1916010190565b9190820180921162001b2857565b634e487b7160e01b5f52601160045260245ffd5b9035601e198236030181121562000925570160208101919035906001600160401b038211620009255781360383136200092557565b356001600160a01b0381168103620009255790565b6001600160a01b0391821681529181166020830152909116604082015260600190565b9081526001600160a01b03909116602082015260400190565b9190820391821162001b2857565b6001600160401b038111620005d65760051b60200190565b915f54918284101562001cb1578262001c02828662001b1a565b111562001c9e575081925b62001c19818562001bc2565b9362001c4262001c298662001bd0565b9562001c39604051978862001a51565b80875262001bd0565b60209590601f190136878301378095835b83811062001c62575050505050565b62001c6d8162001943565b9190548682039085518210156200195a576001938591858060a01b039160031b1c169160051b860101520162001c53565b62001caa908462001b1a565b9262001c0d565b5060405191925090602081016001600160401b03811182821017620005d6576040525f81525f3681379190565b5f915b600a831062001cef57505050565b60019060ff8351168152602080910192019201919062001ce1565b90600a8110156200195a5760051b0190565b6040805190926101408083016001600160401b03811184821017620005d6576040523683375f5b60ff8116600a81101562001d6c5760ff918162001d636001938762001d0a565b52011662001d43565b50509290916009805b62001d805750505090565b82516020810190838252828582015284815262001d9d8162001a35565b5190206001820180831162001b2857801562001e035762001df19060ff80918162001dc9878b62001d0a565b51169406169062001ddb828962001d0a565b511662001de9858962001d0a565b528662001d0a565b52801562001b28575f19018062001d75565b634e487b7160e01b5f52601260045260245ffdfe60e0346200010657601f6200416b38819003918201601f19168301916001600160401b038311848410176200010a57808492604094855283398101031262000106576200005a602062000052836200011e565b92016200011e565b6001600160a01b039091166080523360a05260c052600e805460ff191690556040516140379081620001348239608051818181610f01015281816122440152612a79015260a0518181816102fc0152818161066b015281816108a1015281816108e5015281816109c601528181610a5e015281816115ea0152818161166201528181611a6301528181611c29015281816120cb0152613a25015260c051818181611015015261236f0152f35b5f80fd5b634e487b7160e01b5f52604160045260245ffd5b51906001600160a01b0382168203620001065756fe608060409080825260049182361015610022575b5050503615610020575f80fd5b005b5f915f3560e01c908163041d443e14612be15750806306fdde0314612bc457806311aa09b114612ba7578063134000c014612b1357806318a7ea5f14612af55780631d821a7a14612ad25780631fe543e314612a4657806324b570a914612a285780632f12313f14612a0a5780633013ce29146129e257806336fa0bcd146129be57806349a046c1146129855780634e108c191461295557806351d2cc8f1461291c578063526022c014612895578063537db1da146123c1578063558ba2131461239e578063570ca7351461235b57806357939805146120ae57806357e5e5b414611be55780635bc5980c14611b295780635d6f2f7914611b0b5780635f09d84c14611a2657806360246c881461198357806360751ea01461193f5780636ec773f61461190457806373c77690146118c95780637902019414611637578063806b456d146115a65780638070bb281461158857806381c426e51461156a57806389f915f6146114f25780638a6cd886146114cf57806396871042146112a55780639c82a93814610fe35780639e19b50614610f7f578063a03e4bc314610f58578063a0c1f15e14610f30578063a3e56fa814610eed578063a88d272414610eb2578063ac62c29a14610e70578063afa3a4bb14610a1b578063b0175cc3146109fd578063b8aa0537146109b1578063b947d44a1461080c578063bafa5e8c14610993578063bd3a4aac14610956578063c19d93fb1461092e578063c1e4996b146108d0578063c45a01551461088d578063c5e10eef14610865578063c69a51a414610847578063d338edf31461080c578063dd4ee59c146107ee578063e3e664cd146106f5578063e507a8a41461063c578063e6a44a891461060a578063ea4365c1146105cc578063ed647d21146105ae578063faff660e1461058e5763ff25e86203610013573461058a57606036600319011261058a5782359060ff90818316809303610586576102f2612f95565b6102fa612fa5565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316330361057657600e91848354169060078210159081610563576002831061053f5787158080610531575b610521576001891491828481610510575b506104bb5760028a1493848181610500575b506104f05760038b14958691826104cb575b50506104bb578b8a10156104a85794879485948a98948e948e9960018f5f80516020613fe28339815191529f9060609f918e9251946103c186612c5a565b169e8f855261040a81602087019b169d8e8c5286019185835260606103f3829189019588875260808a01978852612fc3565b9751169b60ff199c8d895416178855511686613293565b518454915163ffff00001990921690151560101b62ff0000161790151560181b63ff00000016178355519101551561046457505050600391508254161790555b61045387613609565b81519384526020840152820152a280f35b1561047657505082541617905561044a565b919250901561048d5750815416600517905561044a565b610499575b505061044a565b81541660061790558880610492565b634e487b7160e01b8b5260218c5260248bfd5b875163529c679b60e01b81528c90fd5b9091506104dd57600514155f80610383565b634e487b7160e01b8c5260218d5260248cfd5b885163529c679b60e01b81528d90fd5b90506104dd578c86141581610371565b90506104a85760038514158461035f565b865163529c679b60e01b81528b90fd5b505f9250600284141561034e565b8551633bf2e2f960e11b815260449061055a818d0186612f4a565b60026024820152fd5b634e487b7160e01b895260218a52602489fd5b8251630636a15760e11b81528790fd5b5f80fd5b5080fd5b503461058a578160031936011261058a57602090600f5415159051908152f35b503461058a578160031936011261058a57602090600c549051908152f35b5090346106075760203660031901126106075782359283101561060757506105f661060392613367565b929091519283928361301f565b0390f35b80fd5b509034610607576020366003190112610607575061062e610629612f34565b6132fd565b825191825215156020820152f35b5082346106f157826003193601126106f15781516303e1469160e61b81526001600160a01b03916020908290817f000000000000000000000000000000000000000000000000000000000000000086165afa9081156106e4576106aa935084916106b5575b5016331461317a565b6106b2613a03565b80f35b6106d7915060203d6020116106dd575b6106cf8183612ce3565b81019061315b565b846106a1565b503d6106c5565b50505051903d90823e3d90fd5b8280fd5b509034610607578060031936011261060757600354815490926001600160a01b03918216151592918290819085806107e1575b610747575b505060809550815194855260208501528301526060820152f35b90935060209150600254169560248351809881936370a0823160e01b835230908301525afa9182156107d65780926107a1575b6080955084838181111561079a5761079292506130a8565b915f8061072d565b5050610792565b91506020853d6020116107ce575b816107bc60209383612ce3565b8101031261058657608094519161077a565b3d91506107af565b9051903d90823e3d90fd5b5080600254161515610728565b503461058a578160031936011261058a576020906005549051908152f35b503461058a5760ff61083c6020938361082436612ff0565b6001600160a01b039091168352607d87529120612fda565b541690519015158152f35b503461058a578160031936011261058a57602090607e549051908152f35b503461058a578160031936011261058a5760015490516001600160a01b039091168152602090f35b503461058a578160031936011261058a57517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b508290346106f157806003193601126106f1577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03163303610921575035600c55602435600d5580f35b51630636a15760e11b8152fd5b503461058a578160031936011261058a5760209061095460ff600e541691518092612f4a565bf35b503461058a57602036600319011261058a5760209160ff9082906001600160a01b03610980612f34565b1681526081855220541690519015158152f35b503461058a578160031936011261058a576020906078549051908152f35b508290346106f157826003193601126106f1577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316330361092157826106b2613a03565b503461058a578160031936011261058a57602090600b549051908152f35b50913461058a5761028090816003193601126106f15761014493368511610e6c573661028411610e6c5780516303e1469160e61b81526020906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000081169183818781865afa908115610e62578891610e45575b50163314908115610e3b575b5015610e0257600e549560ff92838816976007891015610def576001809903610db65784607a541615610d745787808a5b610cb3575b5050888889915b610bed575b5050858890895b8b87600a8310610bd25750505050607455828890895b8b87600a8310610bb157505050509060029160755560ff19908a8260765416176076551617600e5551938488845f925b600a8410610b935750505050506101408401905f915b600a8310610b7657877fadae328c77fa94227f7d61b017c8da7cd84a3e69885efae46b879b9bae3df9b78888a180f35b83808a9287610b8486612fb5565b16815201920192019190610b46565b819088610b9f87612fb5565b16815201930191019091848a91610b30565b90919293610bc99084610bc3876130e4565b91613147565b93019101610b00565b90919293610be49084610bc3876130e4565b93019101610aea565b868216600a80821015610cac5788610c0c610c07846132bb565b6130e4565b161015610c715787610c21610c078e936132bb565b161b90818116610c3857918b01871691178a610ade565b845162461bcd60e51b8152808a018890526013602482015272111d5c1b1a58d85d194818dbdb08191a59da5d606a1b6044820152606490fd5b845162461bcd60e51b8152808a018890526015602482015274436f6c206469676974206d75737420626520302d3960581b6044820152606490fd5b5050610ae3565b868216600a80821015610d6d5788610ccd610c07846132a9565b161015610d325787610ce2610c078e936132a9565b161b90818116610cf957918b01871691178a610ad2565b845162461bcd60e51b8152808a018890526013602482015272111d5c1b1a58d85d19481c9bddc8191a59da5d606a1b6044820152606490fd5b845162461bcd60e51b8152808a018890526015602482015274526f77206469676974206d75737420626520302d3960581b6044820152606490fd5b5050610ad7565b815162461bcd60e51b8152808701859052601c60248201527b159491881b5d5cdd081a185d99481899595b881c995c5d595cdd195960221b6044820152606490fd5b815162461bcd60e51b81528087018590526013602482015272141bdbdb081b5d5cdd0818994810d313d4d151606a1b6044820152606490fd5b634e487b7160e01b885260218652602488fd5b905162461bcd60e51b81529182015260156024820152744f6e6c792061646d696e206f7220666163746f727960581b6044820152606490fd5b905033145f610aa1565b610e5c9150843d86116106dd576106cf8183612ce3565b5f610a95565b85513d8a823e3d90fd5b8380fd5b503461058a578160031936011261058a57608090600b549060ff607a5416906079549060ff607654169281519485521515602085015283015215156060820152f35b50913461058a57602036600319011261058a573590600a821015610607575060ff60209260f88360051c6074015491519360031b161c168152f35b503461058a578160031936011261058a57517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b503461058a578160031936011261058a5760025490516001600160a01b039091168152602090f35b503461058a578160031936011261058a57905490516001600160a01b039091168152602090f35b509034610607576020366003190112610607578235928310156106075750610fa860a092612fc3565b60ff60018254920154918351938282168552828260081c166020860152828260101c1615159085015260181c16151560608301526080820152f35b5082346106f15760603660031901126106f15780359181831015610e6c57611009612f95565b91611012612fa5565b907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03163303611297578415808061126b575b611249576001861490818061121d575b6111f257600287149586806111da575b6111af576003881480611183575b611158578360ff61108a8a612fc3565b92169760ff199289848254161781556110a38882613293565b805463ffff0000191663010100001790555f93156111145750506003919250600e541617600e555b6110d486613609565b611101575091848260ff5f80516020613fe28339815191529560609551948552166020840152820152a280f35b634e487b7160e01b865260219052602485fd5b9092505f935f1461112d5750600e541617600e556110cb565b5f93509091501561114857600590600e541617600e556110cb565b600690600e541617600e556110cb565b600e548651633bf2e2f960e11b815260449161117a908288019060ff16612f4a565b60056024820152fd5b5060ff600e5416600781101561119c576005141561107a565b634e487b7160e01b8a526021855260248afd5b600e548651633bf2e2f960e11b815260449186906111d3908383019060ff16612f4a565b6024820152fd5b5060ff600e5416600781101561119c5784141561106c565b600e548551633bf2e2f960e11b8152604491611214908287019060ff16612f4a565b60036024820152fd5b5060ff600e54166007811015611236576003141561105c565b634e487b7160e01b895260218452602489fd5b600e548451633bf2e2f960e11b815260449161055a908286019060ff16612f4a565b5060ff600e54166007811015611284576002141561104c565b634e487b7160e01b885260218352602488fd5b82516327e1f1e560e01b8152fd5b5082346106f157602080600319360112610e6c57813592828410156114cb5760ff8085168015806114b4575b61147757600181148061149e575b611477576002811480611487575b611477576003148061144d575b61143d57338652607d835261131185838820612fda565b541661142e5761132084613367565b93906001600160a01b03908116330361141f57338752607d845261134686848920612fda565b805460ff19166001179055600654168061139b57508580808087335af161136b6130b5565b501561138d5750905f80516020613fc2833981519152915b519283523392a380f35b90516312171d8360e31b8152fd5b838351809263a9059cbb60e01b8252818a816113ba8b338a840161301f565b03925af19081156114155787916113e8575b501561138d5750905f80516020613fc283398151915291611383565b6114089150843d861161140e575b6114008183612ce3565b810190613090565b876113cc565b503d6113f6565b83513d89823e3d90fd5b5090516330c6392160e11b8152fd5b5163538d585960e11b81529050fd5b8151631d6c64bd60e21b81528490fd5b5080600e54166007811015611464576006116112fa565b634e487b7160e01b875260218552602487fd5b8251631d6c64bd60e21b81528590fd5b5081600e54166007811015610def576005116112ed565b5081600e54166007811015610def5785116112df565b5081600e54166007811015610def576003116112d1565b8480fd5b503461058a578160031936011261058a5760209060ff607a541690519015158152f35b503461058a578160031936011261058a578061095461028092519161151683612cc7565b610140809336903782815161152a81612cc7565b369037805192611539846131b3565b61154284612cc7565b61156282519261155184613223565b61155a84612cc7565b518095612f6b565b830190612f6b565b503461058a578160031936011261058a57602090607f549051908152f35b503461058a578160031936011261058a576020906003549051908152f35b5082346106f15760603660031901126106f1576115c1612f34565b906001600160a01b036024358181169290839003610586576044359482861680960361058657827f000000000000000000000000000000000000000000000000000000000000000016330361092157505060018060a01b0319921682855416178455816001541617600155600254161760025580f35b508290346106f157806003193601126106f157611652612f34565b602491823560018060a01b0392837f0000000000000000000000000000000000000000000000000000000000000000168351946303e1469160e61b9283875260209687818b81875afa9081156118bf576116b79184918d916118a2575016331461317a565b60ff600e541660078110156118905760060361185a5716918451936370a0823160e01b8552308986015286858981875afa948515611850578a95611821575b505f19810361181b5750835b84116117e25785908886518094819382525afa9081156117d85791611747939186938a916117bb575b5089865180968195829463a9059cbb60e01b84528d840161301f565b03925af19081156117b1578691611794575b5015611763578480f35b5162461bcd60e51b815292830152600f908201526e151c985b9cd9995c8819985a5b1959608a1b6044820152606490fd5b6117ab9150833d851161140e576114008183612ce3565b86611759565b82513d88823e3d90fd5b6117d29150843d86116106dd576106cf8183612ce3565b8a61172b565b84513d8a823e3d90fd5b845162461bcd60e51b815280890187905260148189015273496e73756666696369656e742062616c616e636560601b6044820152606490fd5b93611702565b9094508681813d8311611849575b6118398183612ce3565b810103126105865751938a6116f6565b503d61182f565b86513d8c823e3d90fd5b855162461bcd60e51b8152808a018890526011818a01527011d85b59481b9bdd08199a5b9a5cda1959607a1b6044820152606490fd5b634e487b7160e01b8b5260218a52888bfd5b6118b991508a3d8c116106dd576106cf8183612ce3565b8d6106a1565b87513d8d823e3d90fd5b503461058a57602036600319011261058a5760209160ff9082906001600160a01b036118f3612f34565b168152607c85522054169051908152f35b50913461058a57602036600319011261058a573590600a821015610607575060ff60209260f88360051c6075015491519360031b161c168152f35b503461058a578160031936011261058a57606090607e5490607f5460ff608054169081611979575b82519384526020840152151590820152f35b8015159150611967565b503461058a578160031936011261058a576119ee9061060360ff600e541691611a186005549360018060a01b036006541694607754607854906119c4612d06565b976119cd612e1f565b936119d6612da4565b976119f981519c8d9c8d610100908181520190612e9a565b9760208d0190612f4a565b8a01526060890152608088015260a087015285820360c0870152612e9a565b9083820360e0850152612e9a565b5082346106f15760203660031901126106f157611a41612f34565b82516303e1469160e61b815290926001600160a01b03929091906020908290817f000000000000000000000000000000000000000000000000000000000000000087165afa918215611b025750611aa39183918691611ae3575016331461317a565b600280546001600160a01b03198116938316938417909155167f53fab91b0553fa390c7df2daf18c9f2f21878be4f7669c66f13569b32fb1ae2d8380a380f35b611afc915060203d6020116106dd576106cf8183612ce3565b866106a1565b513d86823e3d90fd5b503461058a578160031936011261058a57602090600a549051908152f35b5082346106f15760203660031901126106f1578035908110156106f157611b759060a09360808451611b5a81612c5a565b82815282602082015282868201528260608201520152612fc3565b90805190611b8282612c5a565b82549060ff82169384845260ff60208501818560081c1681526080600185880194848860101c16151586528460608a019860181c161515885201549601958652835196875251166020860152511515908401525115156060830152516080820152f35b50913461058a576020906003198281360112610e6c578135926001600160401b03918285116120aa576101a08585019186360301126120aa576001600160a01b03967f00000000000000000000000000000000000000000000000000000000000000008816330361209b5760848601815191611c6083612c91565b82610104890193368511612097578684915b86831061207f575061ffff91508260ff6060611cac611ca0611cb695848060649a51169187015116906135f3565b838986015116906135f3565b92015116906135f3565b16036120705750611cc783806130ff565b86819b929b1161205d57808a9b89611ce2819c9d9b54612bfb565b89601f9c8d8311612031575b92505050908d8b8411600114611fcd5792611fc2575b50508160011b915f199060031b1c19161788555b60248901356005556044890135908116809103611fbe576006549060ff60a01b611d4460648c016130e4565b60a01b169160018060a81b03191617176006558890895b85898210611fa357505050600755611d7390826130ff565b90848211611f9057611d86600854612bfb565b868111611f63575b508890868311600114611efb57611dc99392918a9183611ef0575b50508160011b915f199060031b1c1916176008555b6101248701906130ff565b9490928511611edd5750611dde600954612bfb565b838111611ea6575b5085928411600114611e3b5750610184939291859183611e30575b50508160011b915f199060031b1c1916176009555b610144810135600a55610164810135600b550135600f5580f35b013590505f80611e01565b91601f198416600987528387209387905b828210611e8e57505091600193918561018497969410611e75575b505050811b01600955611e16565b01355f19600384901b60f8161c191690555f8080611e67565b80600185978294968801358155019601930190611e4c565b611ece90600988528288208580880160051c820192858910611ed4575b0160051c0190613131565b5f611de6565b92508192611ec3565b634e487b7160e01b875260419052602486fd5b013590505f80611da9565b60088a52848a2091601f1984168b5b87828210611f4d575050916001939185611dc997969410611f34575b505050811b01600855611dbe565b01355f19600384901b60f8161c191690555f8080611f26565b6001849682939587013581550195019201611f0a565b611f8a9060088b52858b208880860160051c820192888710611ed4570160051c0190613131565b5f611d8e565b634e487b7160e01b895260418752602489fd5b611fb5839483610bc3600195966130e4565b93019101611d5b565b8980fd5b013590505f80611d04565b925090601f1984168c8452898420935b8a82821061201b575050908460019594939210612002575b505050811b018855611d18565b01355f19600384901b60f8161c191690555f8080611ff5565b6001849682939587013581550195019201611fdd565b612054938152208c80860160051c8201928c8710611ed4570160051c0190613131565b8a5f898f611cee565b634e487b7160e01b8a526041885260248afd5b51632bfdf23b60e11b81528690fd5b819061208a84612fb5565b8152019101908790611c72565b8a80fd5b51630636a15760e11b81528490fd5b8580fd5b5082346106f157826003193601126106f1576001600160a01b03907f00000000000000000000000000000000000000000000000000000000000000008216330361234d57600e549260ff8416600781101561233a57806123165750607854156123095760ff607a54166122fc57600160ff1980951617600e558051924284527f925a19753e677c9dc36a80e0fc824ca0c5b1afde494872b43daccab9ffeaffd460208095a1600d54600c54835190946001600160401b039392909190878201858111838210176122e95786526001825285519163125fa26760e31b898401525115156024830152602482526121a282612cac565b85519460c08601908111868210176122e9579261224089969594938c9388958a528752848701998a5288870160038152606088016207a120815261ffff60808a01926001845260a08b019485528c519d8e9b8c9a8b99634d8e1c2f60e11b8b528a015251602489015251604488015251166064860152519063ffffffff8092166084860152511660a48401525160c060c484015260e4830190612e9a565b03927f0000000000000000000000000000000000000000000000000000000000000000165af19182156122df5785926122ad575b5060017fd28591fd5b8e6c8a0d976474c82e138053b4b2004d8d12bab9c3e5383ad9706b9483607955607a541617607a5551908152a180f35b9391508284813d83116122d8575b6122c58183612ce3565b8101031261058657925190926001612274565b503d6122bb565b81513d87823e3d90fd5b604184634e487b7160e01b5f525260245ffd5b5163029c583d60e01b8152fd5b516387dc625760e01b8152fd5b60449350612332915192633bf2e2f960e11b8452830190612f4a565b5f6024820152fd5b634e487b7160e01b865260218352602486fd5b8251630636a15760e11b8152fd5b503461058a578160031936011261058a57517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b503461058a578160031936011261058a5760209060ff6076541690519015158152f35b508281600319360112610586576001600160401b03908035828111610586576123ed9036908301612ed8565b936024928335948086116105865736602387011215610586578582013590811161058657368582880101116105865760ff9586600e5416600781101561288357806128605750600a54421161285057600f549182612809575b50505060059061245887600554613055565b95335f52602095607c875261247582865f205416838b169061307c565b8260065460a01c16801515806127fe575b6127cf5750335f52607c885282865f20911660ff1982541617905560018060a01b039182600654168881155f1461276957505088341061274d5788341161271f575b825f5416806125b3575b508a5b8a81106124fe578b6124f88c6124ed8d6077546130f2565b6077556078546130f2565b60785580f35b61250b81871b89016130e4565b8281169060648083101561259d5781101561258b576010018054868116612575576001600160a01b03191633908117909155875489519081526001939291907fd55d45dbc537b82471506213de5e3d6eb436a9048098dc7378087f719b128b9d908d90a3016124d5565b8951630d1a490360e31b81528089018490528690fd5b634e487b7160e01b8e5260328752848efd5b8951634e6153af60e11b81528089018490528690fd5b888a856006541680155f1461266857505050508a836001541684825416813b156106f1578b91606484928b51948593849263474cf53d60e01b84528c840152308a8401528560448401525af1801561265e5761264a575b50505b612619896003546130f2565b6003557fcfd990c8ea9e3bd9e38863886d1bef1edfff9ef9e09b19806edcae848db500c88887518b8152a18b6124d2565b61265390612c33565b612097578a8c61260a565b88513d84823e3d90fd5b612687935f8b5180968195829463095ea7b360e01b84528d840161301f565b03925af180156126f857612702575b50825f54168360065416813b15610586578a60845f92838b51958694859363617ba03760e01b85528c850152898401523060448401528160648401525af180156126f8576126e5575b5061260d565b6126f0919b50612c33565b5f998b6126df565b87513d5f823e3d90fd5b61271890893d8b1161140e576114008183612ce3565b508b612696565b5f80808061272d8d346130a8565b335af16127386130b5565b506124c85785516312171d8360e31b81528490fd5b855163b99e2ab760e01b815234818601528083018a9052604490fd5b5f9160648c8a5194859384926323b872dd60e01b8452338c850152308a85015260448401525af19081156126f8575f916127b257506124c85785516312171d8360e31b81528490fd5b6127c99150893d8b1161140e576114008183612ce3565b8c612738565b84606491888587607c8e335f5252825f205416915193639d69e24960e01b855233908501528301526044820152fd5b508084831611612486565b5f60206128158361303a565b9261282288519485612ce3565b808452808a83860196018637830101525190200361284257878080612446565b90516303e4e47560e21b8152fd5b835163bec2907f60e01b81528390fd5b835f8861287e604494895194633bf2e2f960e11b8652850190612f4a565b820152fd5b86602185634e487b7160e01b5f52525ffd5b508234610586575f36600319011261058657608082516128b481612c91565b36903781516007549060ff9360ff8316825260ff602093818160081c166020850152818160101c168385015260181c1660608301526128f282612c91565b51935f9190855b85841061290557608087f35b8480600192848651168152019301930192916128f9565b50823461058657602036600319011261058657359060648210156105865760109091015490516001600160a01b03919091168152602090f35b5034610586575f3660031901126105865761060390612972612e1f565b9051918291602083526020830190612e9a565b838234610586576020366003190112610586578135918210156105865760ff6129af602093612f08565b92905490519260031b1c168152f35b5034610586575f3660031901126105865760209060ff60065460a01c169051908152f35b5034610586575f3660031901126105865760065490516001600160a01b039091168152602090f35b5034610586575f36600319011261058657602090600f549051908152f35b5034610586575f366003190112610586576020906077549051908152f35b5082346105865781600319360112610586576024356001600160401b03811161058657612a769036908301612ed8565b917f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031633819003612ab557506100209350356134be565b6044925084519163073e64fd60e21b835233908301526024820152fd5b5034610586575f3660031901126105865760209060ff6080541690519015158152f35b5034610586575f366003190112610586576020906079549051908152f35b5034610586575f36600319011261058657805190612b3082612c75565b610c80809236903780519060105f835b60648210612b8757505050612b5482612c75565b51905f90825b60648310612b6757505050f35b81516001600160a01b031681526001929092019160209182019101612b5a565b82546001600160a01b031681526001928301929190910190602001612b40565b5034610586575f3660031901126105865761060390612972612da4565b5034610586575f3660031901126105865761060390612972612d06565b34610586575f36600319011261058657602090600d548152f35b90600182811c92168015612c29575b6020831014612c1557565b634e487b7160e01b5f52602260045260245ffd5b91607f1691612c0a565b6001600160401b038111612c4657604052565b634e487b7160e01b5f52604160045260245ffd5b60a081019081106001600160401b03821117612c4657604052565b610c8081019081106001600160401b03821117612c4657604052565b608081019081106001600160401b03821117612c4657604052565b606081019081106001600160401b03821117612c4657604052565b61014081019081106001600160401b03821117612c4657604052565b601f909101601f19168101906001600160401b03821190821017612c4657604052565b604051905f8260045491612d1983612bfb565b808352602093600190818116908115612d845750600114612d45575b5050612d4392500383612ce3565b565b9093915060045f52815f20935f915b818310612d6c575050612d4393508201015f80612d35565b85548884018501529485019487945091830191612d54565b915050612d4394925060ff191682840152151560051b8201015f80612d35565b604051905f8260095491612db783612bfb565b808352602093600190818116908115612d845750600114612de0575050612d4392500383612ce3565b9093915060095f52815f20935f915b818310612e07575050612d4393508201015f80612d35565b85548884018501529485019487945091830191612def565b604051905f8260085491612e3283612bfb565b808352602093600190818116908115612d845750600114612e5b575050612d4392500383612ce3565b9093915060085f52815f20935f915b818310612e82575050612d4393508201015f80612d35565b85548884018501529485019487945091830191612e6a565b91908251928382525f5b848110612ec4575050825f602080949584010152601f8019910116010190565b602081830181015184830182015201612ea4565b9181601f84011215610586578235916001600160401b038311610586576020808501948460051b01011161058657565b906004821015612f2057601f8260051c600701921690565b634e487b7160e01b5f52603260045260245ffd5b600435906001600160a01b038216820361058657565b906007821015612f575752565b634e487b7160e01b5f52602160045260245ffd5b5f915b600a8310612f7b57505050565b60019060ff83511681526020809101920192019190612f6e565b6024359060ff8216820361058657565b6044359060ff8216820361058657565b359060ff8216820361058657565b6004811015612f57575f52607b60205260405f2090565b906004811015612f57575f5260205260405f2090565b6040906003190112610586576004356001600160a01b0381168103610586579060243560048110156105865790565b6001600160a01b039091168152602081019190915260400190565b6001600160401b038111612c4657601f01601f191660200190565b8181029291811591840414171561306857565b634e487b7160e01b5f52601160045260245ffd5b9060ff8091169116019060ff821161306857565b90816020910312610586575180151581036105865790565b9190820391821161306857565b3d156130df573d906130c68261303a565b916130d46040519384612ce3565b82523d5f602084013e565b606090565b3560ff811681036105865790565b9190820180921161306857565b903590601e198136030182121561058657018035906001600160401b0382116105865760200191813603831361058657565b81811061313c575050565b5f8155600101613131565b9060ff809160031b9316831b921b19161790565b9081602091031261058657516001600160a01b03811681036105865790565b1561318157565b60405162461bcd60e51b815260206004820152600a60248201526927b7363c9030b236b4b760b11b6044820152606490fd5b61012060745460ff908181168452818160081c166020850152818160101c166040850152818160181c166060850152818160201c166080850152818160281c1660a0850152818160301c1660c0850152818160381c1660e0850152818160401c1661010085015260481c16910152565b61012060755460ff908181168452818160081c166020850152818160101c166040850152818160181c166060850152818160201c166080850152818160281c1660a0850152818160301c1660c0850152818160381c1660e0850152818160401c1661010085015260481c16910152565b9061ff0082549160081b169061ff001916179055565b600a811015612f205760051b60040190565b600a811015612f205760051b6101440190565b90600a811015612f205760051b0190565b81156132e9570490565b634e487b7160e01b5f52601260045260245ffd5b60018060a01b03165f52608160205260ff60405f20541660ff6080541615801561335d575b61335957607c60205260ff60405f20541680156133545761334861335191607f54613055565b607854906132df565b91565b505f91565b5f91565b50607f5415613322565b9061337182612fc3565b549060ff91828160181c16156134b5576040519161338e836131b3565b61339783612cc7565b6040516133a381613223565b6133ac81612cc7565b600a908386168290068616865f805b828116868110156134a657836133d286928c6132ce565b5116146133e35760010182166133bb565b939495969750505050919390935b5f9486600a815f9560081c160616925b8781168381101561349457886134188692856132ce565b511614613429576001018716613401565b94969550600a938693509150505b1602908282169182036130685761344d9161307c565b6064811015612f2057601001546077546001600160a01b0390911693906004821015612f575760649261348261349093612f08565b90549060031b1c1690613055565b0490565b505050505083600a9194939294613437565b505094959392965050506133f1565b505f9250829150565b929192607954036135ee57600e549060ff9060ff8316946007861015612f575760018096036135e65715612f20573593906134f885613cab565b5f905f5b600a81106135c45750506074556135366040519560209660208101918252600160408201526040815261352e81612cac565b519020613cab565b905f955f5b600a81106135a35750505050506002919260755560ff199060018260765416176076551617600e557fb48ed8f2b67f7a4cf1d83936fb4e55a40b7d5bb2b1fb09fe69e91f29a231aa85610280604051613593816131b3565b6135a06101408201613223565ba1565b90919293826135b88299848789511691613147565b9895019392910161353b565b90919360206135da829996848787511691613147565b930191019693966134fc565b505050509050565b509050565b91909161ffff8080941691160191821161306857565b61361281613367565b90607e805461362181856130f2565b6001600160a01b039184831690811515806139d7575b156139795750805f52607d60205260409161365488845f20612fda565b600160ff198254161790555f855580845f5416613969575b50806138c0575b6004881015612f575760205f80516020613fc283398151915291899451908152a35b6004906004861015612f575760038614806138b6575b6136ea575b505050907f4c68367ddbd34d3d0144e5b73c2842cda488332a1acacf11debff28fd03c346b916136e56040519283928361301f565b0390a2565b82549283607f556001935f60ff199260018460805416176080555580835f54166138a6575b50607892607854916040927fd24fb2570481d3406d54ce976d298a890625e4fb3a3451648580154b4294b53b84805184815260209384820152a181613759575b50505050506136b0565b5f5b60648110613769575061374f565b80848a92601001541680151580613891575b613787575b500161375b565b805f52607c845260ff875f205416876137ab6137a38389613055565b8c54906132df565b60065487908a168e81613821575050505f80808084885af16137cb6130b5565b505b6137da575b505050613780565b7f057130b033aff37ce94779177f5a0aba5e46cc901337064f7734906ff0afab6f92845f5260818852825f20878d825416179055825191825287820152a25f8087816137d2565b86979850955f84969761384a96959495519687958694859363a9059cbb60e01b8552840161301f565b03925af1908115613887579189918e969594935f9161386a575b506137cd565b6138819150883d8a1161140e576114008183612ce3565b5f613864565b89513d5f823e3d90fd5b50805f526081845260ff875f2054161561377b565b6138b09150613d70565b5f61370f565b50825415156136ab565b80846006541680155f146138f557505f808080938a5af16138df6130b5565b506136735782516312171d8360e31b8152600490fd5b61391991602091895f885180968195829463a9059cbb60e01b84526004840161301f565b03925af190811561395f575f9161394057506136735782516312171d8360e31b8152600490fd5b613959915060203d60201161140e576114008183612ce3565b5f6138df565b84513d5f823e3d90fd5b6139739150613d70565b5f61366c565b915050848061398a575b5050613695565b613993916130f2565b8083556004861015612f57577f60fc94fd6b7dad2e0cb4f5562c81be8ce4874e7806a06b53bae21e2a0a21471c604087928151908882526020820152a25f84613983565b50821515613637565b6001600160a01b0391821681526020810192909252909116604082015260600190565b604080516303e1469160e61b8152909160206001600160a01b036004828582817f000000000000000000000000000000000000000000000000000000000000000086165afa948515613ca1575f95613c82575b5060ff600e54166007811015613c6f57600603613c6157815f5416958615613c545760249495968484600254168351978880926370a0823160e01b825230888301525afa958615613b8d575f96613c25575b508515613c1757600654841680613b97575050613ae58486856002541686600154165f865180968195829463095ea7b360e01b84528b840161301f565b03925af18015613b8d57613b70575b50826001541691835f541690833b156105865788935f92838993613b2b87519d8e9687958694630402806960e51b865285016139e0565b03925af1968715613b66575f80516020613fa2833981519152959697613b57575b505b519485521692a2565b613b6090612c33565b5f613b4c565b50513d5f823e3d90fd5b613b8690853d871161140e576114008183612ce3565b505f613af4565b82513d5f823e3d90fd5b95859183975f8b96613bbf859c9d975197889687958694631a4ca37b60e21b865285016139e0565b03925af18015613c0d57613be3575b505f80516020613fa283398151915293613b4e565b8390813d8311613c06575b613bf88183612ce3565b81010312610586575f613bce565b503d613bee565b85513d5f823e3d90fd5b50516325b6fdd160e21b8152fd5b9095508481813d8311613c4d575b613c3d8183612ce3565b810103126105865751945f613aa8565b503d613c33565b51631d3b721560e11b8152fd5b855163b7c3a5b360e01b8152fd5b602182634e487b7160e01b5f525260245ffd5b613c9a919550833d85116106dd576106cf8183612ce3565b935f613a56565b86513d5f823e3d90fd5b60409160405190613cbb82612cc7565b6101403683375f5b60ff8116600a811015613ce85760ff9181613ce0600193876132ce565b520116613cc3565b50509290916009805b613cfb5750505090565b825160208101908382528285820152848152613d1681612cac565b519020600182018083116130685780156132e957613d609060ff809181613d3d878b6132ce565b511694061690613d4d82896132ce565b5116613d5985896132ce565b52866132ce565b528015613068575f190180613cf1565b600254604080516370a0823160e01b81523060048201526001600160a01b03945f94909360209392908490829060249082908b165afa908115613ebe575f91613f54575b5080821015613f4c5750935b8415613f4257600654861680613ec85750613dfc8386886002541689600154165f875180968195829463095ea7b360e01b84526004840161301f565b03925af18015613ebe57613ea1575b506001545f5487169616803b1561058657613e425f979188928551998a80948193630402806960e51b83528c3091600485016139e0565b03925af1958615613b8d575f80516020613f82833981519152949596613e90575b505b6003548690808210613e7e5750506003555b51848152a1565b613e8892506130a8565b600355613e77565b613e9a9150612c33565b5f80613e63565b613eb790843d861161140e576114008183612ce3565b505f613e0b565b83513d5f823e3d90fd5b613ef484915f93969798845416908551948580948193631a4ca37b60e21b83528d3091600485016139e0565b03925af18015613b8d57613f18575b505f80516020613f8283398151915292613e65565b8290813d8311613f3b575b613f2d8183612ce3565b81010312610586575f613f03565b503d613f23565b5050509150505f90565b905093613dc0565b90508381813d8311613f7a575b613f6b8183612ce3565b8101031261058657515f613db4565b503d613f6156fe60a52561bffd64573086f5280c1c9cd1bc6ab49cc478f8ccfe689dacec8677eeaed495d70193dd915b96156dc90595c95aa5cc4f6985a2cd79286232d96ae8e264c7f0ad3eacf9f60d2a7e70eca5b5a740b09c3132541056bf6ca27abb6f516e08a8313d2d7c3eae8e87faafbdf295a58197eb2e3767d54d2ae0cce7c3a48292a26469706673582212205df68c8c12fe9a2467be773dad0051a2c57e46708aa193c4f83eb4ec56f23e5564736f6c63430008180033bfe2b363f53fad720f333b704f83a37d87c0db5ab9059f3f446d13d1c1918429a2646970667358221220d0a1b62510f468f6381c50b2bef1327ba3a663a098fd0967c4e041e7509bfd4a64736f6c63430008180033000000000000000000000000d7f86b4b8cae7d942340ff628f82735b7a20893a8077df514608a09f83e4e8d300645594e5d7234665448ba83f51a50f842bd3d90000000000000000000000000000000000000000000000000000000000000000

Deployed Bytecode

0x60808060405260043610156200001e575b5036156200001c575f80fd5b005b5f905f3560e01c90816303067a6c14620018f55750806311d9c46c14620018d6578063164e68de146200185357806319d625d114620017de5780631da7d7d114620017b4578063291a749e146200178a57806335a512d71462000fc657806341d1de971462000f7e5780634b884f061462000e93578063501c3fa91462000e6b578063559fda331462000e405780636d2a583a1462000e1557806370c8ea691462000d9457806375829def1462000d0d5780637d03d5ff1462000c245780637e9e0cd21462000b3057806381471bd21462000aa45780638dc44b2b1462000a015780638eec5d7014620009e2578063a03e4bc314620009b7578063a3e56fa81462000970578063a7753f091462000929578063b219762714620007e8578063b7d8622514620007be578063c038bedf146200079e578063c5e10eef1462000773578063c84728ef14620006d6578063dce0b4e414620006b6578063dfe44c4314620003e6578063e054921114620003bc578063e12f454214620002e1578063f5a1b8fb1462000282578063f631b0171462000251578063f851a440146200022e5763fcbf570b036200001057346200022b5760403660031901126200022b57620001e76200192c565b6001600160a01b039081168252600160205260408220805460243593908410156200022b57506020926200021b916200196e565b9190546040519260031b1c168152f35b80fd5b50346200022b57806003193601126200022b57602060065460601c604051908152f35b50346200022b5760203660031901126200022b5760ff60406020926004358152600284522054166040519015158152f35b50346200022b5760203660031901126200022b576004356001600160601b03811690819003620002dd576006548060601c3303620002cb576001600160601b0319161760065580f35b604051634755657960e01b8152600490fd5b5080fd5b50346200022b57806003193601126200022b5760065460601c3303620002cb57808154825b8181106200033d57837f6681ef9bb1d7e9f0d5fb6afdf11a8305d120fde4d23a961fd220bcee0108d8ee602085604051908152a180f35b83620003498262001943565b905460039190911b1c6001600160a01b0316803b15620002dd5781809160046040518095819363b8aa053760e01b83525af19182620003a0575b505062000394575b60010162000306565b6001909201916200038b565b620003ab9062001a21565b620003b857845f62000383565b8480fd5b50346200022b5760203660031901126200022b5760065460601c3303620002cb5760043560045580f35b50346200022b57806003193601126200022b5760065460609060601c3303620002cb5781548291825b8281106200044657847fa83a05390e989a35528cd2943a7571e710c21f546a19e2f314c1feda41ddcf80602086604051908152a180f35b620004518162001943565b905460405163c19d93fb60e01b815291602091600391821b1c6001600160a01b0316908284600481855afa89948162000677575b5062000499575b505050506001016200040f565b60078410156200066357600180941480620005ea575b156200048c57620004c08562001943565b9054604080514287820190815244928201929092529290931b1c871b6001600160601b0319168782015260748082018790528152919060a08301906001600160401b03821184831017620005d6578a936200054e92604052519020620005268162001d1c565b9460405190810191825286604082015260408152620005458162001a35565b51902062001d1c565b92813b15620005d25761028462000583918480946200058f604051988996879563afa3a4bb60e01b8752600487019062001cde565b61014485019062001cde565b5af19182620005b6575b5050620005aa575b8080806200048c565b909301926001620005a1565b620005c19062001a21565b620005ce57865f62000599565b8680fd5b8280fd5b634e487b7160e01b5f52604160045260245ffd5b506040516345366c4360e11b81528381600481865afa90811562000658578a9162000617575b50620004af565b90508381813d831162000650575b62000631818362001a51565b810103126200064c575180151581036200064c575f62000610565b8980fd5b503d62000625565b6040513d8c823e3d90fd5b634e487b7160e01b89526021600452602489fd5b9094508381813d8311620006ae575b62000692818362001a51565b810103126200064c575160078110156200064c57935f62000485565b503d62000686565b50346200022b57806003193601126200022b576020600554604051908152f35b50346200022b57602080600319360112620002dd576001600160a01b039182620006ff6200192c565b168152600192600183526040822060405194858693868454928381520193865286862095905b87838310620007585786906200073e8288038362001a51565b62000754604051928284938452830190620019d4565b0390f35b87548216865296840196899650909401939083019062000725565b50346200022b57806003193601126200022b576009546040516001600160a01b039091168152602090f35b50346200022b57806003193601126200022b576020600354604051908152f35b50346200022b5760203660031901126200022b5760065460601c3303620002cb5760043560055580f35b50346200022b5760603660031901126200022b5760043560ff81168091036200092557602480359060ff821680920362000925576044906044359160ff831680930362000925576007546001600160a01b03929083163314158062000915575b62000904578691825493835b858110620008915784897f4b1986dcbcb78b682e65a229e18c43d8e4a2080518b306cdb8f85b2ee1b14ba160408b8b82519182526020820152a280f35b816200089d8262001943565b90549060031b1c16803b15620009005787868a60648d8380966040519687958694637f92f43160e11b865260048601528d8501528b8401525af1620008e8575b505060010162000854565b620008f39062001a21565b620003b857845f620008dd565b8580fd5b6040516282b42960e81b8152600490fd5b5060065460601c33141562000848565b5f80fd5b50346200022b5760403660031901126200022b57620009666200095160243560043562001be8565b604051928392604084526040840190620019d4565b9060208301520390f35b50346200022b57806003193601126200022b576040517f000000000000000000000000d7f86b4b8cae7d942340ff628f82735b7a20893a6001600160a01b03168152602090f35b50346200022b57806003193601126200022b576008546040516001600160a01b039091168152602090f35b50346200022b57806003193601126200022b5760209054604051908152f35b50346200022b5762000a133662001984565b92909160065460601c3303620002cb577f1fea32c5c9d09eaea79b4999b471368cb3f599c79e5b4b7174655bd5ff3cbc1d9360809360018060a01b0380941693808060018060a01b0319958787600854161760085516928386600954161760095516928385600a541617600a55168093600b541617600b55604051938452602084015260408301526060820152a180f35b50346200022b5760203660031901126200022b5762000ac26200192c565b60065460601c3303620002cb576001600160a01b0390811690811562000b1e57816007549182167f05c46174528c76376ba492f660f23858b8a0294bfb60484c50afa4cb1863019d8580a36001600160a01b0319161760075580f35b60405163e6c4247b60e01b8152600490fd5b50346200022b57806003193601126200022b576007546001600160a01b039081163314158062000c14575b62000904578182805492815b84811062000b9e57827fef068e541d93ebd27fb844bdc1349aec687f85bc25f0306bf70767ae71df8f74602086604051908152a180f35b8162000baa8262001943565b90549060031b1c16803b1562000c105783808092600460405180958193635793980560e01b83525af1918262000bf8575b505062000bec575b60010162000b67565b60019093019262000be3565b62000c039062001a21565b62000c1057835f62000bdb565b8380fd5b5060065460601c33141562000b5b565b50806003193601126200022b5760065460601c3303620002cb57341562000cee5760035481907f000000000000000000000000d7f86b4b8cae7d942340ff628f82735b7a20893a6001600160a01b031690813b1562000cea57829060246040518094819363256d573f60e21b8352600483015234905af1801562000cdf5762000cc7575b506003545f8051602062005f838339815191526020604051348152a280f35b62000cd29062001a21565b6200022b57805f62000ca8565b6040513d84823e3d90fd5b5050fd5b6044906040519063273cc57560e01b8252600482015260016024820152fd5b50346200022b5760203660031901126200022b5762000d2b6200192c565b600654908160601c803303620002cb576001600160a01b03821690811562000b1e577ff8ccb027dfcd135e000e9d45e6cc2d662578a8825d4c45b5e32e0adf67e79ec68580a36001600160601b039190911660609190911b6001600160601b0319161760065580f35b50346200022b5762000da63662001984565b60069291925460601c3303620002cb5784936001600160a01b0316803b15620003b85762000def938580946040519687958694859363806b456d60e01b85526004850162001b86565b03925af1801562000cdf5762000e025750f35b62000e0d9062001a21565b6200022b5780f35b50346200022b57806003193601126200022b57600a546040516001600160a01b039091168152602090f35b50346200022b57806003193601126200022b57600b546040516001600160a01b039091168152602090f35b50346200022b57806003193601126200022b57602060ff60075460a01c166040519015158152f35b50346200022b5760203660031901126200022b5762000eb16200192c565b60065460601c3303620002cb576001600160a01b0381811691821562000b1e5760035484927f000000000000000000000000d7f86b4b8cae7d942340ff628f82735b7a20893a1691823b1562000c105762000f2592849283604051809681958294622b825560e61b84526004840162001ba9565b03925af1801562000cdf5762000f66575b50506003547f834b5a3c46f0f4ca691c9fe03583b86e9f00d43fcfb141642dd9e1674a5c25908380a38060035580f35b62000f719062001a21565b620002dd57815f62000f36565b50346200022b5760203660031901126200022b576004359080548210156200022b57602062000fad8362001943565b905460405160039290921b1c6001600160a01b03168152f35b506003199060203683011262000925576001600160401b03600435811062000925576101a0809360043536030112620009255760ff60075460a01c1662001778576200101760048035018062001ac5565b620010228162001a75565b9062001032604051928362001a51565b808252602082019236828201116200092557815f92602092863783010152519020805f52600260205260ff60405f2054166200173e575f52600260205260405f20600160ff198254161790556200109760055460018060601b03600654169062001b1a565b908134106200171f576040519061416b80830191821183831017620005d657604091839162001e1883397f000000000000000000000000d7f86b4b8cae7d942340ff628f82735b7a20893a6001600160a01b031681523360208201520301905ff0928315620016ed576001600160a01b0384163b15620009255762001153604051916315f9796d60e21b8352602060048401526200114060043560040160043560040162001b3c565b909160248501526101c484019162001afa565b600435602481013560448481019190915201356001600160a01b0381168103620009255760018060a01b0316606483015260ff6200119660646004350162001a12565b1660848301528160a48101608460043501905f905b60048210620016f85750505080620011f76200121b5f9462001208620011dd6101046004350160043560040162001b3c565b94909260231995610124948789840301868a015262001afa565b916004350160043560040162001b3c565b9061014494868403018587015262001afa565b90610164906004350135818401526101849060043501358184015260043501356101a483015203818360018060a01b0389165af18015620016ed57620016d7575b50600380546004546001600160a01b0386163b15620003b85760405163c1e4996b60e01b8152600481019290925260248201528381604481836001600160a01b038a165af18015620015e357908491620016bf575b50506008546001600160a01b03168062001606575b5080547f000000000000000000000000d7f86b4b8cae7d942340ff628f82735b7a20893a6001600160a01b03163b1562000c1057604051632fb1302360e21b81529084908290819062001328906001600160a01b038a16906004840162001ba9565b0381837f000000000000000000000000d7f86b4b8cae7d942340ff628f82735b7a20893a6001600160a01b03165af18015620015e357908491620015ee575b50508054604051906001600160a01b038616817fcf382112f05f221144590c76e493ae0dd402c6d5a5556248c342e32023655dea8780a36006546001600160601b0316908162001516575b50505f54600160401b915081811015620005d657806001620013d792015f5562001943565b81546001600160a01b0388811692861b92831b921b191617905533845260016020526040842080549091811015620005d6576200141a916001820181556200196e565b81546001600160a01b038781169290941b91821b9390911b19169190911790556200144a60048035018062001ac5565b90620014736200145f60446004350162001b71565b916040519360608552606085019162001afa565b6004356024013560208401526001600160a01b0391821660408401523392918616917f0c5a5c56d638e96912bd0138c6b9e4c76e13383df2aa09d18bc936c4966c4b449181900390a3803411620014d9575b6040516001600160a01b0384168152602090f35b8180620014e881933462001bc2565b335af1620014f562001a91565b501562001504575f80620014c5565b6040516312171d8360e31b8152600490fd5b7f000000000000000000000000d7f86b4b8cae7d942340ff628f82735b7a20893a6001600160a01b03163b15620009005763256d573f60e21b835260048301528490829060249082907f000000000000000000000000d7f86b4b8cae7d942340ff628f82735b7a20893a6001600160a01b03165af18015620015e357908491620015cb575b505080545f8051602062005f83833981519152602060018060601b0360065416604051908152a25f8080620013b2565b620015d69062001a21565b620005d257825f6200159b565b6040513d86823e3d90fd5b620015f99062001a21565b620005d257825f62001367565b6001600160a01b036200161e60043560440162001b71565b16620016ac57600a546001600160a01b0316905b6009546001600160a01b03908116919087163b1562000900579085916200166e604051948593849363806b456d60e01b85526004850162001b86565b0381836001600160a01b038a165af18015620015e35790849162001694575b50620012c6565b6200169f9062001a21565b620005d257825f6200168d565b600b546001600160a01b03169062001632565b620016ca9062001a21565b620005d257825f620012b1565b620016e491925062001a21565b5f905f6200125c565b6040513d5f823e3d90fd5b8293506020809160ff6200170f6001959662001a12565b16815201930191018492620011ab565b60405163273cc57560e01b815234600482015260248101839052604490fd5b6200174e60048035018062001ac5565b620017746040519283926315c2072160e11b845260206004850152602484019162001afa565b0390fd5b604051639556375560e01b8152600490fd5b3462000925575f36600319011262000925576006546040516001600160601b039091168152602090f35b3462000925575f36600319011262000925576007546040516001600160a01b039091168152602090f35b34620009255760203660031901126200092557600435801515809103620009255760065460601c3303620002cb576007805460ff60a01b191660a083901b60ff60a01b161790556040519081527f3387d2b55c2294aa390fd99c3e1429775aa0e41d3da9f40db18bb3668d2e7c3390602090a1005b34620009255760203660031901126200092557620018706200192c565b60065460601c3303620002cb576001600160a01b03811690811562000b1e575f8080804780955af1620018a262001a91565b5015620015045760207fc0819c13be868895eb93e40eaceb96de976442fa1d404e5c55f14bb65a8c489a91604051908152a2005b3462000925575f36600319011262000925576020600454604051908152f35b3462000925576020366003190112620009255760065460601c33036200191d57600435600355005b634755657960e01b8152600490fd5b600435906001600160a01b03821682036200092557565b5f548110156200195a575f805260205f2001905f90565b634e487b7160e01b5f52603260045260245ffd5b80548210156200195a575f5260205f2001905f90565b608090600319011262000925576001600160a01b03906004358281168103620009255791602435818116810362000925579160443582811681036200092557916064359081168103620009255790565b9081518082526020808093019301915f5b828110620019f4575050505090565b83516001600160a01b031685529381019392810192600101620019e5565b359060ff821682036200092557565b6001600160401b038111620005d657604052565b606081019081106001600160401b03821117620005d657604052565b601f909101601f19168101906001600160401b03821190821017620005d657604052565b6001600160401b038111620005d657601f01601f191660200190565b3d1562001ac0573d9062001aa58262001a75565b9162001ab5604051938462001a51565b82523d5f602084013e565b606090565b903590601e19813603018212156200092557018035906001600160401b03821162000925576020019181360383136200092557565b908060209392818452848401375f828201840152601f01601f1916010190565b9190820180921162001b2857565b634e487b7160e01b5f52601160045260245ffd5b9035601e198236030181121562000925570160208101919035906001600160401b038211620009255781360383136200092557565b356001600160a01b0381168103620009255790565b6001600160a01b0391821681529181166020830152909116604082015260600190565b9081526001600160a01b03909116602082015260400190565b9190820391821162001b2857565b6001600160401b038111620005d65760051b60200190565b915f54918284101562001cb1578262001c02828662001b1a565b111562001c9e575081925b62001c19818562001bc2565b9362001c4262001c298662001bd0565b9562001c39604051978862001a51565b80875262001bd0565b60209590601f190136878301378095835b83811062001c62575050505050565b62001c6d8162001943565b9190548682039085518210156200195a576001938591858060a01b039160031b1c169160051b860101520162001c53565b62001caa908462001b1a565b9262001c0d565b5060405191925090602081016001600160401b03811182821017620005d6576040525f81525f3681379190565b5f915b600a831062001cef57505050565b60019060ff8351168152602080910192019201919062001ce1565b90600a8110156200195a5760051b0190565b6040805190926101408083016001600160401b03811184821017620005d6576040523683375f5b60ff8116600a81101562001d6c5760ff918162001d636001938762001d0a565b52011662001d43565b50509290916009805b62001d805750505090565b82516020810190838252828582015284815262001d9d8162001a35565b5190206001820180831162001b2857801562001e035762001df19060ff80918162001dc9878b62001d0a565b51169406169062001ddb828962001d0a565b511662001de9858962001d0a565b528662001d0a565b52801562001b28575f19018062001d75565b634e487b7160e01b5f52601260045260245ffdfe60e0346200010657601f6200416b38819003918201601f19168301916001600160401b038311848410176200010a57808492604094855283398101031262000106576200005a602062000052836200011e565b92016200011e565b6001600160a01b039091166080523360a05260c052600e805460ff191690556040516140379081620001348239608051818181610f01015281816122440152612a79015260a0518181816102fc0152818161066b015281816108a1015281816108e5015281816109c601528181610a5e015281816115ea0152818161166201528181611a6301528181611c29015281816120cb0152613a25015260c051818181611015015261236f0152f35b5f80fd5b634e487b7160e01b5f52604160045260245ffd5b51906001600160a01b0382168203620001065756fe608060409080825260049182361015610022575b5050503615610020575f80fd5b005b5f915f3560e01c908163041d443e14612be15750806306fdde0314612bc457806311aa09b114612ba7578063134000c014612b1357806318a7ea5f14612af55780631d821a7a14612ad25780631fe543e314612a4657806324b570a914612a285780632f12313f14612a0a5780633013ce29146129e257806336fa0bcd146129be57806349a046c1146129855780634e108c191461295557806351d2cc8f1461291c578063526022c014612895578063537db1da146123c1578063558ba2131461239e578063570ca7351461235b57806357939805146120ae57806357e5e5b414611be55780635bc5980c14611b295780635d6f2f7914611b0b5780635f09d84c14611a2657806360246c881461198357806360751ea01461193f5780636ec773f61461190457806373c77690146118c95780637902019414611637578063806b456d146115a65780638070bb281461158857806381c426e51461156a57806389f915f6146114f25780638a6cd886146114cf57806396871042146112a55780639c82a93814610fe35780639e19b50614610f7f578063a03e4bc314610f58578063a0c1f15e14610f30578063a3e56fa814610eed578063a88d272414610eb2578063ac62c29a14610e70578063afa3a4bb14610a1b578063b0175cc3146109fd578063b8aa0537146109b1578063b947d44a1461080c578063bafa5e8c14610993578063bd3a4aac14610956578063c19d93fb1461092e578063c1e4996b146108d0578063c45a01551461088d578063c5e10eef14610865578063c69a51a414610847578063d338edf31461080c578063dd4ee59c146107ee578063e3e664cd146106f5578063e507a8a41461063c578063e6a44a891461060a578063ea4365c1146105cc578063ed647d21146105ae578063faff660e1461058e5763ff25e86203610013573461058a57606036600319011261058a5782359060ff90818316809303610586576102f2612f95565b6102fa612fa5565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316330361057657600e91848354169060078210159081610563576002831061053f5787158080610531575b610521576001891491828481610510575b506104bb5760028a1493848181610500575b506104f05760038b14958691826104cb575b50506104bb578b8a10156104a85794879485948a98948e948e9960018f5f80516020613fe28339815191529f9060609f918e9251946103c186612c5a565b169e8f855261040a81602087019b169d8e8c5286019185835260606103f3829189019588875260808a01978852612fc3565b9751169b60ff199c8d895416178855511686613293565b518454915163ffff00001990921690151560101b62ff0000161790151560181b63ff00000016178355519101551561046457505050600391508254161790555b61045387613609565b81519384526020840152820152a280f35b1561047657505082541617905561044a565b919250901561048d5750815416600517905561044a565b610499575b505061044a565b81541660061790558880610492565b634e487b7160e01b8b5260218c5260248bfd5b875163529c679b60e01b81528c90fd5b9091506104dd57600514155f80610383565b634e487b7160e01b8c5260218d5260248cfd5b885163529c679b60e01b81528d90fd5b90506104dd578c86141581610371565b90506104a85760038514158461035f565b865163529c679b60e01b81528b90fd5b505f9250600284141561034e565b8551633bf2e2f960e11b815260449061055a818d0186612f4a565b60026024820152fd5b634e487b7160e01b895260218a52602489fd5b8251630636a15760e11b81528790fd5b5f80fd5b5080fd5b503461058a578160031936011261058a57602090600f5415159051908152f35b503461058a578160031936011261058a57602090600c549051908152f35b5090346106075760203660031901126106075782359283101561060757506105f661060392613367565b929091519283928361301f565b0390f35b80fd5b509034610607576020366003190112610607575061062e610629612f34565b6132fd565b825191825215156020820152f35b5082346106f157826003193601126106f15781516303e1469160e61b81526001600160a01b03916020908290817f000000000000000000000000000000000000000000000000000000000000000086165afa9081156106e4576106aa935084916106b5575b5016331461317a565b6106b2613a03565b80f35b6106d7915060203d6020116106dd575b6106cf8183612ce3565b81019061315b565b846106a1565b503d6106c5565b50505051903d90823e3d90fd5b8280fd5b509034610607578060031936011261060757600354815490926001600160a01b03918216151592918290819085806107e1575b610747575b505060809550815194855260208501528301526060820152f35b90935060209150600254169560248351809881936370a0823160e01b835230908301525afa9182156107d65780926107a1575b6080955084838181111561079a5761079292506130a8565b915f8061072d565b5050610792565b91506020853d6020116107ce575b816107bc60209383612ce3565b8101031261058657608094519161077a565b3d91506107af565b9051903d90823e3d90fd5b5080600254161515610728565b503461058a578160031936011261058a576020906005549051908152f35b503461058a5760ff61083c6020938361082436612ff0565b6001600160a01b039091168352607d87529120612fda565b541690519015158152f35b503461058a578160031936011261058a57602090607e549051908152f35b503461058a578160031936011261058a5760015490516001600160a01b039091168152602090f35b503461058a578160031936011261058a57517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b508290346106f157806003193601126106f1577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03163303610921575035600c55602435600d5580f35b51630636a15760e11b8152fd5b503461058a578160031936011261058a5760209061095460ff600e541691518092612f4a565bf35b503461058a57602036600319011261058a5760209160ff9082906001600160a01b03610980612f34565b1681526081855220541690519015158152f35b503461058a578160031936011261058a576020906078549051908152f35b508290346106f157826003193601126106f1577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316330361092157826106b2613a03565b503461058a578160031936011261058a57602090600b549051908152f35b50913461058a5761028090816003193601126106f15761014493368511610e6c573661028411610e6c5780516303e1469160e61b81526020906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000081169183818781865afa908115610e62578891610e45575b50163314908115610e3b575b5015610e0257600e549560ff92838816976007891015610def576001809903610db65784607a541615610d745787808a5b610cb3575b5050888889915b610bed575b5050858890895b8b87600a8310610bd25750505050607455828890895b8b87600a8310610bb157505050509060029160755560ff19908a8260765416176076551617600e5551938488845f925b600a8410610b935750505050506101408401905f915b600a8310610b7657877fadae328c77fa94227f7d61b017c8da7cd84a3e69885efae46b879b9bae3df9b78888a180f35b83808a9287610b8486612fb5565b16815201920192019190610b46565b819088610b9f87612fb5565b16815201930191019091848a91610b30565b90919293610bc99084610bc3876130e4565b91613147565b93019101610b00565b90919293610be49084610bc3876130e4565b93019101610aea565b868216600a80821015610cac5788610c0c610c07846132bb565b6130e4565b161015610c715787610c21610c078e936132bb565b161b90818116610c3857918b01871691178a610ade565b845162461bcd60e51b8152808a018890526013602482015272111d5c1b1a58d85d194818dbdb08191a59da5d606a1b6044820152606490fd5b845162461bcd60e51b8152808a018890526015602482015274436f6c206469676974206d75737420626520302d3960581b6044820152606490fd5b5050610ae3565b868216600a80821015610d6d5788610ccd610c07846132a9565b161015610d325787610ce2610c078e936132a9565b161b90818116610cf957918b01871691178a610ad2565b845162461bcd60e51b8152808a018890526013602482015272111d5c1b1a58d85d19481c9bddc8191a59da5d606a1b6044820152606490fd5b845162461bcd60e51b8152808a018890526015602482015274526f77206469676974206d75737420626520302d3960581b6044820152606490fd5b5050610ad7565b815162461bcd60e51b8152808701859052601c60248201527b159491881b5d5cdd081a185d99481899595b881c995c5d595cdd195960221b6044820152606490fd5b815162461bcd60e51b81528087018590526013602482015272141bdbdb081b5d5cdd0818994810d313d4d151606a1b6044820152606490fd5b634e487b7160e01b885260218652602488fd5b905162461bcd60e51b81529182015260156024820152744f6e6c792061646d696e206f7220666163746f727960581b6044820152606490fd5b905033145f610aa1565b610e5c9150843d86116106dd576106cf8183612ce3565b5f610a95565b85513d8a823e3d90fd5b8380fd5b503461058a578160031936011261058a57608090600b549060ff607a5416906079549060ff607654169281519485521515602085015283015215156060820152f35b50913461058a57602036600319011261058a573590600a821015610607575060ff60209260f88360051c6074015491519360031b161c168152f35b503461058a578160031936011261058a57517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b503461058a578160031936011261058a5760025490516001600160a01b039091168152602090f35b503461058a578160031936011261058a57905490516001600160a01b039091168152602090f35b509034610607576020366003190112610607578235928310156106075750610fa860a092612fc3565b60ff60018254920154918351938282168552828260081c166020860152828260101c1615159085015260181c16151560608301526080820152f35b5082346106f15760603660031901126106f15780359181831015610e6c57611009612f95565b91611012612fa5565b907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03163303611297578415808061126b575b611249576001861490818061121d575b6111f257600287149586806111da575b6111af576003881480611183575b611158578360ff61108a8a612fc3565b92169760ff199289848254161781556110a38882613293565b805463ffff0000191663010100001790555f93156111145750506003919250600e541617600e555b6110d486613609565b611101575091848260ff5f80516020613fe28339815191529560609551948552166020840152820152a280f35b634e487b7160e01b865260219052602485fd5b9092505f935f1461112d5750600e541617600e556110cb565b5f93509091501561114857600590600e541617600e556110cb565b600690600e541617600e556110cb565b600e548651633bf2e2f960e11b815260449161117a908288019060ff16612f4a565b60056024820152fd5b5060ff600e5416600781101561119c576005141561107a565b634e487b7160e01b8a526021855260248afd5b600e548651633bf2e2f960e11b815260449186906111d3908383019060ff16612f4a565b6024820152fd5b5060ff600e5416600781101561119c5784141561106c565b600e548551633bf2e2f960e11b8152604491611214908287019060ff16612f4a565b60036024820152fd5b5060ff600e54166007811015611236576003141561105c565b634e487b7160e01b895260218452602489fd5b600e548451633bf2e2f960e11b815260449161055a908286019060ff16612f4a565b5060ff600e54166007811015611284576002141561104c565b634e487b7160e01b885260218352602488fd5b82516327e1f1e560e01b8152fd5b5082346106f157602080600319360112610e6c57813592828410156114cb5760ff8085168015806114b4575b61147757600181148061149e575b611477576002811480611487575b611477576003148061144d575b61143d57338652607d835261131185838820612fda565b541661142e5761132084613367565b93906001600160a01b03908116330361141f57338752607d845261134686848920612fda565b805460ff19166001179055600654168061139b57508580808087335af161136b6130b5565b501561138d5750905f80516020613fc2833981519152915b519283523392a380f35b90516312171d8360e31b8152fd5b838351809263a9059cbb60e01b8252818a816113ba8b338a840161301f565b03925af19081156114155787916113e8575b501561138d5750905f80516020613fc283398151915291611383565b6114089150843d861161140e575b6114008183612ce3565b810190613090565b876113cc565b503d6113f6565b83513d89823e3d90fd5b5090516330c6392160e11b8152fd5b5163538d585960e11b81529050fd5b8151631d6c64bd60e21b81528490fd5b5080600e54166007811015611464576006116112fa565b634e487b7160e01b875260218552602487fd5b8251631d6c64bd60e21b81528590fd5b5081600e54166007811015610def576005116112ed565b5081600e54166007811015610def5785116112df565b5081600e54166007811015610def576003116112d1565b8480fd5b503461058a578160031936011261058a5760209060ff607a541690519015158152f35b503461058a578160031936011261058a578061095461028092519161151683612cc7565b610140809336903782815161152a81612cc7565b369037805192611539846131b3565b61154284612cc7565b61156282519261155184613223565b61155a84612cc7565b518095612f6b565b830190612f6b565b503461058a578160031936011261058a57602090607f549051908152f35b503461058a578160031936011261058a576020906003549051908152f35b5082346106f15760603660031901126106f1576115c1612f34565b906001600160a01b036024358181169290839003610586576044359482861680960361058657827f000000000000000000000000000000000000000000000000000000000000000016330361092157505060018060a01b0319921682855416178455816001541617600155600254161760025580f35b508290346106f157806003193601126106f157611652612f34565b602491823560018060a01b0392837f0000000000000000000000000000000000000000000000000000000000000000168351946303e1469160e61b9283875260209687818b81875afa9081156118bf576116b79184918d916118a2575016331461317a565b60ff600e541660078110156118905760060361185a5716918451936370a0823160e01b8552308986015286858981875afa948515611850578a95611821575b505f19810361181b5750835b84116117e25785908886518094819382525afa9081156117d85791611747939186938a916117bb575b5089865180968195829463a9059cbb60e01b84528d840161301f565b03925af19081156117b1578691611794575b5015611763578480f35b5162461bcd60e51b815292830152600f908201526e151c985b9cd9995c8819985a5b1959608a1b6044820152606490fd5b6117ab9150833d851161140e576114008183612ce3565b86611759565b82513d88823e3d90fd5b6117d29150843d86116106dd576106cf8183612ce3565b8a61172b565b84513d8a823e3d90fd5b845162461bcd60e51b815280890187905260148189015273496e73756666696369656e742062616c616e636560601b6044820152606490fd5b93611702565b9094508681813d8311611849575b6118398183612ce3565b810103126105865751938a6116f6565b503d61182f565b86513d8c823e3d90fd5b855162461bcd60e51b8152808a018890526011818a01527011d85b59481b9bdd08199a5b9a5cda1959607a1b6044820152606490fd5b634e487b7160e01b8b5260218a52888bfd5b6118b991508a3d8c116106dd576106cf8183612ce3565b8d6106a1565b87513d8d823e3d90fd5b503461058a57602036600319011261058a5760209160ff9082906001600160a01b036118f3612f34565b168152607c85522054169051908152f35b50913461058a57602036600319011261058a573590600a821015610607575060ff60209260f88360051c6075015491519360031b161c168152f35b503461058a578160031936011261058a57606090607e5490607f5460ff608054169081611979575b82519384526020840152151590820152f35b8015159150611967565b503461058a578160031936011261058a576119ee9061060360ff600e541691611a186005549360018060a01b036006541694607754607854906119c4612d06565b976119cd612e1f565b936119d6612da4565b976119f981519c8d9c8d610100908181520190612e9a565b9760208d0190612f4a565b8a01526060890152608088015260a087015285820360c0870152612e9a565b9083820360e0850152612e9a565b5082346106f15760203660031901126106f157611a41612f34565b82516303e1469160e61b815290926001600160a01b03929091906020908290817f000000000000000000000000000000000000000000000000000000000000000087165afa918215611b025750611aa39183918691611ae3575016331461317a565b600280546001600160a01b03198116938316938417909155167f53fab91b0553fa390c7df2daf18c9f2f21878be4f7669c66f13569b32fb1ae2d8380a380f35b611afc915060203d6020116106dd576106cf8183612ce3565b866106a1565b513d86823e3d90fd5b503461058a578160031936011261058a57602090600a549051908152f35b5082346106f15760203660031901126106f1578035908110156106f157611b759060a09360808451611b5a81612c5a565b82815282602082015282868201528260608201520152612fc3565b90805190611b8282612c5a565b82549060ff82169384845260ff60208501818560081c1681526080600185880194848860101c16151586528460608a019860181c161515885201549601958652835196875251166020860152511515908401525115156060830152516080820152f35b50913461058a576020906003198281360112610e6c578135926001600160401b03918285116120aa576101a08585019186360301126120aa576001600160a01b03967f00000000000000000000000000000000000000000000000000000000000000008816330361209b5760848601815191611c6083612c91565b82610104890193368511612097578684915b86831061207f575061ffff91508260ff6060611cac611ca0611cb695848060649a51169187015116906135f3565b838986015116906135f3565b92015116906135f3565b16036120705750611cc783806130ff565b86819b929b1161205d57808a9b89611ce2819c9d9b54612bfb565b89601f9c8d8311612031575b92505050908d8b8411600114611fcd5792611fc2575b50508160011b915f199060031b1c19161788555b60248901356005556044890135908116809103611fbe576006549060ff60a01b611d4460648c016130e4565b60a01b169160018060a81b03191617176006558890895b85898210611fa357505050600755611d7390826130ff565b90848211611f9057611d86600854612bfb565b868111611f63575b508890868311600114611efb57611dc99392918a9183611ef0575b50508160011b915f199060031b1c1916176008555b6101248701906130ff565b9490928511611edd5750611dde600954612bfb565b838111611ea6575b5085928411600114611e3b5750610184939291859183611e30575b50508160011b915f199060031b1c1916176009555b610144810135600a55610164810135600b550135600f5580f35b013590505f80611e01565b91601f198416600987528387209387905b828210611e8e57505091600193918561018497969410611e75575b505050811b01600955611e16565b01355f19600384901b60f8161c191690555f8080611e67565b80600185978294968801358155019601930190611e4c565b611ece90600988528288208580880160051c820192858910611ed4575b0160051c0190613131565b5f611de6565b92508192611ec3565b634e487b7160e01b875260419052602486fd5b013590505f80611da9565b60088a52848a2091601f1984168b5b87828210611f4d575050916001939185611dc997969410611f34575b505050811b01600855611dbe565b01355f19600384901b60f8161c191690555f8080611f26565b6001849682939587013581550195019201611f0a565b611f8a9060088b52858b208880860160051c820192888710611ed4570160051c0190613131565b5f611d8e565b634e487b7160e01b895260418752602489fd5b611fb5839483610bc3600195966130e4565b93019101611d5b565b8980fd5b013590505f80611d04565b925090601f1984168c8452898420935b8a82821061201b575050908460019594939210612002575b505050811b018855611d18565b01355f19600384901b60f8161c191690555f8080611ff5565b6001849682939587013581550195019201611fdd565b612054938152208c80860160051c8201928c8710611ed4570160051c0190613131565b8a5f898f611cee565b634e487b7160e01b8a526041885260248afd5b51632bfdf23b60e11b81528690fd5b819061208a84612fb5565b8152019101908790611c72565b8a80fd5b51630636a15760e11b81528490fd5b8580fd5b5082346106f157826003193601126106f1576001600160a01b03907f00000000000000000000000000000000000000000000000000000000000000008216330361234d57600e549260ff8416600781101561233a57806123165750607854156123095760ff607a54166122fc57600160ff1980951617600e558051924284527f925a19753e677c9dc36a80e0fc824ca0c5b1afde494872b43daccab9ffeaffd460208095a1600d54600c54835190946001600160401b039392909190878201858111838210176122e95786526001825285519163125fa26760e31b898401525115156024830152602482526121a282612cac565b85519460c08601908111868210176122e9579261224089969594938c9388958a528752848701998a5288870160038152606088016207a120815261ffff60808a01926001845260a08b019485528c519d8e9b8c9a8b99634d8e1c2f60e11b8b528a015251602489015251604488015251166064860152519063ffffffff8092166084860152511660a48401525160c060c484015260e4830190612e9a565b03927f0000000000000000000000000000000000000000000000000000000000000000165af19182156122df5785926122ad575b5060017fd28591fd5b8e6c8a0d976474c82e138053b4b2004d8d12bab9c3e5383ad9706b9483607955607a541617607a5551908152a180f35b9391508284813d83116122d8575b6122c58183612ce3565b8101031261058657925190926001612274565b503d6122bb565b81513d87823e3d90fd5b604184634e487b7160e01b5f525260245ffd5b5163029c583d60e01b8152fd5b516387dc625760e01b8152fd5b60449350612332915192633bf2e2f960e11b8452830190612f4a565b5f6024820152fd5b634e487b7160e01b865260218352602486fd5b8251630636a15760e11b8152fd5b503461058a578160031936011261058a57517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b503461058a578160031936011261058a5760209060ff6076541690519015158152f35b508281600319360112610586576001600160401b03908035828111610586576123ed9036908301612ed8565b936024928335948086116105865736602387011215610586578582013590811161058657368582880101116105865760ff9586600e5416600781101561288357806128605750600a54421161285057600f549182612809575b50505060059061245887600554613055565b95335f52602095607c875261247582865f205416838b169061307c565b8260065460a01c16801515806127fe575b6127cf5750335f52607c885282865f20911660ff1982541617905560018060a01b039182600654168881155f1461276957505088341061274d5788341161271f575b825f5416806125b3575b508a5b8a81106124fe578b6124f88c6124ed8d6077546130f2565b6077556078546130f2565b60785580f35b61250b81871b89016130e4565b8281169060648083101561259d5781101561258b576010018054868116612575576001600160a01b03191633908117909155875489519081526001939291907fd55d45dbc537b82471506213de5e3d6eb436a9048098dc7378087f719b128b9d908d90a3016124d5565b8951630d1a490360e31b81528089018490528690fd5b634e487b7160e01b8e5260328752848efd5b8951634e6153af60e11b81528089018490528690fd5b888a856006541680155f1461266857505050508a836001541684825416813b156106f1578b91606484928b51948593849263474cf53d60e01b84528c840152308a8401528560448401525af1801561265e5761264a575b50505b612619896003546130f2565b6003557fcfd990c8ea9e3bd9e38863886d1bef1edfff9ef9e09b19806edcae848db500c88887518b8152a18b6124d2565b61265390612c33565b612097578a8c61260a565b88513d84823e3d90fd5b612687935f8b5180968195829463095ea7b360e01b84528d840161301f565b03925af180156126f857612702575b50825f54168360065416813b15610586578a60845f92838b51958694859363617ba03760e01b85528c850152898401523060448401528160648401525af180156126f8576126e5575b5061260d565b6126f0919b50612c33565b5f998b6126df565b87513d5f823e3d90fd5b61271890893d8b1161140e576114008183612ce3565b508b612696565b5f80808061272d8d346130a8565b335af16127386130b5565b506124c85785516312171d8360e31b81528490fd5b855163b99e2ab760e01b815234818601528083018a9052604490fd5b5f9160648c8a5194859384926323b872dd60e01b8452338c850152308a85015260448401525af19081156126f8575f916127b257506124c85785516312171d8360e31b81528490fd5b6127c99150893d8b1161140e576114008183612ce3565b8c612738565b84606491888587607c8e335f5252825f205416915193639d69e24960e01b855233908501528301526044820152fd5b508084831611612486565b5f60206128158361303a565b9261282288519485612ce3565b808452808a83860196018637830101525190200361284257878080612446565b90516303e4e47560e21b8152fd5b835163bec2907f60e01b81528390fd5b835f8861287e604494895194633bf2e2f960e11b8652850190612f4a565b820152fd5b86602185634e487b7160e01b5f52525ffd5b508234610586575f36600319011261058657608082516128b481612c91565b36903781516007549060ff9360ff8316825260ff602093818160081c166020850152818160101c168385015260181c1660608301526128f282612c91565b51935f9190855b85841061290557608087f35b8480600192848651168152019301930192916128f9565b50823461058657602036600319011261058657359060648210156105865760109091015490516001600160a01b03919091168152602090f35b5034610586575f3660031901126105865761060390612972612e1f565b9051918291602083526020830190612e9a565b838234610586576020366003190112610586578135918210156105865760ff6129af602093612f08565b92905490519260031b1c168152f35b5034610586575f3660031901126105865760209060ff60065460a01c169051908152f35b5034610586575f3660031901126105865760065490516001600160a01b039091168152602090f35b5034610586575f36600319011261058657602090600f549051908152f35b5034610586575f366003190112610586576020906077549051908152f35b5082346105865781600319360112610586576024356001600160401b03811161058657612a769036908301612ed8565b917f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031633819003612ab557506100209350356134be565b6044925084519163073e64fd60e21b835233908301526024820152fd5b5034610586575f3660031901126105865760209060ff6080541690519015158152f35b5034610586575f366003190112610586576020906079549051908152f35b5034610586575f36600319011261058657805190612b3082612c75565b610c80809236903780519060105f835b60648210612b8757505050612b5482612c75565b51905f90825b60648310612b6757505050f35b81516001600160a01b031681526001929092019160209182019101612b5a565b82546001600160a01b031681526001928301929190910190602001612b40565b5034610586575f3660031901126105865761060390612972612da4565b5034610586575f3660031901126105865761060390612972612d06565b34610586575f36600319011261058657602090600d548152f35b90600182811c92168015612c29575b6020831014612c1557565b634e487b7160e01b5f52602260045260245ffd5b91607f1691612c0a565b6001600160401b038111612c4657604052565b634e487b7160e01b5f52604160045260245ffd5b60a081019081106001600160401b03821117612c4657604052565b610c8081019081106001600160401b03821117612c4657604052565b608081019081106001600160401b03821117612c4657604052565b606081019081106001600160401b03821117612c4657604052565b61014081019081106001600160401b03821117612c4657604052565b601f909101601f19168101906001600160401b03821190821017612c4657604052565b604051905f8260045491612d1983612bfb565b808352602093600190818116908115612d845750600114612d45575b5050612d4392500383612ce3565b565b9093915060045f52815f20935f915b818310612d6c575050612d4393508201015f80612d35565b85548884018501529485019487945091830191612d54565b915050612d4394925060ff191682840152151560051b8201015f80612d35565b604051905f8260095491612db783612bfb565b808352602093600190818116908115612d845750600114612de0575050612d4392500383612ce3565b9093915060095f52815f20935f915b818310612e07575050612d4393508201015f80612d35565b85548884018501529485019487945091830191612def565b604051905f8260085491612e3283612bfb565b808352602093600190818116908115612d845750600114612e5b575050612d4392500383612ce3565b9093915060085f52815f20935f915b818310612e82575050612d4393508201015f80612d35565b85548884018501529485019487945091830191612e6a565b91908251928382525f5b848110612ec4575050825f602080949584010152601f8019910116010190565b602081830181015184830182015201612ea4565b9181601f84011215610586578235916001600160401b038311610586576020808501948460051b01011161058657565b906004821015612f2057601f8260051c600701921690565b634e487b7160e01b5f52603260045260245ffd5b600435906001600160a01b038216820361058657565b906007821015612f575752565b634e487b7160e01b5f52602160045260245ffd5b5f915b600a8310612f7b57505050565b60019060ff83511681526020809101920192019190612f6e565b6024359060ff8216820361058657565b6044359060ff8216820361058657565b359060ff8216820361058657565b6004811015612f57575f52607b60205260405f2090565b906004811015612f57575f5260205260405f2090565b6040906003190112610586576004356001600160a01b0381168103610586579060243560048110156105865790565b6001600160a01b039091168152602081019190915260400190565b6001600160401b038111612c4657601f01601f191660200190565b8181029291811591840414171561306857565b634e487b7160e01b5f52601160045260245ffd5b9060ff8091169116019060ff821161306857565b90816020910312610586575180151581036105865790565b9190820391821161306857565b3d156130df573d906130c68261303a565b916130d46040519384612ce3565b82523d5f602084013e565b606090565b3560ff811681036105865790565b9190820180921161306857565b903590601e198136030182121561058657018035906001600160401b0382116105865760200191813603831361058657565b81811061313c575050565b5f8155600101613131565b9060ff809160031b9316831b921b19161790565b9081602091031261058657516001600160a01b03811681036105865790565b1561318157565b60405162461bcd60e51b815260206004820152600a60248201526927b7363c9030b236b4b760b11b6044820152606490fd5b61012060745460ff908181168452818160081c166020850152818160101c166040850152818160181c166060850152818160201c166080850152818160281c1660a0850152818160301c1660c0850152818160381c1660e0850152818160401c1661010085015260481c16910152565b61012060755460ff908181168452818160081c166020850152818160101c166040850152818160181c166060850152818160201c166080850152818160281c1660a0850152818160301c1660c0850152818160381c1660e0850152818160401c1661010085015260481c16910152565b9061ff0082549160081b169061ff001916179055565b600a811015612f205760051b60040190565b600a811015612f205760051b6101440190565b90600a811015612f205760051b0190565b81156132e9570490565b634e487b7160e01b5f52601260045260245ffd5b60018060a01b03165f52608160205260ff60405f20541660ff6080541615801561335d575b61335957607c60205260ff60405f20541680156133545761334861335191607f54613055565b607854906132df565b91565b505f91565b5f91565b50607f5415613322565b9061337182612fc3565b549060ff91828160181c16156134b5576040519161338e836131b3565b61339783612cc7565b6040516133a381613223565b6133ac81612cc7565b600a908386168290068616865f805b828116868110156134a657836133d286928c6132ce565b5116146133e35760010182166133bb565b939495969750505050919390935b5f9486600a815f9560081c160616925b8781168381101561349457886134188692856132ce565b511614613429576001018716613401565b94969550600a938693509150505b1602908282169182036130685761344d9161307c565b6064811015612f2057601001546077546001600160a01b0390911693906004821015612f575760649261348261349093612f08565b90549060031b1c1690613055565b0490565b505050505083600a9194939294613437565b505094959392965050506133f1565b505f9250829150565b929192607954036135ee57600e549060ff9060ff8316946007861015612f575760018096036135e65715612f20573593906134f885613cab565b5f905f5b600a81106135c45750506074556135366040519560209660208101918252600160408201526040815261352e81612cac565b519020613cab565b905f955f5b600a81106135a35750505050506002919260755560ff199060018260765416176076551617600e557fb48ed8f2b67f7a4cf1d83936fb4e55a40b7d5bb2b1fb09fe69e91f29a231aa85610280604051613593816131b3565b6135a06101408201613223565ba1565b90919293826135b88299848789511691613147565b9895019392910161353b565b90919360206135da829996848787511691613147565b930191019693966134fc565b505050509050565b509050565b91909161ffff8080941691160191821161306857565b61361281613367565b90607e805461362181856130f2565b6001600160a01b039184831690811515806139d7575b156139795750805f52607d60205260409161365488845f20612fda565b600160ff198254161790555f855580845f5416613969575b50806138c0575b6004881015612f575760205f80516020613fc283398151915291899451908152a35b6004906004861015612f575760038614806138b6575b6136ea575b505050907f4c68367ddbd34d3d0144e5b73c2842cda488332a1acacf11debff28fd03c346b916136e56040519283928361301f565b0390a2565b82549283607f556001935f60ff199260018460805416176080555580835f54166138a6575b50607892607854916040927fd24fb2570481d3406d54ce976d298a890625e4fb3a3451648580154b4294b53b84805184815260209384820152a181613759575b50505050506136b0565b5f5b60648110613769575061374f565b80848a92601001541680151580613891575b613787575b500161375b565b805f52607c845260ff875f205416876137ab6137a38389613055565b8c54906132df565b60065487908a168e81613821575050505f80808084885af16137cb6130b5565b505b6137da575b505050613780565b7f057130b033aff37ce94779177f5a0aba5e46cc901337064f7734906ff0afab6f92845f5260818852825f20878d825416179055825191825287820152a25f8087816137d2565b86979850955f84969761384a96959495519687958694859363a9059cbb60e01b8552840161301f565b03925af1908115613887579189918e969594935f9161386a575b506137cd565b6138819150883d8a1161140e576114008183612ce3565b5f613864565b89513d5f823e3d90fd5b50805f526081845260ff875f2054161561377b565b6138b09150613d70565b5f61370f565b50825415156136ab565b80846006541680155f146138f557505f808080938a5af16138df6130b5565b506136735782516312171d8360e31b8152600490fd5b61391991602091895f885180968195829463a9059cbb60e01b84526004840161301f565b03925af190811561395f575f9161394057506136735782516312171d8360e31b8152600490fd5b613959915060203d60201161140e576114008183612ce3565b5f6138df565b84513d5f823e3d90fd5b6139739150613d70565b5f61366c565b915050848061398a575b5050613695565b613993916130f2565b8083556004861015612f57577f60fc94fd6b7dad2e0cb4f5562c81be8ce4874e7806a06b53bae21e2a0a21471c604087928151908882526020820152a25f84613983565b50821515613637565b6001600160a01b0391821681526020810192909252909116604082015260600190565b604080516303e1469160e61b8152909160206001600160a01b036004828582817f000000000000000000000000000000000000000000000000000000000000000086165afa948515613ca1575f95613c82575b5060ff600e54166007811015613c6f57600603613c6157815f5416958615613c545760249495968484600254168351978880926370a0823160e01b825230888301525afa958615613b8d575f96613c25575b508515613c1757600654841680613b97575050613ae58486856002541686600154165f865180968195829463095ea7b360e01b84528b840161301f565b03925af18015613b8d57613b70575b50826001541691835f541690833b156105865788935f92838993613b2b87519d8e9687958694630402806960e51b865285016139e0565b03925af1968715613b66575f80516020613fa2833981519152959697613b57575b505b519485521692a2565b613b6090612c33565b5f613b4c565b50513d5f823e3d90fd5b613b8690853d871161140e576114008183612ce3565b505f613af4565b82513d5f823e3d90fd5b95859183975f8b96613bbf859c9d975197889687958694631a4ca37b60e21b865285016139e0565b03925af18015613c0d57613be3575b505f80516020613fa283398151915293613b4e565b8390813d8311613c06575b613bf88183612ce3565b81010312610586575f613bce565b503d613bee565b85513d5f823e3d90fd5b50516325b6fdd160e21b8152fd5b9095508481813d8311613c4d575b613c3d8183612ce3565b810103126105865751945f613aa8565b503d613c33565b51631d3b721560e11b8152fd5b855163b7c3a5b360e01b8152fd5b602182634e487b7160e01b5f525260245ffd5b613c9a919550833d85116106dd576106cf8183612ce3565b935f613a56565b86513d5f823e3d90fd5b60409160405190613cbb82612cc7565b6101403683375f5b60ff8116600a811015613ce85760ff9181613ce0600193876132ce565b520116613cc3565b50509290916009805b613cfb5750505090565b825160208101908382528285820152848152613d1681612cac565b519020600182018083116130685780156132e957613d609060ff809181613d3d878b6132ce565b511694061690613d4d82896132ce565b5116613d5985896132ce565b52866132ce565b528015613068575f190180613cf1565b600254604080516370a0823160e01b81523060048201526001600160a01b03945f94909360209392908490829060249082908b165afa908115613ebe575f91613f54575b5080821015613f4c5750935b8415613f4257600654861680613ec85750613dfc8386886002541689600154165f875180968195829463095ea7b360e01b84526004840161301f565b03925af18015613ebe57613ea1575b506001545f5487169616803b1561058657613e425f979188928551998a80948193630402806960e51b83528c3091600485016139e0565b03925af1958615613b8d575f80516020613f82833981519152949596613e90575b505b6003548690808210613e7e5750506003555b51848152a1565b613e8892506130a8565b600355613e77565b613e9a9150612c33565b5f80613e63565b613eb790843d861161140e576114008183612ce3565b505f613e0b565b83513d5f823e3d90fd5b613ef484915f93969798845416908551948580948193631a4ca37b60e21b83528d3091600485016139e0565b03925af18015613b8d57613f18575b505f80516020613f8283398151915292613e65565b8290813d8311613f3b575b613f2d8183612ce3565b81010312610586575f613f03565b503d613f23565b5050509150505f90565b905093613dc0565b90508381813d8311613f7a575b613f6b8183612ce3565b8101031261058657515f613db4565b503d613f6156fe60a52561bffd64573086f5280c1c9cd1bc6ab49cc478f8ccfe689dacec8677eeaed495d70193dd915b96156dc90595c95aa5cc4f6985a2cd79286232d96ae8e264c7f0ad3eacf9f60d2a7e70eca5b5a740b09c3132541056bf6ca27abb6f516e08a8313d2d7c3eae8e87faafbdf295a58197eb2e3767d54d2ae0cce7c3a48292a26469706673582212205df68c8c12fe9a2467be773dad0051a2c57e46708aa193c4f83eb4ec56f23e5564736f6c63430008180033bfe2b363f53fad720f333b704f83a37d87c0db5ab9059f3f446d13d1c1918429a2646970667358221220d0a1b62510f468f6381c50b2bef1327ba3a663a098fd0967c4e041e7509bfd4a64736f6c63430008180033

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading
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.