ETH Price: $2,643.59 (+0.08%)

Contract

0xC15568330926E2A6f1519992b0364ca00faf6A7A
 
Transaction Hash
Method
Block
From
To
Emergency Withdr...141671302025-01-17 20:01:1522 days ago1737144075IN
0xC1556833...00faf6A7A
0 ETH00.00006758
Nullify Battle141630092025-01-17 17:43:5322 days ago1737135833IN
0xC1556833...00faf6A7A
0 ETH00.00000056
Nullify Battle141620342025-01-17 17:11:2322 days ago1737133883IN
0xC1556833...00faf6A7A
0 ETH00.00000032
Nullify Battle141619622025-01-17 17:08:5922 days ago1737133739IN
0xC1556833...00faf6A7A
0 ETH00.00000032
Nullify Battle141619232025-01-17 17:07:4122 days ago1737133661IN
0xC1556833...00faf6A7A
0 ETH00.00000031
Nullify Battle141619182025-01-17 17:07:3122 days ago1737133651IN
0xC1556833...00faf6A7A
0 ETH00.00000032
Nullify Battle141606492025-01-17 16:25:1322 days ago1737131113IN
0xC1556833...00faf6A7A
0 ETH00.00000034
Claim Proceeds141605252025-01-17 16:21:0522 days ago1737130865IN
0xC1556833...00faf6A7A
0.0006 ETH0.000000130.00110033
Nullify Battle141602592025-01-17 16:12:1322 days ago1737130333IN
0xC1556833...00faf6A7A
0 ETH00.00000027
Nullify Battle141602432025-01-17 16:11:4122 days ago1737130301IN
0xC1556833...00faf6A7A
0 ETH00.00000027
Claim Proceeds141601962025-01-17 16:10:0722 days ago1737130207IN
0xC1556833...00faf6A7A
0.00012 ETH00.00000027
Claim Proceeds141601412025-01-17 16:08:1722 days ago1737130097IN
0xC1556833...00faf6A7A
0.00006 ETH0.000000130.00110028
Nullify Battle141601322025-01-17 16:07:5922 days ago1737130079IN
0xC1556833...00faf6A7A
0 ETH0.000000080.00110028
Nullify Battle141601272025-01-17 16:07:4922 days ago1737130069IN
0xC1556833...00faf6A7A
0 ETH00.00000029
Nullify Battle141601262025-01-17 16:07:4722 days ago1737130067IN
0xC1556833...00faf6A7A
0 ETH0.000000080.00110029
Withdraw Stake141601182025-01-17 16:07:3122 days ago1737130051IN
0xC1556833...00faf6A7A
0 ETH0.000000130.00110029
Claim Proceeds141600002025-01-17 16:03:3522 days ago1737129815IN
0xC1556833...00faf6A7A
0.003 ETH0.000000120.00100029
Claim Proceeds141599922025-01-17 16:03:1922 days ago1737129799IN
0xC1556833...00faf6A7A
0.027 ETH00.00000029
Claim Proceeds141599782025-01-17 16:02:5122 days ago1737129771IN
0xC1556833...00faf6A7A
0.0006 ETH0.000000130.00110028
Claim Proceeds141599702025-01-17 16:02:3522 days ago1737129755IN
0xC1556833...00faf6A7A
0.00006 ETH0.000000130.00110028
Nullify Battle141599682025-01-17 16:02:3122 days ago1737129751IN
0xC1556833...00faf6A7A
0 ETH00.00000028
Claim Proceeds141599522025-01-17 16:01:5922 days ago1737129719IN
0xC1556833...00faf6A7A
0.0003 ETH0.000000120.00100028
Nullify Battle141599512025-01-17 16:01:5722 days ago1737129717IN
0xC1556833...00faf6A7A
0 ETH00.00000028
Join Battle141599442025-01-17 16:01:4322 days ago1737129703IN
0xC1556833...00faf6A7A
0.01 ETH0.000000270.00150027
Init Battle141599402025-01-17 16:01:3522 days ago1737129695IN
0xC1556833...00faf6A7A
0.01 ETH0.000000240.00110027
View all transactions

Latest 25 internal transactions (View All)

Parent Transaction Hash Block From To
141671302025-01-17 20:01:1522 days ago1737144075
0xC1556833...00faf6A7A
2.30148 ETH
141599442025-01-17 16:01:4322 days ago1737129703
0xC1556833...00faf6A7A
0.01 ETH
141599402025-01-17 16:01:3522 days ago1737129695
0xC1556833...00faf6A7A
0.01 ETH
141599342025-01-17 16:01:2322 days ago1737129683
0xC1556833...00faf6A7A
0.001 ETH
141599232025-01-17 16:01:0122 days ago1737129661
0xC1556833...00faf6A7A
0.005 ETH
141599182025-01-17 16:00:5122 days ago1737129651
0xC1556833...00faf6A7A
0.005 ETH
141599182025-01-17 16:00:5122 days ago1737129651
0xC1556833...00faf6A7A
0.001 ETH
141599052025-01-17 16:00:2522 days ago1737129625
0xC1556833...00faf6A7A
0.04 ETH
141598992025-01-17 16:00:1322 days ago1737129613
0xC1556833...00faf6A7A
0.04 ETH
141598982025-01-17 16:00:1122 days ago1737129611
0xC1556833...00faf6A7A
0.002 ETH
141598972025-01-17 16:00:0922 days ago1737129609
0xC1556833...00faf6A7A
0.03 ETH
141598942025-01-17 16:00:0322 days ago1737129603
0xC1556833...00faf6A7A
0.001 ETH
141598922025-01-17 15:59:5922 days ago1737129599
0xC1556833...00faf6A7A
0.03 ETH
141598912025-01-17 15:59:5722 days ago1737129597
0xC1556833...00faf6A7A
0.01 ETH
141598912025-01-17 15:59:5722 days ago1737129597
0xC1556833...00faf6A7A
0.001 ETH
141598872025-01-17 15:59:4922 days ago1737129589
0xC1556833...00faf6A7A
0.005 ETH
141598852025-01-17 15:59:4522 days ago1737129585
0xC1556833...00faf6A7A
0.01 ETH
141598832025-01-17 15:59:4122 days ago1737129581
0xC1556833...00faf6A7A
0.005 ETH
141598832025-01-17 15:59:4122 days ago1737129581
0xC1556833...00faf6A7A
0.01 ETH
141598822025-01-17 15:59:3922 days ago1737129579
0xC1556833...00faf6A7A
0.001 ETH
141598802025-01-17 15:59:3522 days ago1737129575
0xC1556833...00faf6A7A
0.01 ETH
141598782025-01-17 15:59:3122 days ago1737129571
0xC1556833...00faf6A7A
0.001 ETH
141598632025-01-17 15:59:0122 days ago1737129541
0xC1556833...00faf6A7A
0.002 ETH
141598612025-01-17 15:58:5722 days ago1737129537
0xC1556833...00faf6A7A
0.001 ETH
141598612025-01-17 15:58:5722 days ago1737129537
0xC1556833...00faf6A7A
0.1 ETH
View All Internal Transactions

Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
BlastDuelArenaBattle

Compiler Version
v0.8.18+commit.87f61d96

Optimization Enabled:
Yes with 10000 runs

Other Settings:
paris EvmVersion
File 1 of 22 : BlastDuelArenaBattle.sol
// SPDX-License-Identifier: UNLICENSED

/**
 * /***
 *
 *      ,- _~.                ,,
 *     (' /|     _            ||           '   _
 *    ((  ||    < \, \\/\\/\\ ||/|, ,._-_ \\  < \,
 *    ((  ||    /-|| || || || || ||  ||   ||  /-||
 *     ( / |   (( || || || || || |'  ||   || (( ||
 *      -____-  \/\\ \\ \\ \\ \\/    \\,  \\  \/\\
 *
 *                                     ___
 *    -_____                ,,        -   -_,
 *      ' | -,              ||       (  ~/||                      _
 *     /| |  |` \\ \\  _-_  ||       (  / ||  ,._-_  _-_  \\/\\  < \,
 *     || |==|| || || || \\ ||        \/==||   ||   || \\ || ||  /-||
 *    ~|| |  |, || || ||/   ||        /_ _||   ||   ||/   || || (( ||
 *     ~-____,  \\/\\ \\,/  \\       (  - \\,  \\,  \\,/  \\ \\  \/\\
 *    (
 *
 * @title Cambria Blast Duel Arena
 * @notice See cambria.gg for more details.
 * @notice Modified for Blast
 * @author will [at] cambria dot gg
 * @notice questions, concerns, vulns to security [at] cambria.gg
 */
pragma solidity 0.8.18;

import "openzeppelin-contracts/access/Ownable.sol";
import "openzeppelin-contracts/security/ReentrancyGuard.sol";
import "openzeppelin-contracts/utils/cryptography/ECDSA.sol";
import "openzeppelin-contracts/utils/cryptography/EIP712.sol";

import { SafeTransferLib } from "solady/utils/SafeTransferLib.sol";

import { ERC721 } from "solady/tokens/ERC721.sol";
import "openzeppelin-contracts/token/ERC721/utils/ERC721Holder.sol";

import "./IBatchBattle.sol";
import "./BlastDuelArenaEscrow.sol";

error BattleNotInitialized();
error BattleNotMatched();
error BattleAlreadyMatched();
error BattleAlreadyFinished();
error BattleAlreadyClaimed();
error BattleAlreadyNullified();
error BattleSelfForbidden();
error OnlyInitiatorCanNullify();
error ForgedSignature();
error ForgedWithdrawSignature();
error StakeAlreadyWithdrawn();
error NotAllowedParticipant();
error ClaimFromNonParticipant();
error UserAlreadyWithdrawn();
error WithdrawFromNonParticipant();
error UniqueJudge();
error TransferFailed();
error FeeTooLow();
error EthFeeNumerTooHigh();
error EthStakeMustMatchMsgValue();
error EthStakeNotUnique();
error ExistingStakes();
error MustHaveStake();
error ExceedsMaximumUniqueStakes();

contract BlastDuelArenaBattle is IBatchBattle, ReentrancyGuard, Ownable, ERC721Holder, EIP712 {
    using ECDSA for bytes32;

    BlastDuelArenaEscrow immutable escrow;
    address public immutable escrowAddr;
    address public judge;
    address public withdrawJudge;

    uint256 public nextBattleId;
    uint256 public withdrawFee;
    uint256 public ethFeeNumer;

    event BattleInitialized(
        uint256 indexed battleId,
        address indexed playerOne,
        address indexed playerTwo,
        uint256[] assetEnum,
        address[] contractAddr,
        uint256[] amtOrTokenId
    );

    event BattleJoined(
        uint256 indexed battleId,
        address indexed playerOne,
        address indexed playerTwo,
        uint256[] assetEnum,
        address[] contractAddr,
        uint256[] amtOrTokenId
    );
    event BattleWon(uint256 indexed battleId, address indexed winner);
    event BattleNullified(uint256 indexed battleId, address indexed caller, address indexed counterparty);
    event BattleCanceled(uint256 indexed battleId, address indexed caller);

    event JudgeChanged(address indexed newJudge);
    event WithdrawJudgeChanged(address indexed newWithdrawJudge);
    event FeeChanged(uint256 indexed feeChanged);
    event EthFeeNumerChanged(uint256 indexed ethFeeNumer);

    mapping(uint256 => Battle) public battles;

    IBlast public constant BLAST = IBlast(0x4300000000000000000000000000000000000002);

    constructor(address _owner, address _judge, address _withdrawJudge) EIP712("CambriaDuelArena", "1") {
        if (_owner == address(0) || _judge == address(0) || _withdrawJudge == address(0)) {
            revert ZeroAddressNotAllowed();
        }

        escrow = new BlastDuelArenaEscrow(_owner);
        escrowAddr = address(escrow);
        judge = _judge;
        withdrawJudge = _withdrawJudge;

        // Blast Specific
        BLAST.configureClaimableYield();
        BLAST.configureClaimableGas();
        BLAST.configureGovernor(_owner);
        // End Blast Specific

        _transferOwnership(_owner);
    }

    function changeJudge(address newJudge) external onlyOwner {
        if (newJudge == address(0)) revert ZeroAddressNotAllowed();
        if (newJudge == withdrawJudge) revert UniqueJudge();
        judge = newJudge;
        emit JudgeChanged(newJudge);
    }

    function changeWithdrawJudge(address newWithdrawJudge) external onlyOwner {
        if (newWithdrawJudge == address(0)) revert ZeroAddressNotAllowed();
        if (newWithdrawJudge == judge) revert UniqueJudge();
        withdrawJudge = newWithdrawJudge;
        emit WithdrawJudgeChanged(newWithdrawJudge);
    }

    function changeFee(uint256 fee) external onlyOwner {
        withdrawFee = fee;
        emit FeeChanged(fee);
    }

    function _ethFeeDenom() internal pure returns (uint256) {
        return 10_000;
    }

    function changeEthFeeNumer(uint256 feeNumer) external onlyOwner {
        if (feeNumer >= _ethFeeDenom()) revert EthFeeNumerTooHigh();
        ethFeeNumer = feeNumer;
        emit EthFeeNumerChanged(feeNumer);
    }

    function emergencyWithdraw(address payable payee) external onlyOwner nonReentrant {
        if (payee == address(0)) {
            revert ZeroAddressNotAllowed();
        }
        uint256 balance = address(this).balance;
        (bool transferTx,) = payee.call{ value: balance }("");
        if (!transferTx) {
            revert TransferFailed();
        }
    }

    function emergencyWithdrawERC20(address payable payee, address token) external onlyOwner nonReentrant {
        if (payee == address(0)) {
            revert ZeroAddressNotAllowed();
        }

        ERC20 tokenContract = ERC20(token);
        uint256 balance = tokenContract.balanceOf(address(this));

        SafeTransferLib.safeTransfer(address(tokenContract), payee, balance);
    }

    function emergencyWithdrawERC721(
        address payable payee,
        address nft,
        uint256 tokenId
    )
        external
        onlyOwner
        nonReentrant
    {
        if (payee == address(0)) {
            revert ZeroAddressNotAllowed();
        }

        ERC721 nftContract = ERC721(nft);
        nftContract.safeTransferFrom(address(this), payee, tokenId);
    }

    function emergencyRefund(uint256 battleId) external battleExists(battleId) onlyOwner nonReentrant {
        Battle storage selectedBattle = battles[battleId];
        if (!selectedBattle.claimed) {
            selectedBattle.claimed = true;
            battles[battleId] = selectedBattle;
            escrow.withdraw(selectedBattle.one.user, payable(selectedBattle.one.user), battleId);
            escrow.withdraw(selectedBattle.two.user, payable(selectedBattle.two.user), battleId);
        }
    }

    modifier battleExists(uint256 battleId) {
        require(battleId < nextBattleId, "Battle must exist!");
        _;
    }

    function getBattle(uint256 battleId) external view battleExists(battleId) returns (Battle memory) {
        Battle memory btl = battles[battleId];
        return btl;
    }

    function initBattle(
        address challenging,
        BatchStake[] calldata stakes
    )
        external
        payable
        nonReentrant
        returns (uint256)
    {
        uint256 numStakes = stakes.length;
        if (numStakes <= 0) revert MustHaveStake();
        if (numStakes > 8) revert ExceedsMaximumUniqueStakes();
        if (msg.sender == challenging) revert BattleSelfForbidden();

        uint256 ethValue = msg.value;

        uint256 battleId = nextBattleId;
        nextBattleId++;

        bool ethFlag = false;

        battles[battleId].one.user = msg.sender;

        BatchStake[] storage _stakes = battles[battleId].one.stakes;
        if (_stakes.length > 0) {
            revert ExistingStakes();
        }
        for (uint256 i = 0; i < numStakes; i++) {
            _stakes.push(stakes[i]);
        }
        battles[battleId].one.stakes = _stakes;
        battles[battleId].two.user = challenging;
        battles[battleId].value = ethValue;

        for (uint256 i = 0; i < numStakes; i++) {
            if (stakes[i].asset == AssetType.ETH) {
                if (ethValue != stakes[i].amtOrTokenId) {
                    revert EthStakeMustMatchMsgValue();
                }
                if (ethFlag) {
                    revert EthStakeNotUnique();
                }
                ethFlag = true;
            } else if (stakes[i].asset == AssetType.TOKEN) {
                ERC20 token = ERC20(stakes[i].contractAddr);
                // transfers token to this contract
                SafeTransferLib.safeTransferFrom(address(token), msg.sender, address(this), stakes[i].amtOrTokenId);
                // approves transfer to escrow
                SafeTransferLib.safeApprove(address(token), escrowAddr, stakes[i].amtOrTokenId);
            } else if (stakes[i].asset == AssetType.NFT) {
                ERC721 nft = ERC721(stakes[i].contractAddr);
                nft.safeTransferFrom(msg.sender, address(this), stakes[i].amtOrTokenId);
                nft.approve(escrowAddr, stakes[i].amtOrTokenId);
            }
        }

        (uint256[] memory assets, address[] memory contractAddrs, uint256[] memory amtOrTokenIds) =
            escrow.deposit{ value: ethValue }(msg.sender, battleId, stakes);

        emit BattleInitialized(battleId, msg.sender, challenging, assets, contractAddrs, amtOrTokenIds);

        return battleId;
    }

    function nullifyBattle(uint256 battleId) external payable battleExists(battleId) nonReentrant {
        Battle storage selectedBattle = battles[battleId];
        if (selectedBattle.matched) {
            revert BattleAlreadyMatched();
        } else if (selectedBattle.nullified) {
            revert BattleAlreadyNullified();
        } else if (selectedBattle.one.user != msg.sender) {
            revert OnlyInitiatorCanNullify();
        }

        selectedBattle.nullified = true;
        emit BattleNullified(battleId, msg.sender, selectedBattle.two.user);
        escrow.withdraw(selectedBattle.one.user, payable(msg.sender), battleId);
    }

    function joinBattle(
        uint256 battleId,
        BatchStake[] calldata stakes
    )
        external
        payable
        battleExists(battleId)
        nonReentrant
    {
        uint256 numStakes = stakes.length;
        if (numStakes <= 0) revert MustHaveStake();
        if (numStakes > 8) revert ExceedsMaximumUniqueStakes();

        uint256 ethValue = msg.value;

        bool ethFlag = false;
        Battle storage selectedBattle = battles[battleId];
        if (selectedBattle.matched) {
            revert BattleAlreadyMatched();
        } else if (selectedBattle.nullified) {
            revert BattleAlreadyNullified();
        } else if (selectedBattle.two.user != msg.sender) {
            revert NotAllowedParticipant();
        } else {
            BatchStake[] storage _stakes = battles[battleId].two.stakes;
            if (_stakes.length > 0) {
                revert ExistingStakes();
            }
            for (uint256 i = 0; i < numStakes; i++) {
                _stakes.push(stakes[i]);
            }
            battles[battleId].two.stakes = _stakes;
            battles[battleId].matched = true;
            battles[battleId].value += ethValue;

            for (uint256 i = 0; i < numStakes; i++) {
                if (stakes[i].asset == AssetType.ETH) {
                    if (ethValue != stakes[i].amtOrTokenId) {
                        revert EthStakeMustMatchMsgValue();
                    }
                    if (ethFlag) {
                        revert EthStakeNotUnique();
                    }
                    ethFlag = true;
                } else if (stakes[i].asset == AssetType.TOKEN) {
                    ERC20 token = ERC20(stakes[i].contractAddr);
                    // transfers token to this contract
                    SafeTransferLib.safeTransferFrom(address(token), msg.sender, address(this), stakes[i].amtOrTokenId);
                    // approves transfer to escrow
                    SafeTransferLib.safeApprove(address(token), escrowAddr, stakes[i].amtOrTokenId);
                } else if (stakes[i].asset == AssetType.NFT) {
                    ERC721 nft = ERC721(stakes[i].contractAddr);
                    nft.safeTransferFrom(msg.sender, address(this), stakes[i].amtOrTokenId);
                    nft.approve(escrowAddr, stakes[i].amtOrTokenId);
                }
            }

            (uint256[] memory assets, address[] memory contractAddrs, uint256[] memory amtOrTokenIds) =
                escrow.deposit{ value: ethValue }(msg.sender, battleId, stakes);

            emit BattleJoined(battleId, selectedBattle.one.user, msg.sender, assets, contractAddrs, amtOrTokenIds);
        }
    }

    function calcEthFee(uint256 battleId) public view battleExists(battleId) returns (uint256) {
        uint256 ethVal = battles[battleId].value;
        if (ethFeeNumer <= 0) return 0;
        return (ethVal * ethFeeNumer) / _ethFeeDenom();
    }

    function claimProceeds(
        uint256 battleId,
        bytes memory signature
    )
        external
        payable
        nonReentrant
        battleExists(battleId)
    {
        if (msg.value < withdrawFee) {
            revert FeeTooLow();
        }
        if (msg.value < calcEthFee(battleId)) {
            revert FeeTooLow();
        }

        if (battles[battleId].nullified) {
            revert BattleAlreadyNullified();
        }
        if (!battles[battleId].matched) {
            revert BattleNotMatched();
        }
        if (battles[battleId].claimed) {
            revert BattleAlreadyClaimed();
        }

        if (battles[battleId].one.hasWithdrawn || battles[battleId].two.hasWithdrawn) {
            revert UserAlreadyWithdrawn();
        }

        address signer =
            ECDSA.recover(getClaimTypedDataHash(Claim({ battleId: battleId, account: msg.sender })), signature);

        if (signer != judge) {
            revert ForgedSignature();
        } else {
            Battle storage selectedBattle = battles[battleId];

            if (msg.sender == selectedBattle.one.user) {
                selectedBattle.one.isWinner = true;
            } else if (msg.sender == selectedBattle.two.user) {
                selectedBattle.two.isWinner = true;
            } else {
                revert ClaimFromNonParticipant();
            }
            selectedBattle.claimed = true;
            battles[battleId] = selectedBattle;

            emit BattleWon(battleId, msg.sender);

            escrow.withdraw(selectedBattle.one.user, payable(msg.sender), battleId);
            escrow.withdraw(selectedBattle.two.user, payable(msg.sender), battleId);
        }
    }

    function claimTypeHash() internal pure returns (bytes32) {
        return keccak256("Claim(uint256 battleId,address account)");
    }

    function getClaimTypedDataHash(Claim memory claim) public view returns (bytes32) {
        return _hashTypedDataV4(keccak256(abi.encode(claimTypeHash(), claim.battleId, claim.account)));
    }

    function withdrawStake(uint256 battleId, bytes memory signature) external nonReentrant battleExists(battleId) {
        Battle storage selectedBattle = battles[battleId];

        if (battles[battleId].nullified) {
            revert BattleAlreadyNullified();
        }
        if (!battles[battleId].matched) {
            revert BattleNotMatched();
        }
        if (selectedBattle.claimed) {
            revert BattleAlreadyClaimed();
        }

        if (selectedBattle.one.user == msg.sender) {
            if (selectedBattle.one.hasWithdrawn) {
                revert UserAlreadyWithdrawn();
            }
        } else if (selectedBattle.two.user == msg.sender) {
            if (selectedBattle.two.hasWithdrawn) {
                revert UserAlreadyWithdrawn();
            }
        } else {
            revert WithdrawFromNonParticipant();
        }

        address signer =
            ECDSA.recover(getClaimTypedDataHash(Claim({ battleId: battleId, account: msg.sender })), signature);

        if (signer != withdrawJudge) {
            revert ForgedWithdrawSignature();
        } else {
            selectedBattle.one.hasWithdrawn = true;
            selectedBattle.two.hasWithdrawn = true;
            battles[battleId] = selectedBattle;

            emit BattleCanceled(battleId, msg.sender);

            escrow.withdraw(selectedBattle.one.user, payable(selectedBattle.one.user), battleId);
            escrow.withdraw(selectedBattle.two.user, payable(selectedBattle.two.user), battleId);
        }
    }
}

File 2 of 22 : Ownable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)

pragma solidity ^0.8.0;

import "../utils/Context.sol";

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

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

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor() {
        _transferOwnership(_msgSender());
    }

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

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

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
    }

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

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        _transferOwnership(newOwner);
    }

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

File 3 of 22 : ReentrancyGuard.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (security/ReentrancyGuard.sol)

pragma solidity ^0.8.0;

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuard {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant _NOT_ENTERED = 1;
    uint256 private constant _ENTERED = 2;

    uint256 private _status;

    constructor() {
        _status = _NOT_ENTERED;
    }

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

    function _nonReentrantBefore() private {
        // On the first call to nonReentrant, _status will be _NOT_ENTERED
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

        // Any calls to nonReentrant after this point will fail
        _status = _ENTERED;
    }

    function _nonReentrantAfter() private {
        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = _NOT_ENTERED;
    }

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

File 4 of 22 : ECDSA.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/cryptography/ECDSA.sol)

pragma solidity ^0.8.0;

import "../Strings.sol";

/**
 * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
 *
 * These functions can be used to verify that a message was signed by the holder
 * of the private keys of a given address.
 */
library ECDSA {
    enum RecoverError {
        NoError,
        InvalidSignature,
        InvalidSignatureLength,
        InvalidSignatureS,
        InvalidSignatureV // Deprecated in v4.8
    }

    function _throwError(RecoverError error) private pure {
        if (error == RecoverError.NoError) {
            return; // no error: do nothing
        } else if (error == RecoverError.InvalidSignature) {
            revert("ECDSA: invalid signature");
        } else if (error == RecoverError.InvalidSignatureLength) {
            revert("ECDSA: invalid signature length");
        } else if (error == RecoverError.InvalidSignatureS) {
            revert("ECDSA: invalid signature 's' value");
        }
    }

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature` or error string. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {toEthSignedMessageHash} on it.
     *
     * Documentation for signature generation:
     * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
     * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
     *
     * _Available since v4.3._
     */
    function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
        if (signature.length == 65) {
            bytes32 r;
            bytes32 s;
            uint8 v;
            // ecrecover takes the signature parameters, and the only way to get them
            // currently is to use assembly.
            /// @solidity memory-safe-assembly
            assembly {
                r := mload(add(signature, 0x20))
                s := mload(add(signature, 0x40))
                v := byte(0, mload(add(signature, 0x60)))
            }
            return tryRecover(hash, v, r, s);
        } else {
            return (address(0), RecoverError.InvalidSignatureLength);
        }
    }

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature`. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {toEthSignedMessageHash} on it.
     */
    function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, signature);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
     *
     * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
     *
     * _Available since v4.3._
     */
    function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError) {
        bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
        uint8 v = uint8((uint256(vs) >> 255) + 27);
        return tryRecover(hash, v, r, s);
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
     *
     * _Available since v4.2._
     */
    function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, r, vs);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `v`,
     * `r` and `s` signature fields separately.
     *
     * _Available since v4.3._
     */
    function tryRecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address, RecoverError) {
        // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
        // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
        // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
        // signatures from current libraries generate a unique signature with an s-value in the lower half order.
        //
        // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
        // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
        // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
        // these malleable signatures as well.
        if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
            return (address(0), RecoverError.InvalidSignatureS);
        }

        // If the signature is valid (and not malleable), return the signer address
        address signer = ecrecover(hash, v, r, s);
        if (signer == address(0)) {
            return (address(0), RecoverError.InvalidSignature);
        }

        return (signer, RecoverError.NoError);
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `v`,
     * `r` and `s` signature fields separately.
     */
    function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, v, r, s);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Returns an Ethereum Signed Message, created from a `hash`. This
     * produces hash corresponding to the one signed with the
     * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
     * JSON-RPC method as part of EIP-191.
     *
     * See {recover}.
     */
    function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32 message) {
        // 32 is the length in bytes of hash,
        // enforced by the type signature above
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, "\x19Ethereum Signed Message:\n32")
            mstore(0x1c, hash)
            message := keccak256(0x00, 0x3c)
        }
    }

    /**
     * @dev Returns an Ethereum Signed Message, created from `s`. This
     * produces hash corresponding to the one signed with the
     * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
     * JSON-RPC method as part of EIP-191.
     *
     * See {recover}.
     */
    function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", Strings.toString(s.length), s));
    }

    /**
     * @dev Returns an Ethereum Signed Typed Data, created from a
     * `domainSeparator` and a `structHash`. This produces hash corresponding
     * to the one signed with the
     * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
     * JSON-RPC method as part of EIP-712.
     *
     * See {recover}.
     */
    function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 data) {
        /// @solidity memory-safe-assembly
        assembly {
            let ptr := mload(0x40)
            mstore(ptr, "\x19\x01")
            mstore(add(ptr, 0x02), domainSeparator)
            mstore(add(ptr, 0x22), structHash)
            data := keccak256(ptr, 0x42)
        }
    }

    /**
     * @dev Returns an Ethereum Signed Data with intended validator, created from a
     * `validator` and `data` according to the version 0 of EIP-191.
     *
     * See {recover}.
     */
    function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked("\x19\x00", validator, data));
    }
}

File 5 of 22 : EIP712.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/cryptography/EIP712.sol)

pragma solidity ^0.8.8;

import "./ECDSA.sol";
import "../ShortStrings.sol";
import "../../interfaces/IERC5267.sol";

/**
 * @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.
 *
 * The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible,
 * thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding
 * they need in their contracts using a combination of `abi.encode` and `keccak256`.
 *
 * This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding
 * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA
 * ({_hashTypedDataV4}).
 *
 * The implementation of the domain separator was designed to be as efficient as possible while still properly updating
 * the chain id to protect against replay attacks on an eventual fork of the chain.
 *
 * NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method
 * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].
 *
 * NOTE: In the upgradeable version of this contract, the cached values will correspond to the address, and the domain
 * separator of the implementation contract. This will cause the `_domainSeparatorV4` function to always rebuild the
 * separator from the immutable values, which is cheaper than accessing a cached version in cold storage.
 *
 * _Available since v3.4._
 *
 * @custom:oz-upgrades-unsafe-allow state-variable-immutable state-variable-assignment
 */
abstract contract EIP712 is IERC5267 {
    using ShortStrings for *;

    bytes32 private constant _TYPE_HASH =
        keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");

    // Cache the domain separator as an immutable value, but also store the chain id that it corresponds to, in order to
    // invalidate the cached domain separator if the chain id changes.
    bytes32 private immutable _cachedDomainSeparator;
    uint256 private immutable _cachedChainId;
    address private immutable _cachedThis;

    bytes32 private immutable _hashedName;
    bytes32 private immutable _hashedVersion;

    ShortString private immutable _name;
    ShortString private immutable _version;
    string private _nameFallback;
    string private _versionFallback;

    /**
     * @dev Initializes the domain separator and parameter caches.
     *
     * The meaning of `name` and `version` is specified in
     * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]:
     *
     * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.
     * - `version`: the current major version of the signing domain.
     *
     * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart
     * contract upgrade].
     */
    constructor(string memory name, string memory version) {
        _name = name.toShortStringWithFallback(_nameFallback);
        _version = version.toShortStringWithFallback(_versionFallback);
        _hashedName = keccak256(bytes(name));
        _hashedVersion = keccak256(bytes(version));

        _cachedChainId = block.chainid;
        _cachedDomainSeparator = _buildDomainSeparator();
        _cachedThis = address(this);
    }

    /**
     * @dev Returns the domain separator for the current chain.
     */
    function _domainSeparatorV4() internal view returns (bytes32) {
        if (address(this) == _cachedThis && block.chainid == _cachedChainId) {
            return _cachedDomainSeparator;
        } else {
            return _buildDomainSeparator();
        }
    }

    function _buildDomainSeparator() private view returns (bytes32) {
        return keccak256(abi.encode(_TYPE_HASH, _hashedName, _hashedVersion, block.chainid, address(this)));
    }

    /**
     * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this
     * function returns the hash of the fully encoded EIP712 message for this domain.
     *
     * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:
     *
     * ```solidity
     * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
     *     keccak256("Mail(address to,string contents)"),
     *     mailTo,
     *     keccak256(bytes(mailContents))
     * )));
     * address signer = ECDSA.recover(digest, signature);
     * ```
     */
    function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {
        return ECDSA.toTypedDataHash(_domainSeparatorV4(), structHash);
    }

    /**
     * @dev See {EIP-5267}.
     *
     * _Available since v4.9._
     */
    function eip712Domain()
        public
        view
        virtual
        override
        returns (
            bytes1 fields,
            string memory name,
            string memory version,
            uint256 chainId,
            address verifyingContract,
            bytes32 salt,
            uint256[] memory extensions
        )
    {
        return (
            hex"0f", // 01111
            _name.toStringWithFallback(_nameFallback),
            _version.toStringWithFallback(_versionFallback),
            block.chainid,
            address(this),
            bytes32(0),
            new uint256[](0)
        );
    }
}

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

/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SafeTransferLib.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
///
/// @dev Note:
/// - For ETH transfers, please use `forceSafeTransferETH` for DoS protection.
/// - For ERC20s, this implementation won't check that a token has code,
///   responsibility is delegated to the caller.
library SafeTransferLib {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The ETH transfer has failed.
    error ETHTransferFailed();

    /// @dev The ERC20 `transferFrom` has failed.
    error TransferFromFailed();

    /// @dev The ERC20 `transfer` has failed.
    error TransferFailed();

    /// @dev The ERC20 `approve` has failed.
    error ApproveFailed();

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         CONSTANTS                          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Suggested gas stipend for contract receiving ETH that disallows any storage writes.
    uint256 internal constant GAS_STIPEND_NO_STORAGE_WRITES = 2300;

    /// @dev Suggested gas stipend for contract receiving ETH to perform a few
    /// storage reads and writes, but low enough to prevent griefing.
    uint256 internal constant GAS_STIPEND_NO_GRIEF = 100000;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       ETH OPERATIONS                       */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    // If the ETH transfer MUST succeed with a reasonable gas budget, use the force variants.
    //
    // The regular variants:
    // - Forwards all remaining gas to the target.
    // - Reverts if the target reverts.
    // - Reverts if the current contract has insufficient balance.
    //
    // The force variants:
    // - Forwards with an optional gas stipend
    //   (defaults to `GAS_STIPEND_NO_GRIEF`, which is sufficient for most cases).
    // - If the target reverts, or if the gas stipend is exhausted,
    //   creates a temporary contract to force send the ETH via `SELFDESTRUCT`.
    //   Future compatible with `SENDALL`: https://eips.ethereum.org/EIPS/eip-4758.
    // - Reverts if the current contract has insufficient balance.
    //
    // The try variants:
    // - Forwards with a mandatory gas stipend.
    // - Instead of reverting, returns whether the transfer succeeded.

    /// @dev Sends `amount` (in wei) ETH to `to`.
    function safeTransferETH(address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(call(gas(), to, amount, gas(), 0x00, gas(), 0x00)) {
                mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Sends all the ETH in the current contract to `to`.
    function safeTransferAllETH(address to) internal {
        /// @solidity memory-safe-assembly
        assembly {
            // Transfer all the ETH and check if it succeeded or not.
            if iszero(call(gas(), to, selfbalance(), gas(), 0x00, gas(), 0x00)) {
                mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Force sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
    function forceSafeTransferETH(address to, uint256 amount, uint256 gasStipend) internal {
        /// @solidity memory-safe-assembly
        assembly {
            if lt(selfbalance(), amount) {
                mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                revert(0x1c, 0x04)
            }
            if iszero(call(gasStipend, to, amount, gas(), 0x00, gas(), 0x00)) {
                mstore(0x00, to) // Store the address in scratch space.
                mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                if iszero(create(amount, 0x0b, 0x16)) {
                    returndatacopy(gas(), returndatasize(), shr(20, gas())) // For gas estimation.
                }
            }
        }
    }

    /// @dev Force sends all the ETH in the current contract to `to`, with a `gasStipend`.
    function forceSafeTransferAllETH(address to, uint256 gasStipend) internal {
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(call(gasStipend, to, selfbalance(), gas(), 0x00, gas(), 0x00)) {
                mstore(0x00, to) // Store the address in scratch space.
                mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                if iszero(create(selfbalance(), 0x0b, 0x16)) {
                    returndatacopy(gas(), returndatasize(), shr(20, gas())) // For gas estimation.
                }
            }
        }
    }

    /// @dev Force sends `amount` (in wei) ETH to `to`, with `GAS_STIPEND_NO_GRIEF`.
    function forceSafeTransferETH(address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            if lt(selfbalance(), amount) {
                mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                revert(0x1c, 0x04)
            }
            if iszero(call(GAS_STIPEND_NO_GRIEF, to, amount, gas(), 0x00, gas(), 0x00)) {
                mstore(0x00, to) // Store the address in scratch space.
                mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                if iszero(create(amount, 0x0b, 0x16)) {
                    returndatacopy(gas(), returndatasize(), shr(20, gas())) // For gas estimation.
                }
            }
        }
    }

    /// @dev Force sends all the ETH in the current contract to `to`, with `GAS_STIPEND_NO_GRIEF`.
    function forceSafeTransferAllETH(address to) internal {
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(call(GAS_STIPEND_NO_GRIEF, to, selfbalance(), gas(), 0x00, gas(), 0x00)) {
                mstore(0x00, to) // Store the address in scratch space.
                mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                if iszero(create(selfbalance(), 0x0b, 0x16)) {
                    returndatacopy(gas(), returndatasize(), shr(20, gas())) // For gas estimation.
                }
            }
        }
    }

    /// @dev Sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
    function trySafeTransferETH(address to, uint256 amount, uint256 gasStipend)
        internal
        returns (bool success)
    {
        /// @solidity memory-safe-assembly
        assembly {
            success := call(gasStipend, to, amount, gas(), 0x00, gas(), 0x00)
        }
    }

    /// @dev Sends all the ETH in the current contract to `to`, with a `gasStipend`.
    function trySafeTransferAllETH(address to, uint256 gasStipend)
        internal
        returns (bool success)
    {
        /// @solidity memory-safe-assembly
        assembly {
            success := call(gasStipend, to, selfbalance(), gas(), 0x00, gas(), 0x00)
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                      ERC20 OPERATIONS                      */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Sends `amount` of ERC20 `token` from `from` to `to`.
    /// Reverts upon failure.
    ///
    /// The `from` account must have at least `amount` approved for
    /// the current contract to manage.
    function safeTransferFrom(address token, address from, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x60, amount) // Store the `amount` argument.
            mstore(0x40, to) // Store the `to` argument.
            mstore(0x2c, shl(96, from)) // Store the `from` argument.
            mstore(0x0c, 0x23b872dd000000000000000000000000) // `transferFrom(address,address,uint256)`.
            // Perform the transfer, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                    call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
                )
            ) {
                mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x60, 0) // Restore the zero slot to zero.
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

    /// @dev Sends all of ERC20 `token` from `from` to `to`.
    /// Reverts upon failure.
    ///
    /// The `from` account must have their entire balance approved for
    /// the current contract to manage.
    function safeTransferAllFrom(address token, address from, address to)
        internal
        returns (uint256 amount)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x40, to) // Store the `to` argument.
            mstore(0x2c, shl(96, from)) // Store the `from` argument.
            mstore(0x0c, 0x70a08231000000000000000000000000) // `balanceOf(address)`.
            // Read the balance, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    gt(returndatasize(), 0x1f), // At least 32 bytes returned.
                    staticcall(gas(), token, 0x1c, 0x24, 0x60, 0x20)
                )
            ) {
                mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x00, 0x23b872dd) // `transferFrom(address,address,uint256)`.
            amount := mload(0x60) // The `amount` is already at 0x60. We'll need to return it.
            // Perform the transfer, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                    call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
                )
            ) {
                mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x60, 0) // Restore the zero slot to zero.
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

    /// @dev Sends `amount` of ERC20 `token` from the current contract to `to`.
    /// Reverts upon failure.
    function safeTransfer(address token, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x14, to) // Store the `to` argument.
            mstore(0x34, amount) // Store the `amount` argument.
            mstore(0x00, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`.
            // Perform the transfer, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                    call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                )
            ) {
                mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
        }
    }

    /// @dev Sends all of ERC20 `token` from the current contract to `to`.
    /// Reverts upon failure.
    function safeTransferAll(address token, address to) internal returns (uint256 amount) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, 0x70a08231) // Store the function selector of `balanceOf(address)`.
            mstore(0x20, address()) // Store the address of the current contract.
            // Read the balance, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    gt(returndatasize(), 0x1f), // At least 32 bytes returned.
                    staticcall(gas(), token, 0x1c, 0x24, 0x34, 0x20)
                )
            ) {
                mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x14, to) // Store the `to` argument.
            amount := mload(0x34) // The `amount` is already at 0x34. We'll need to return it.
            mstore(0x00, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`.
            // Perform the transfer, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                    call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                )
            ) {
                mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
        }
    }

    /// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract.
    /// Reverts upon failure.
    function safeApprove(address token, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x14, to) // Store the `to` argument.
            mstore(0x34, amount) // Store the `amount` argument.
            mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
            // Perform the approval, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                    call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                )
            ) {
                mstore(0x00, 0x3e3f8f73) // `ApproveFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
        }
    }

    /// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract.
    /// If the initial attempt to approve fails, attempts to reset the approved amount to zero,
    /// then retries the approval again (some tokens, e.g. USDT, requires this).
    /// Reverts upon failure.
    function safeApproveWithRetry(address token, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x14, to) // Store the `to` argument.
            mstore(0x34, amount) // Store the `amount` argument.
            mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
            // Perform the approval, retrying upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                    call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                )
            ) {
                mstore(0x34, 0) // Store 0 for the `amount`.
                mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
                pop(call(gas(), token, 0, 0x10, 0x44, 0x00, 0x00)) // Reset the approval.
                mstore(0x34, amount) // Store back the original `amount`.
                // Retry the approval, reverting upon failure.
                if iszero(
                    and(
                        or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                        call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                    )
                ) {
                    mstore(0x00, 0x3e3f8f73) // `ApproveFailed()`.
                    revert(0x1c, 0x04)
                }
            }
            mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
        }
    }

    /// @dev Returns the amount of ERC20 `token` owned by `account`.
    /// Returns zero if the `token` does not exist.
    function balanceOf(address token, address account) internal view returns (uint256 amount) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x14, account) // Store the `account` argument.
            mstore(0x00, 0x70a08231000000000000000000000000) // `balanceOf(address)`.
            amount :=
                mul(
                    mload(0x20),
                    and( // The arguments of `and` are evaluated from right to left.
                        gt(returndatasize(), 0x1f), // At least 32 bytes returned.
                        staticcall(gas(), token, 0x10, 0x24, 0x20, 0x20)
                    )
                )
        }
    }
}

File 7 of 22 : ERC721.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Simple ERC721 implementation with storage hitchhiking.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/tokens/ERC721.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC721.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/tree/master/contracts/token/ERC721/ERC721.sol)
///
/// @dev Note:
/// - The ERC721 standard allows for self-approvals.
///   For performance, this implementation WILL NOT revert for such actions.
///   Please add any checks with overrides if desired.
/// - For performance, methods are made payable where permitted by the ERC721 standard.
/// - The `safeTransfer` functions use the identity precompile (0x4)
///   to copy memory internally.
///
/// If you are overriding:
/// - NEVER violate the ERC721 invariant:
///   the balance of an owner MUST be always be equal to their number of ownership slots.
///   The transfer functions do not have an underflow guard for user token balances.
/// - Make sure all variables written to storage are properly cleaned
//    (e.g. the bool value for `isApprovedForAll` MUST be either 1 or 0 under the hood).
/// - Check that the overridden function is actually used in the function you want to
///   change the behavior of. Much of the code has been manually inlined for performance.
abstract contract ERC721 {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         CONSTANTS                          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev An account can hold up to 4294967295 tokens.
    uint256 internal constant _MAX_ACCOUNT_BALANCE = 0xffffffff;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Only the token owner or an approved account can manage the token.
    error NotOwnerNorApproved();

    /// @dev The token does not exist.
    error TokenDoesNotExist();

    /// @dev The token already exists.
    error TokenAlreadyExists();

    /// @dev Cannot query the balance for the zero address.
    error BalanceQueryForZeroAddress();

    /// @dev Cannot mint or transfer to the zero address.
    error TransferToZeroAddress();

    /// @dev The token must be owned by `from`.
    error TransferFromIncorrectOwner();

    /// @dev The recipient's balance has overflowed.
    error AccountBalanceOverflow();

    /// @dev Cannot safely transfer to a contract that does not implement
    /// the ERC721Receiver interface.
    error TransferToNonERC721ReceiverImplementer();

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                           EVENTS                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Emitted when token `id` is transferred from `from` to `to`.
    event Transfer(address indexed from, address indexed to, uint256 indexed id);

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

    /// @dev Emitted when `owner` enables or disables `operator` to manage all of their tokens.
    event ApprovalForAll(address indexed owner, address indexed operator, bool isApproved);

    /// @dev `keccak256(bytes("Transfer(address,address,uint256)"))`.
    uint256 private constant _TRANSFER_EVENT_SIGNATURE =
        0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef;

    /// @dev `keccak256(bytes("Approval(address,address,uint256)"))`.
    uint256 private constant _APPROVAL_EVENT_SIGNATURE =
        0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925;

    /// @dev `keccak256(bytes("ApprovalForAll(address,address,bool)"))`.
    uint256 private constant _APPROVAL_FOR_ALL_EVENT_SIGNATURE =
        0x17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                          STORAGE                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The ownership data slot of `id` is given by:
    /// ```
    ///     mstore(0x00, id)
    ///     mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
    ///     let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20)))
    /// ```
    /// Bits Layout:
    /// - [0..159]   `addr`
    /// - [160..255] `extraData`
    ///
    /// The approved address slot is given by: `add(1, ownershipSlot)`.
    ///
    /// See: https://notes.ethereum.org/%40vbuterin/verkle_tree_eip
    ///
    /// The balance slot of `owner` is given by:
    /// ```
    ///     mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
    ///     mstore(0x00, owner)
    ///     let balanceSlot := keccak256(0x0c, 0x1c)
    /// ```
    /// Bits Layout:
    /// - [0..31]   `balance`
    /// - [32..255] `aux`
    ///
    /// The `operator` approval slot of `owner` is given by:
    /// ```
    ///     mstore(0x1c, or(_ERC721_MASTER_SLOT_SEED, operator))
    ///     mstore(0x00, owner)
    ///     let operatorApprovalSlot := keccak256(0x0c, 0x30)
    /// ```
    uint256 private constant _ERC721_MASTER_SLOT_SEED = 0x7d8825530a5a2e7a << 192;

    /// @dev Pre-shifted and pre-masked constant.
    uint256 private constant _ERC721_MASTER_SLOT_SEED_MASKED = 0x0a5a2e7a00000000;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                      ERC721 METADATA                       */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns the token collection name.
    function name() public view virtual returns (string memory);

    /// @dev Returns the token collection symbol.
    function symbol() public view virtual returns (string memory);

    /// @dev Returns the Uniform Resource Identifier (URI) for token `id`.
    function tokenURI(uint256 id) public view virtual returns (string memory);

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                           ERC721                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns the owner of token `id`.
    ///
    /// Requirements:
    /// - Token `id` must exist.
    function ownerOf(uint256 id) public view virtual returns (address result) {
        result = _ownerOf(id);
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(result) {
                mstore(0x00, 0xceea21b6) // `TokenDoesNotExist()`.
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Returns the number of tokens owned by `owner`.
    ///
    /// Requirements:
    /// - `owner` must not be the zero address.
    function balanceOf(address owner) public view virtual returns (uint256 result) {
        /// @solidity memory-safe-assembly
        assembly {
            // Revert if the `owner` is the zero address.
            if iszero(owner) {
                mstore(0x00, 0x8f4eb604) // `BalanceQueryForZeroAddress()`.
                revert(0x1c, 0x04)
            }
            mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
            mstore(0x00, owner)
            result := and(sload(keccak256(0x0c, 0x1c)), _MAX_ACCOUNT_BALANCE)
        }
    }

    /// @dev Returns the account approved to manage token `id`.
    ///
    /// Requirements:
    /// - Token `id` must exist.
    function getApproved(uint256 id) public view virtual returns (address result) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, id)
            mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
            let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20)))
            if iszero(shl(96, sload(ownershipSlot))) {
                mstore(0x00, 0xceea21b6) // `TokenDoesNotExist()`.
                revert(0x1c, 0x04)
            }
            result := sload(add(1, ownershipSlot))
        }
    }

    /// @dev Sets `account` as the approved account to manage token `id`.
    ///
    /// Requirements:
    /// - Token `id` must exist.
    /// - The caller must be the owner of the token,
    ///   or an approved operator for the token owner.
    ///
    /// Emits an {Approval} event.
    function approve(address account, uint256 id) public payable virtual {
        _approve(msg.sender, account, id);
    }

    /// @dev Returns whether `operator` is approved to manage the tokens of `owner`.
    function isApprovedForAll(address owner, address operator)
        public
        view
        virtual
        returns (bool result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x1c, operator)
            mstore(0x08, _ERC721_MASTER_SLOT_SEED_MASKED)
            mstore(0x00, owner)
            result := sload(keccak256(0x0c, 0x30))
        }
    }

    /// @dev Sets whether `operator` is approved to manage the tokens of the caller.
    ///
    /// Emits an {ApprovalForAll} event.
    function setApprovalForAll(address operator, bool isApproved) public virtual {
        /// @solidity memory-safe-assembly
        assembly {
            // Convert to 0 or 1.
            isApproved := iszero(iszero(isApproved))
            // Update the `isApproved` for (`msg.sender`, `operator`).
            mstore(0x1c, operator)
            mstore(0x08, _ERC721_MASTER_SLOT_SEED_MASKED)
            mstore(0x00, caller())
            sstore(keccak256(0x0c, 0x30), isApproved)
            // Emit the {ApprovalForAll} event.
            mstore(0x00, isApproved)
            log3(
                0x00, 0x20, _APPROVAL_FOR_ALL_EVENT_SIGNATURE, caller(), shr(96, shl(96, operator))
            )
        }
    }

    /// @dev Transfers token `id` from `from` to `to`.
    ///
    /// Requirements:
    ///
    /// - Token `id` must exist.
    /// - `from` must be the owner of the token.
    /// - `to` cannot be the zero address.
    /// - The caller must be the owner of the token, or be approved to manage the token.
    ///
    /// Emits a {Transfer} event.
    function transferFrom(address from, address to, uint256 id) public payable virtual {
        _beforeTokenTransfer(from, to, id);
        /// @solidity memory-safe-assembly
        assembly {
            // Clear the upper 96 bits.
            let bitmaskAddress := shr(96, not(0))
            from := and(bitmaskAddress, from)
            to := and(bitmaskAddress, to)
            // Load the ownership data.
            mstore(0x00, id)
            mstore(0x1c, or(_ERC721_MASTER_SLOT_SEED, caller()))
            let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20)))
            let ownershipPacked := sload(ownershipSlot)
            let owner := and(bitmaskAddress, ownershipPacked)
            // Revert if `from` is not the owner, or does not exist.
            if iszero(mul(owner, eq(owner, from))) {
                if iszero(owner) {
                    mstore(0x00, 0xceea21b6) // `TokenDoesNotExist()`.
                    revert(0x1c, 0x04)
                }
                mstore(0x00, 0xa1148100) // `TransferFromIncorrectOwner()`.
                revert(0x1c, 0x04)
            }
            // Revert if `to` is the zero address.
            if iszero(to) {
                mstore(0x00, 0xea553b34) // `TransferToZeroAddress()`.
                revert(0x1c, 0x04)
            }
            // Load, check, and update the token approval.
            {
                mstore(0x00, from)
                let approvedAddress := sload(add(1, ownershipSlot))
                // Revert if the caller is not the owner, nor approved.
                if iszero(or(eq(caller(), from), eq(caller(), approvedAddress))) {
                    if iszero(sload(keccak256(0x0c, 0x30))) {
                        mstore(0x00, 0x4b6e7f18) // `NotOwnerNorApproved()`.
                        revert(0x1c, 0x04)
                    }
                }
                // Delete the approved address if any.
                if approvedAddress { sstore(add(1, ownershipSlot), 0) }
            }
            // Update with the new owner.
            sstore(ownershipSlot, xor(ownershipPacked, xor(from, to)))
            // Decrement the balance of `from`.
            {
                let fromBalanceSlot := keccak256(0x0c, 0x1c)
                sstore(fromBalanceSlot, sub(sload(fromBalanceSlot), 1))
            }
            // Increment the balance of `to`.
            {
                mstore(0x00, to)
                let toBalanceSlot := keccak256(0x0c, 0x1c)
                let toBalanceSlotPacked := add(sload(toBalanceSlot), 1)
                if iszero(and(toBalanceSlotPacked, _MAX_ACCOUNT_BALANCE)) {
                    mstore(0x00, 0x01336cea) // `AccountBalanceOverflow()`.
                    revert(0x1c, 0x04)
                }
                sstore(toBalanceSlot, toBalanceSlotPacked)
            }
            // Emit the {Transfer} event.
            log4(0x00, 0x00, _TRANSFER_EVENT_SIGNATURE, from, to, id)
        }
        _afterTokenTransfer(from, to, id);
    }

    /// @dev Equivalent to `safeTransferFrom(from, to, id, "")`.
    function safeTransferFrom(address from, address to, uint256 id) public payable virtual {
        transferFrom(from, to, id);
        if (_hasCode(to)) _checkOnERC721Received(from, to, id, "");
    }

    /// @dev Transfers token `id` from `from` to `to`.
    ///
    /// Requirements:
    ///
    /// - Token `id` must exist.
    /// - `from` must be the owner of the token.
    /// - `to` cannot be the zero address.
    /// - The caller must be the owner of the token, or be approved to manage the token.
    /// - If `to` refers to a smart contract, it must implement
    ///   {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
    ///
    /// Emits a {Transfer} event.
    function safeTransferFrom(address from, address to, uint256 id, bytes calldata data)
        public
        payable
        virtual
    {
        transferFrom(from, to, id);
        if (_hasCode(to)) _checkOnERC721Received(from, to, id, data);
    }

    /// @dev Returns true if this contract implements the interface defined by `interfaceId`.
    /// See: https://eips.ethereum.org/EIPS/eip-165
    /// This function call must use less than 30000 gas.
    function supportsInterface(bytes4 interfaceId) public view virtual returns (bool result) {
        /// @solidity memory-safe-assembly
        assembly {
            let s := shr(224, interfaceId)
            // ERC165: 0x01ffc9a7, ERC721: 0x80ac58cd, ERC721Metadata: 0x5b5e139f.
            result := or(or(eq(s, 0x01ffc9a7), eq(s, 0x80ac58cd)), eq(s, 0x5b5e139f))
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                  INTERNAL QUERY FUNCTIONS                  */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns if token `id` exists.
    function _exists(uint256 id) internal view virtual returns (bool result) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, id)
            mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
            result := iszero(iszero(shl(96, sload(add(id, add(id, keccak256(0x00, 0x20)))))))
        }
    }

    /// @dev Returns the owner of token `id`.
    /// Returns the zero address instead of reverting if the token does not exist.
    function _ownerOf(uint256 id) internal view virtual returns (address result) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, id)
            mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
            result := shr(96, shl(96, sload(add(id, add(id, keccak256(0x00, 0x20))))))
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*            INTERNAL DATA HITCHHIKING FUNCTIONS             */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    // For performance, no events are emitted for the hitchhiking setters.
    // Please emit your own events if required.

    /// @dev Returns the auxiliary data for `owner`.
    /// Minting, transferring, burning the tokens of `owner` will not change the auxiliary data.
    /// Auxiliary data can be set for any address, even if it does not have any tokens.
    function _getAux(address owner) internal view virtual returns (uint224 result) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
            mstore(0x00, owner)
            result := shr(32, sload(keccak256(0x0c, 0x1c)))
        }
    }

    /// @dev Set the auxiliary data for `owner` to `value`.
    /// Minting, transferring, burning the tokens of `owner` will not change the auxiliary data.
    /// Auxiliary data can be set for any address, even if it does not have any tokens.
    function _setAux(address owner, uint224 value) internal virtual {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
            mstore(0x00, owner)
            let balanceSlot := keccak256(0x0c, 0x1c)
            let packed := sload(balanceSlot)
            sstore(balanceSlot, xor(packed, shl(32, xor(value, shr(32, packed)))))
        }
    }

    /// @dev Returns the extra data for token `id`.
    /// Minting, transferring, burning a token will not change the extra data.
    /// The extra data can be set on a non-existent token.
    function _getExtraData(uint256 id) internal view virtual returns (uint96 result) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, id)
            mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
            result := shr(160, sload(add(id, add(id, keccak256(0x00, 0x20)))))
        }
    }

    /// @dev Sets the extra data for token `id` to `value`.
    /// Minting, transferring, burning a token will not change the extra data.
    /// The extra data can be set on a non-existent token.
    function _setExtraData(uint256 id, uint96 value) internal virtual {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, id)
            mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
            let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20)))
            let packed := sload(ownershipSlot)
            sstore(ownershipSlot, xor(packed, shl(160, xor(value, shr(160, packed)))))
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                  INTERNAL MINT FUNCTIONS                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Mints token `id` to `to`.
    ///
    /// Requirements:
    ///
    /// - Token `id` must not exist.
    /// - `to` cannot be the zero address.
    ///
    /// Emits a {Transfer} event.
    function _mint(address to, uint256 id) internal virtual {
        _beforeTokenTransfer(address(0), to, id);
        /// @solidity memory-safe-assembly
        assembly {
            // Clear the upper 96 bits.
            to := shr(96, shl(96, to))
            // Revert if `to` is the zero address.
            if iszero(to) {
                mstore(0x00, 0xea553b34) // `TransferToZeroAddress()`.
                revert(0x1c, 0x04)
            }
            // Load the ownership data.
            mstore(0x00, id)
            mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
            let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20)))
            let ownershipPacked := sload(ownershipSlot)
            // Revert if the token already exists.
            if shl(96, ownershipPacked) {
                mstore(0x00, 0xc991cbb1) // `TokenAlreadyExists()`.
                revert(0x1c, 0x04)
            }
            // Update with the owner.
            sstore(ownershipSlot, or(ownershipPacked, to))
            // Increment the balance of the owner.
            {
                mstore(0x00, to)
                let balanceSlot := keccak256(0x0c, 0x1c)
                let balanceSlotPacked := add(sload(balanceSlot), 1)
                if iszero(and(balanceSlotPacked, _MAX_ACCOUNT_BALANCE)) {
                    mstore(0x00, 0x01336cea) // `AccountBalanceOverflow()`.
                    revert(0x1c, 0x04)
                }
                sstore(balanceSlot, balanceSlotPacked)
            }
            // Emit the {Transfer} event.
            log4(0x00, 0x00, _TRANSFER_EVENT_SIGNATURE, 0, to, id)
        }
        _afterTokenTransfer(address(0), to, id);
    }

    /// @dev Equivalent to `_safeMint(to, id, "")`.
    function _safeMint(address to, uint256 id) internal virtual {
        _safeMint(to, id, "");
    }

    /// @dev Mints token `id` to `to`.
    ///
    /// Requirements:
    ///
    /// - Token `id` must not exist.
    /// - `to` cannot be the zero address.
    /// - If `to` refers to a smart contract, it must implement
    ///   {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
    ///
    /// Emits a {Transfer} event.
    function _safeMint(address to, uint256 id, bytes memory data) internal virtual {
        _mint(to, id);
        if (_hasCode(to)) _checkOnERC721Received(address(0), to, id, data);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                  INTERNAL BURN FUNCTIONS                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Equivalent to `_burn(address(0), id)`.
    function _burn(uint256 id) internal virtual {
        _burn(address(0), id);
    }

    /// @dev Destroys token `id`, using `by`.
    ///
    /// Requirements:
    ///
    /// - Token `id` must exist.
    /// - If `by` is not the zero address,
    ///   it must be the owner of the token, or be approved to manage the token.
    ///
    /// Emits a {Transfer} event.
    function _burn(address by, uint256 id) internal virtual {
        address owner = ownerOf(id);
        _beforeTokenTransfer(owner, address(0), id);
        /// @solidity memory-safe-assembly
        assembly {
            // Clear the upper 96 bits.
            by := shr(96, shl(96, by))
            // Load the ownership data.
            mstore(0x00, id)
            mstore(0x1c, or(_ERC721_MASTER_SLOT_SEED, by))
            let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20)))
            let ownershipPacked := sload(ownershipSlot)
            // Reload the owner in case it is changed in `_beforeTokenTransfer`.
            owner := shr(96, shl(96, ownershipPacked))
            // Revert if the token does not exist.
            if iszero(owner) {
                mstore(0x00, 0xceea21b6) // `TokenDoesNotExist()`.
                revert(0x1c, 0x04)
            }
            // Load and check the token approval.
            {
                mstore(0x00, owner)
                let approvedAddress := sload(add(1, ownershipSlot))
                // If `by` is not the zero address, do the authorization check.
                // Revert if the `by` is not the owner, nor approved.
                if iszero(or(iszero(by), or(eq(by, owner), eq(by, approvedAddress)))) {
                    if iszero(sload(keccak256(0x0c, 0x30))) {
                        mstore(0x00, 0x4b6e7f18) // `NotOwnerNorApproved()`.
                        revert(0x1c, 0x04)
                    }
                }
                // Delete the approved address if any.
                if approvedAddress { sstore(add(1, ownershipSlot), 0) }
            }
            // Clear the owner.
            sstore(ownershipSlot, xor(ownershipPacked, owner))
            // Decrement the balance of `owner`.
            {
                let balanceSlot := keccak256(0x0c, 0x1c)
                sstore(balanceSlot, sub(sload(balanceSlot), 1))
            }
            // Emit the {Transfer} event.
            log4(0x00, 0x00, _TRANSFER_EVENT_SIGNATURE, owner, 0, id)
        }
        _afterTokenTransfer(owner, address(0), id);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                INTERNAL APPROVAL FUNCTIONS                 */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns whether `account` is the owner of token `id`, or is approved to manage it.
    ///
    /// Requirements:
    /// - Token `id` must exist.
    function _isApprovedOrOwner(address account, uint256 id)
        internal
        view
        virtual
        returns (bool result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            result := 1
            // Clear the upper 96 bits.
            account := shr(96, shl(96, account))
            // Load the ownership data.
            mstore(0x00, id)
            mstore(0x1c, or(_ERC721_MASTER_SLOT_SEED, account))
            let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20)))
            let owner := shr(96, shl(96, sload(ownershipSlot)))
            // Revert if the token does not exist.
            if iszero(owner) {
                mstore(0x00, 0xceea21b6) // `TokenDoesNotExist()`.
                revert(0x1c, 0x04)
            }
            // Check if `account` is the `owner`.
            if iszero(eq(account, owner)) {
                mstore(0x00, owner)
                // Check if `account` is approved to manage the token.
                if iszero(sload(keccak256(0x0c, 0x30))) {
                    result := eq(account, sload(add(1, ownershipSlot)))
                }
            }
        }
    }

    /// @dev Returns the account approved to manage token `id`.
    /// Returns the zero address instead of reverting if the token does not exist.
    function _getApproved(uint256 id) internal view virtual returns (address result) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, id)
            mstore(0x1c, _ERC721_MASTER_SLOT_SEED)
            result := sload(add(1, add(id, add(id, keccak256(0x00, 0x20)))))
        }
    }

    /// @dev Equivalent to `_approve(address(0), account, id)`.
    function _approve(address account, uint256 id) internal virtual {
        _approve(address(0), account, id);
    }

    /// @dev Sets `account` as the approved account to manage token `id`, using `by`.
    ///
    /// Requirements:
    /// - Token `id` must exist.
    /// - If `by` is not the zero address, `by` must be the owner
    ///   or an approved operator for the token owner.
    ///
    /// Emits a {Transfer} event.
    function _approve(address by, address account, uint256 id) internal virtual {
        assembly {
            // Clear the upper 96 bits.
            let bitmaskAddress := shr(96, not(0))
            account := and(bitmaskAddress, account)
            by := and(bitmaskAddress, by)
            // Load the owner of the token.
            mstore(0x00, id)
            mstore(0x1c, or(_ERC721_MASTER_SLOT_SEED, by))
            let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20)))
            let owner := and(bitmaskAddress, sload(ownershipSlot))
            // Revert if the token does not exist.
            if iszero(owner) {
                mstore(0x00, 0xceea21b6) // `TokenDoesNotExist()`.
                revert(0x1c, 0x04)
            }
            // If `by` is not the zero address, do the authorization check.
            // Revert if `by` is not the owner, nor approved.
            if iszero(or(iszero(by), eq(by, owner))) {
                mstore(0x00, owner)
                if iszero(sload(keccak256(0x0c, 0x30))) {
                    mstore(0x00, 0x4b6e7f18) // `NotOwnerNorApproved()`.
                    revert(0x1c, 0x04)
                }
            }
            // Sets `account` as the approved account to manage `id`.
            sstore(add(1, ownershipSlot), account)
            // Emit the {Approval} event.
            log4(0x00, 0x00, _APPROVAL_EVENT_SIGNATURE, owner, account, id)
        }
    }

    /// @dev Approve or remove the `operator` as an operator for `by`,
    /// without authorization checks.
    ///
    /// Emits an {ApprovalForAll} event.
    function _setApprovalForAll(address by, address operator, bool isApproved) internal virtual {
        /// @solidity memory-safe-assembly
        assembly {
            // Clear the upper 96 bits.
            by := shr(96, shl(96, by))
            operator := shr(96, shl(96, operator))
            // Convert to 0 or 1.
            isApproved := iszero(iszero(isApproved))
            // Update the `isApproved` for (`by`, `operator`).
            mstore(0x1c, or(_ERC721_MASTER_SLOT_SEED, operator))
            mstore(0x00, by)
            sstore(keccak256(0x0c, 0x30), isApproved)
            // Emit the {ApprovalForAll} event.
            mstore(0x00, isApproved)
            log3(0x00, 0x20, _APPROVAL_FOR_ALL_EVENT_SIGNATURE, by, operator)
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                INTERNAL TRANSFER FUNCTIONS                 */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Equivalent to `_transfer(address(0), from, to, id)`.
    function _transfer(address from, address to, uint256 id) internal virtual {
        _transfer(address(0), from, to, id);
    }

    /// @dev Transfers token `id` from `from` to `to`.
    ///
    /// Requirements:
    ///
    /// - Token `id` must exist.
    /// - `from` must be the owner of the token.
    /// - `to` cannot be the zero address.
    /// - If `by` is not the zero address,
    ///   it must be the owner of the token, or be approved to manage the token.
    ///
    /// Emits a {Transfer} event.
    function _transfer(address by, address from, address to, uint256 id) internal virtual {
        _beforeTokenTransfer(from, to, id);
        /// @solidity memory-safe-assembly
        assembly {
            // Clear the upper 96 bits.
            let bitmaskAddress := shr(96, not(0))
            from := and(bitmaskAddress, from)
            to := and(bitmaskAddress, to)
            by := and(bitmaskAddress, by)
            // Load the ownership data.
            mstore(0x00, id)
            mstore(0x1c, or(_ERC721_MASTER_SLOT_SEED, by))
            let ownershipSlot := add(id, add(id, keccak256(0x00, 0x20)))
            let ownershipPacked := sload(ownershipSlot)
            let owner := and(bitmaskAddress, ownershipPacked)
            // Revert if `from` is not the owner, or does not exist.
            if iszero(mul(owner, eq(owner, from))) {
                if iszero(owner) {
                    mstore(0x00, 0xceea21b6) // `TokenDoesNotExist()`.
                    revert(0x1c, 0x04)
                }
                mstore(0x00, 0xa1148100) // `TransferFromIncorrectOwner()`.
                revert(0x1c, 0x04)
            }
            // Revert if `to` is the zero address.
            if iszero(to) {
                mstore(0x00, 0xea553b34) // `TransferToZeroAddress()`.
                revert(0x1c, 0x04)
            }
            // Load, check, and update the token approval.
            {
                mstore(0x00, from)
                let approvedAddress := sload(add(1, ownershipSlot))
                // If `by` is not the zero address, do the authorization check.
                // Revert if the `by` is not the owner, nor approved.
                if iszero(or(iszero(by), or(eq(by, from), eq(by, approvedAddress)))) {
                    if iszero(sload(keccak256(0x0c, 0x30))) {
                        mstore(0x00, 0x4b6e7f18) // `NotOwnerNorApproved()`.
                        revert(0x1c, 0x04)
                    }
                }
                // Delete the approved address if any.
                if approvedAddress { sstore(add(1, ownershipSlot), 0) }
            }
            // Update with the new owner.
            sstore(ownershipSlot, xor(ownershipPacked, xor(from, to)))
            // Decrement the balance of `from`.
            {
                let fromBalanceSlot := keccak256(0x0c, 0x1c)
                sstore(fromBalanceSlot, sub(sload(fromBalanceSlot), 1))
            }
            // Increment the balance of `to`.
            {
                mstore(0x00, to)
                let toBalanceSlot := keccak256(0x0c, 0x1c)
                let toBalanceSlotPacked := add(sload(toBalanceSlot), 1)
                if iszero(and(toBalanceSlotPacked, _MAX_ACCOUNT_BALANCE)) {
                    mstore(0x00, 0x01336cea) // `AccountBalanceOverflow()`.
                    revert(0x1c, 0x04)
                }
                sstore(toBalanceSlot, toBalanceSlotPacked)
            }
            // Emit the {Transfer} event.
            log4(0x00, 0x00, _TRANSFER_EVENT_SIGNATURE, from, to, id)
        }
        _afterTokenTransfer(from, to, id);
    }

    /// @dev Equivalent to `_safeTransfer(from, to, id, "")`.
    function _safeTransfer(address from, address to, uint256 id) internal virtual {
        _safeTransfer(from, to, id, "");
    }

    /// @dev Transfers token `id` from `from` to `to`.
    ///
    /// Requirements:
    ///
    /// - Token `id` must exist.
    /// - `from` must be the owner of the token.
    /// - `to` cannot be the zero address.
    /// - The caller must be the owner of the token, or be approved to manage the token.
    /// - If `to` refers to a smart contract, it must implement
    ///   {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
    ///
    /// Emits a {Transfer} event.
    function _safeTransfer(address from, address to, uint256 id, bytes memory data)
        internal
        virtual
    {
        _transfer(address(0), from, to, id);
        if (_hasCode(to)) _checkOnERC721Received(from, to, id, data);
    }

    /// @dev Equivalent to `_safeTransfer(by, from, to, id, "")`.
    function _safeTransfer(address by, address from, address to, uint256 id) internal virtual {
        _safeTransfer(by, from, to, id, "");
    }

    /// @dev Transfers token `id` from `from` to `to`.
    ///
    /// Requirements:
    ///
    /// - Token `id` must exist.
    /// - `from` must be the owner of the token.
    /// - `to` cannot be the zero address.
    /// - If `by` is not the zero address,
    ///   it must be the owner of the token, or be approved to manage the token.
    /// - If `to` refers to a smart contract, it must implement
    ///   {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
    ///
    /// Emits a {Transfer} event.
    function _safeTransfer(address by, address from, address to, uint256 id, bytes memory data)
        internal
        virtual
    {
        _transfer(by, from, to, id);
        if (_hasCode(to)) _checkOnERC721Received(from, to, id, data);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                    HOOKS FOR OVERRIDING                    */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Hook that is called before any token transfers, including minting and burning.
    function _beforeTokenTransfer(address from, address to, uint256 id) internal virtual {}

    /// @dev Hook that is called after any token transfers, including minting and burning.
    function _afterTokenTransfer(address from, address to, uint256 id) internal virtual {}

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                      PRIVATE HELPERS                       */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns if `a` has bytecode of non-zero length.
    function _hasCode(address a) private view returns (bool result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := extcodesize(a) // Can handle dirty upper bits.
        }
    }

    /// @dev Perform a call to invoke {IERC721Receiver-onERC721Received} on `to`.
    /// Reverts if the target does not support the function correctly.
    function _checkOnERC721Received(address from, address to, uint256 id, bytes memory data)
        private
    {
        /// @solidity memory-safe-assembly
        assembly {
            // Prepare the calldata.
            let m := mload(0x40)
            let onERC721ReceivedSelector := 0x150b7a02
            mstore(m, onERC721ReceivedSelector)
            mstore(add(m, 0x20), caller()) // The `operator`, which is always `msg.sender`.
            mstore(add(m, 0x40), shr(96, shl(96, from)))
            mstore(add(m, 0x60), id)
            mstore(add(m, 0x80), 0x80)
            let n := mload(data)
            mstore(add(m, 0xa0), n)
            if n { pop(staticcall(gas(), 4, add(data, 0x20), n, add(m, 0xc0), n)) }
            // Revert if the call reverts.
            if iszero(call(gas(), to, 0, add(m, 0x1c), add(n, 0xa4), m, 0x20)) {
                if returndatasize() {
                    // Bubble up the revert if the call reverts.
                    returndatacopy(0x00, 0x00, returndatasize())
                    revert(0x00, returndatasize())
                }
            }
            // Load the returndata and compare it.
            if iszero(eq(mload(m), shl(224, onERC721ReceivedSelector))) {
                mstore(0x00, 0xd1a57ed6) // `TransferToNonERC721ReceiverImplementer()`.
                revert(0x1c, 0x04)
            }
        }
    }
}

File 8 of 22 : ERC721Holder.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC721/utils/ERC721Holder.sol)

pragma solidity ^0.8.0;

import "../IERC721Receiver.sol";

/**
 * @dev Implementation of the {IERC721Receiver} interface.
 *
 * Accepts all token transfers.
 * Make sure the contract is able to use its token with {IERC721-safeTransferFrom}, {IERC721-approve} or {IERC721-setApprovalForAll}.
 */
contract ERC721Holder is IERC721Receiver {
    /**
     * @dev See {IERC721Receiver-onERC721Received}.
     *
     * Always returns `IERC721Receiver.onERC721Received.selector`.
     */
    function onERC721Received(address, address, uint256, bytes memory) public virtual override returns (bytes4) {
        return this.onERC721Received.selector;
    }
}

File 9 of 22 : IBatchBattle.sol
// SPDX-License-Identifier: UNLICENSED
// Written by will at cambria dot gg
// reachout for questions / concerns / vuln reports

pragma solidity 0.8.18;

import "./IBatchEscrow.sol";

interface IBatchBattle is IBatchEscrow {
    struct BattleParticipant {
        bool isWinner; // 8 bits
        bool hasWithdrawn; // 8 bits
        address user; // 160 bits
        BatchStake[] stakes;
    }

    struct Battle {
        bool claimed;
        bool matched;
        bool nullified;
        uint256 value;
        BattleParticipant one;
        BattleParticipant two;
    }

    struct Claim {
        uint256 battleId;
        address account;
    }
}

File 10 of 22 : BlastDuelArenaEscrow.sol
// SPDX-License-Identifier: UNLICENSED
// Written by will [at] cambria dot gg
// reachout for questions / concerns / vuln reports
// Modified from OpenZeppelin Escrow (last updated v4.7.0) (utils/escrow/Escrow.sol)

pragma solidity 0.8.18;

import "openzeppelin-contracts/access/Ownable.sol";
import "openzeppelin-contracts/utils/Address.sol";

import { ERC20 } from "solady/tokens/ERC20.sol";
import { SafeTransferLib } from "solady/utils/SafeTransferLib.sol";

import { ERC721 } from "solady/tokens/ERC721.sol";
import "openzeppelin-contracts/token/ERC721/utils/ERC721Holder.sol";

import "./IBatchEscrow.sol";
import "./IBlast.sol";

error ZeroValueETH();
error ZeroAddressNotAllowed();

/**
 * @title DuelArenaEscrow
 * @notice Modified for Blast
 * Modified OpenZeppelin Escrow Contract for ETH/ERC20/ERC721
 */
contract BlastDuelArenaEscrow is IBatchEscrow, Ownable, ERC721Holder {
    using Address for address payable;

    event Deposited(
        address indexed payee, uint256 battleId, uint256[] assetEnum, address[] contractAddr, uint256[] amtOrTokenId
    );
    event Withdrawn(
        address indexed payee, uint256 battleId, uint256[] assetEnum, address[] contractAddr, uint256[] amtOrTokenId
    );

    /* 
    - - - - - - - - - - - - - - - - - - -
    |   Blast Specific 
    - - - - - - - - - - - - - - - - - - -
    */
    IBlast public constant BLAST = IBlast(0x4300000000000000000000000000000000000002);

    constructor(address gov) {
        if (gov == address(0)) revert ZeroAddressNotAllowed();
        BLAST.configureClaimableYield();
        BLAST.configureClaimableGas();
        BLAST.configureGovernor(gov);
    }
    /* 
    = = = = = = = = = = = = = = = = = = = 
    ||  End Blast Specific 
    = = = = = = = = = = = = = = = = = = = 
    */

    mapping(address => mapping(uint256 => BatchStake[])) private _deposits;

    function depositsOf(address payee, uint256 battleId) public view returns (BatchStake[] memory) {
        BatchStake[] memory stakes = _deposits[payee][battleId];
        return stakes;
    }

    /**
     * @dev Stores the sent amount as credit to be withdrawn.
     * @param payee The destination address of the funds.
     *
     * Emits a {Deposited} event.
     */
    function deposit(
        address payee,
        uint256 battleId,
        BatchStake[] calldata stakes
    )
        public
        payable
        onlyOwner
        returns (uint256[] memory, address[] memory, uint256[] memory)
    {
        uint256 numStakes = stakes.length;
        uint256 ethValue = msg.value;
        // event data
        uint256[] memory assets = new uint256[](numStakes);
        address[] memory contractAddrs = new address[](numStakes);
        uint256[] memory amtOrTokenIds = new uint256[](numStakes);

        // to avoid re-entrancy need to write state variables prior to
        // the action.
        for (uint256 i = 0; i < numStakes; i++) {
            assets[i] = uint256(stakes[i].asset);
            contractAddrs[i] = stakes[i].contractAddr;
            amtOrTokenIds[i] = stakes[i].amtOrTokenId;

            _deposits[payee][battleId].push(
                BatchStake({
                    asset: stakes[i].asset,
                    contractAddr: stakes[i].contractAddr,
                    amtOrTokenId: stakes[i].amtOrTokenId
                })
            );
        }

        for (uint256 i = 0; i < numStakes; i++) {
            // split depending on asset type
            if (stakes[i].asset == AssetType.ETH) {
                if (ethValue <= 0) {
                    revert ZeroValueETH();
                }
            } else if (stakes[i].asset == AssetType.TOKEN) {
                ERC20 token = ERC20(stakes[i].contractAddr);
                SafeTransferLib.safeTransferFrom(address(token), msg.sender, address(this), stakes[i].amtOrTokenId);
            } else if (stakes[i].asset == AssetType.NFT) {
                ERC721 nft = ERC721(stakes[i].contractAddr);
                nft.safeTransferFrom(msg.sender, address(this), stakes[i].amtOrTokenId);
            }
        }

        emit Deposited(payee, battleId, assets, contractAddrs, amtOrTokenIds);
        return (assets, contractAddrs, amtOrTokenIds);
    }

    /**
     * @dev Withdraw accumulated balance for a payee, forwarding all gas to the
     * recipient.
     *
     * WARNING: Forwarding all gas opens the door to reentrancy vulnerabilities.
     * Make sure you trust the recipient, or are either following the
     * checks-effects-interactions pattern or using {ReentrancyGuard}.
     *
     * @param payee The address whose funds will be withdrawn and transferred to.
     *
     * Emits a {Withdrawn} event.
     */
    function withdraw(address payee, address payable receiver, uint256 battleId) public onlyOwner {
        BatchStake[] memory stakes = _deposits[payee][battleId];
        uint256 numStakes = stakes.length;

        // event data
        uint256[] memory assets = new uint256[](numStakes);
        address[] memory contractAddrs = new address[](numStakes);
        uint256[] memory amtOrTokenIds = new uint256[](numStakes);

        for (uint256 i = 0; i < numStakes; i++) {
            BatchStake memory stake = _deposits[payee][battleId][i];
            // split depending on asset type
            if (stake.asset == AssetType.ETH) {
                receiver.sendValue(stake.amtOrTokenId);
            } else if (stake.asset == AssetType.TOKEN) {
                ERC20 token = ERC20(stake.contractAddr);
                SafeTransferLib.safeTransfer(address(token), receiver, stake.amtOrTokenId);
            } else if (stake.asset == AssetType.NFT) {
                ERC721 nft = ERC721(stake.contractAddr);
                nft.safeTransferFrom(address(this), receiver, stake.amtOrTokenId);
            }
            assets[i] = uint256(stake.asset);
            contractAddrs[i] = stake.contractAddr;
            amtOrTokenIds[i] = stake.amtOrTokenId;
        }
        emit Withdrawn(payee, battleId, assets, contractAddrs, amtOrTokenIds);
    }
}

File 11 of 22 : Context.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity ^0.8.0;

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

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

File 12 of 22 : Strings.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol)

pragma solidity ^0.8.0;

import "./math/Math.sol";
import "./math/SignedMath.sol";

/**
 * @dev String operations.
 */
library Strings {
    bytes16 private constant _SYMBOLS = "0123456789abcdef";
    uint8 private constant _ADDRESS_LENGTH = 20;

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        unchecked {
            uint256 length = Math.log10(value) + 1;
            string memory buffer = new string(length);
            uint256 ptr;
            /// @solidity memory-safe-assembly
            assembly {
                ptr := add(buffer, add(32, length))
            }
            while (true) {
                ptr--;
                /// @solidity memory-safe-assembly
                assembly {
                    mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
                }
                value /= 10;
                if (value == 0) break;
            }
            return buffer;
        }
    }

    /**
     * @dev Converts a `int256` to its ASCII `string` decimal representation.
     */
    function toString(int256 value) internal pure returns (string memory) {
        return string(abi.encodePacked(value < 0 ? "-" : "", toString(SignedMath.abs(value))));
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        unchecked {
            return toHexString(value, Math.log256(value) + 1);
        }
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
     */
    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
        bytes memory buffer = new bytes(2 * length + 2);
        buffer[0] = "0";
        buffer[1] = "x";
        for (uint256 i = 2 * length + 1; i > 1; --i) {
            buffer[i] = _SYMBOLS[value & 0xf];
            value >>= 4;
        }
        require(value == 0, "Strings: hex length insufficient");
        return string(buffer);
    }

    /**
     * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
     */
    function toHexString(address addr) internal pure returns (string memory) {
        return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
    }

    /**
     * @dev Returns true if the two strings are equal.
     */
    function equal(string memory a, string memory b) internal pure returns (bool) {
        return keccak256(bytes(a)) == keccak256(bytes(b));
    }
}

File 13 of 22 : ShortStrings.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/ShortStrings.sol)

pragma solidity ^0.8.8;

import "./StorageSlot.sol";

// | string  | 0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA   |
// | length  | 0x                                                              BB |
type ShortString is bytes32;

/**
 * @dev This library provides functions to convert short memory strings
 * into a `ShortString` type that can be used as an immutable variable.
 *
 * Strings of arbitrary length can be optimized using this library if
 * they are short enough (up to 31 bytes) by packing them with their
 * length (1 byte) in a single EVM word (32 bytes). Additionally, a
 * fallback mechanism can be used for every other case.
 *
 * Usage example:
 *
 * ```solidity
 * contract Named {
 *     using ShortStrings for *;
 *
 *     ShortString private immutable _name;
 *     string private _nameFallback;
 *
 *     constructor(string memory contractName) {
 *         _name = contractName.toShortStringWithFallback(_nameFallback);
 *     }
 *
 *     function name() external view returns (string memory) {
 *         return _name.toStringWithFallback(_nameFallback);
 *     }
 * }
 * ```
 */
library ShortStrings {
    // Used as an identifier for strings longer than 31 bytes.
    bytes32 private constant _FALLBACK_SENTINEL = 0x00000000000000000000000000000000000000000000000000000000000000FF;

    error StringTooLong(string str);
    error InvalidShortString();

    /**
     * @dev Encode a string of at most 31 chars into a `ShortString`.
     *
     * This will trigger a `StringTooLong` error is the input string is too long.
     */
    function toShortString(string memory str) internal pure returns (ShortString) {
        bytes memory bstr = bytes(str);
        if (bstr.length > 31) {
            revert StringTooLong(str);
        }
        return ShortString.wrap(bytes32(uint256(bytes32(bstr)) | bstr.length));
    }

    /**
     * @dev Decode a `ShortString` back to a "normal" string.
     */
    function toString(ShortString sstr) internal pure returns (string memory) {
        uint256 len = byteLength(sstr);
        // using `new string(len)` would work locally but is not memory safe.
        string memory str = new string(32);
        /// @solidity memory-safe-assembly
        assembly {
            mstore(str, len)
            mstore(add(str, 0x20), sstr)
        }
        return str;
    }

    /**
     * @dev Return the length of a `ShortString`.
     */
    function byteLength(ShortString sstr) internal pure returns (uint256) {
        uint256 result = uint256(ShortString.unwrap(sstr)) & 0xFF;
        if (result > 31) {
            revert InvalidShortString();
        }
        return result;
    }

    /**
     * @dev Encode a string into a `ShortString`, or write it to storage if it is too long.
     */
    function toShortStringWithFallback(string memory value, string storage store) internal returns (ShortString) {
        if (bytes(value).length < 32) {
            return toShortString(value);
        } else {
            StorageSlot.getStringSlot(store).value = value;
            return ShortString.wrap(_FALLBACK_SENTINEL);
        }
    }

    /**
     * @dev Decode a string that was encoded to `ShortString` or written to storage using {setWithFallback}.
     */
    function toStringWithFallback(ShortString value, string storage store) internal pure returns (string memory) {
        if (ShortString.unwrap(value) != _FALLBACK_SENTINEL) {
            return toString(value);
        } else {
            return store;
        }
    }

    /**
     * @dev Return the length of a string that was encoded to `ShortString` or written to storage using {setWithFallback}.
     *
     * WARNING: This will return the "byte length" of the string. This may not reflect the actual length in terms of
     * actual characters as the UTF-8 encoding of a single character can span over multiple bytes.
     */
    function byteLengthWithFallback(ShortString value, string storage store) internal view returns (uint256) {
        if (ShortString.unwrap(value) != _FALLBACK_SENTINEL) {
            return byteLength(value);
        } else {
            return bytes(store).length;
        }
    }
}

File 14 of 22 : IERC5267.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (interfaces/IERC5267.sol)

pragma solidity ^0.8.0;

interface IERC5267 {
    /**
     * @dev MAY be emitted to signal that the domain could have changed.
     */
    event EIP712DomainChanged();

    /**
     * @dev returns the fields and values that describe the domain separator used by this contract for EIP-712
     * signature.
     */
    function eip712Domain()
        external
        view
        returns (
            bytes1 fields,
            string memory name,
            string memory version,
            uint256 chainId,
            address verifyingContract,
            bytes32 salt,
            uint256[] memory extensions
        );
}

File 15 of 22 : IERC721Receiver.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721Receiver.sol)

pragma solidity ^0.8.0;

/**
 * @title ERC721 token receiver interface
 * @dev Interface for any contract that wants to support safeTransfers
 * from ERC721 asset contracts.
 */
interface IERC721Receiver {
    /**
     * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
     * by `operator` from `from`, this function is called.
     *
     * It must return its Solidity selector to confirm the token transfer.
     * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
     *
     * The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.
     */
    function onERC721Received(
        address operator,
        address from,
        uint256 tokenId,
        bytes calldata data
    ) external returns (bytes4);
}

File 16 of 22 : IBatchEscrow.sol
// SPDX-License-Identifier: UNLICENSED
// Written by will at cambria dot gg
// reachout for questions / concerns / vuln reports

pragma solidity 0.8.18;

interface IBatchEscrow {
    enum AssetType {
        ETH,
        TOKEN,
        NFT
    }

    struct BatchStake {
        AssetType asset; // 8 bits
        address contractAddr; // 160 bits
        uint256 amtOrTokenId; // 256 bits
    }
}

File 17 of 22 : Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)

pragma solidity ^0.8.1;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     *
     * Furthermore, `isContract` will also return true if the target contract within
     * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
     * which only has an effect at the end of a transaction.
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 0;
    }

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

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

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

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

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

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
    }

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

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
     *
     * _Available since v4.8._
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        if (success) {
            if (returndata.length == 0) {
                // only check isContract if the call was successful and the return data is empty
                // otherwise we already know that it was a contract
                require(isContract(target), "Address: call to non-contract");
            }
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason or using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

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

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

/// @notice Simple ERC20 + EIP-2612 implementation.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/tokens/ERC20.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol)
///
/// @dev Note:
/// - The ERC20 standard allows minting and transferring to and from the zero address,
///   minting and transferring zero tokens, as well as self-approvals.
///   For performance, this implementation WILL NOT revert for such actions.
///   Please add any checks with overrides if desired.
/// - The `permit` function use the ecrecover precompile (0x1).
///
/// If you are overriding:
/// - NEVER violate the ERC20 invariant:
///   the total sum of all balances must be equal to `totalSupply()`.
/// - Check that the overridden function is actually used in the function you want to
///   change the behavior of. Much of the code has been manually inlined for performance.
abstract contract ERC20 {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The total supply has overflowed.
    error TotalSupplyOverflow();

    /// @dev The allowance has overflowed.
    error AllowanceOverflow();

    /// @dev The allowance has underflowed.
    error AllowanceUnderflow();

    /// @dev Insufficient balance.
    error InsufficientBalance();

    /// @dev Insufficient allowance.
    error InsufficientAllowance();

    /// @dev The permit is invalid.
    error InvalidPermit();

    /// @dev The permit has expired.
    error PermitExpired();

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                           EVENTS                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Emitted when `amount` tokens is transferred from `from` to `to`.
    event Transfer(address indexed from, address indexed to, uint256 amount);

    /// @dev Emitted when `amount` tokens is approved by `owner` to be used by `spender`.
    event Approval(address indexed owner, address indexed spender, uint256 amount);

    /// @dev `keccak256(bytes("Transfer(address,address,uint256)"))`.
    uint256 private constant _TRANSFER_EVENT_SIGNATURE =
        0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef;

    /// @dev `keccak256(bytes("Approval(address,address,uint256)"))`.
    uint256 private constant _APPROVAL_EVENT_SIGNATURE =
        0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                          STORAGE                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The storage slot for the total supply.
    uint256 private constant _TOTAL_SUPPLY_SLOT = 0x05345cdf77eb68f44c;

    /// @dev The balance slot of `owner` is given by:
    /// ```
    ///     mstore(0x0c, _BALANCE_SLOT_SEED)
    ///     mstore(0x00, owner)
    ///     let balanceSlot := keccak256(0x0c, 0x20)
    /// ```
    uint256 private constant _BALANCE_SLOT_SEED = 0x87a211a2;

    /// @dev The allowance slot of (`owner`, `spender`) is given by:
    /// ```
    ///     mstore(0x20, spender)
    ///     mstore(0x0c, _ALLOWANCE_SLOT_SEED)
    ///     mstore(0x00, owner)
    ///     let allowanceSlot := keccak256(0x0c, 0x34)
    /// ```
    uint256 private constant _ALLOWANCE_SLOT_SEED = 0x7f5e9f20;

    /// @dev The nonce slot of `owner` is given by:
    /// ```
    ///     mstore(0x0c, _NONCES_SLOT_SEED)
    ///     mstore(0x00, owner)
    ///     let nonceSlot := keccak256(0x0c, 0x20)
    /// ```
    uint256 private constant _NONCES_SLOT_SEED = 0x38377508;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         CONSTANTS                          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev `(_NONCES_SLOT_SEED << 16) | 0x1901`.
    uint256 private constant _NONCES_SLOT_SEED_WITH_SIGNATURE_PREFIX = 0x383775081901;

    /// @dev `keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")`.
    bytes32 private constant _DOMAIN_TYPEHASH =
        0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f;

    /// @dev `keccak256("1")`.
    bytes32 private constant _VERSION_HASH =
        0xc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6;

    /// @dev `keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)")`.
    bytes32 private constant _PERMIT_TYPEHASH =
        0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       ERC20 METADATA                       */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns the name of the token.
    function name() public view virtual returns (string memory);

    /// @dev Returns the symbol of the token.
    function symbol() public view virtual returns (string memory);

    /// @dev Returns the decimals places of the token.
    function decimals() public view virtual returns (uint8) {
        return 18;
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                           ERC20                            */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns the amount of tokens in existence.
    function totalSupply() public view virtual returns (uint256 result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := sload(_TOTAL_SUPPLY_SLOT)
        }
    }

    /// @dev Returns the amount of tokens owned by `owner`.
    function balanceOf(address owner) public view virtual returns (uint256 result) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x0c, _BALANCE_SLOT_SEED)
            mstore(0x00, owner)
            result := sload(keccak256(0x0c, 0x20))
        }
    }

    /// @dev Returns the amount of tokens that `spender` can spend on behalf of `owner`.
    function allowance(address owner, address spender)
        public
        view
        virtual
        returns (uint256 result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x20, spender)
            mstore(0x0c, _ALLOWANCE_SLOT_SEED)
            mstore(0x00, owner)
            result := sload(keccak256(0x0c, 0x34))
        }
    }

    /// @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
    ///
    /// Emits a {Approval} event.
    function approve(address spender, uint256 amount) public virtual returns (bool) {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute the allowance slot and store the amount.
            mstore(0x20, spender)
            mstore(0x0c, _ALLOWANCE_SLOT_SEED)
            mstore(0x00, caller())
            sstore(keccak256(0x0c, 0x34), amount)
            // Emit the {Approval} event.
            mstore(0x00, amount)
            log3(0x00, 0x20, _APPROVAL_EVENT_SIGNATURE, caller(), shr(96, mload(0x2c)))
        }
        return true;
    }

    /// @dev Transfer `amount` tokens from the caller to `to`.
    ///
    /// Requirements:
    /// - `from` must at least have `amount`.
    ///
    /// Emits a {Transfer} event.
    function transfer(address to, uint256 amount) public virtual returns (bool) {
        _beforeTokenTransfer(msg.sender, to, amount);
        /// @solidity memory-safe-assembly
        assembly {
            // Compute the balance slot and load its value.
            mstore(0x0c, _BALANCE_SLOT_SEED)
            mstore(0x00, caller())
            let fromBalanceSlot := keccak256(0x0c, 0x20)
            let fromBalance := sload(fromBalanceSlot)
            // Revert if insufficient balance.
            if gt(amount, fromBalance) {
                mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
                revert(0x1c, 0x04)
            }
            // Subtract and store the updated balance.
            sstore(fromBalanceSlot, sub(fromBalance, amount))
            // Compute the balance slot of `to`.
            mstore(0x00, to)
            let toBalanceSlot := keccak256(0x0c, 0x20)
            // Add and store the updated balance of `to`.
            // Will not overflow because the sum of all user balances
            // cannot exceed the maximum uint256 value.
            sstore(toBalanceSlot, add(sload(toBalanceSlot), amount))
            // Emit the {Transfer} event.
            mstore(0x20, amount)
            log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, caller(), shr(96, mload(0x0c)))
        }
        _afterTokenTransfer(msg.sender, to, amount);
        return true;
    }

    /// @dev Transfers `amount` tokens from `from` to `to`.
    ///
    /// Note: Does not update the allowance if it is the maximum uint256 value.
    ///
    /// Requirements:
    /// - `from` must at least have `amount`.
    /// - The caller must have at least `amount` of allowance to transfer the tokens of `from`.
    ///
    /// Emits a {Transfer} event.
    function transferFrom(address from, address to, uint256 amount) public virtual returns (bool) {
        _beforeTokenTransfer(from, to, amount);
        /// @solidity memory-safe-assembly
        assembly {
            let from_ := shl(96, from)
            // Compute the allowance slot and load its value.
            mstore(0x20, caller())
            mstore(0x0c, or(from_, _ALLOWANCE_SLOT_SEED))
            let allowanceSlot := keccak256(0x0c, 0x34)
            let allowance_ := sload(allowanceSlot)
            // If the allowance is not the maximum uint256 value.
            if add(allowance_, 1) {
                // Revert if the amount to be transferred exceeds the allowance.
                if gt(amount, allowance_) {
                    mstore(0x00, 0x13be252b) // `InsufficientAllowance()`.
                    revert(0x1c, 0x04)
                }
                // Subtract and store the updated allowance.
                sstore(allowanceSlot, sub(allowance_, amount))
            }
            // Compute the balance slot and load its value.
            mstore(0x0c, or(from_, _BALANCE_SLOT_SEED))
            let fromBalanceSlot := keccak256(0x0c, 0x20)
            let fromBalance := sload(fromBalanceSlot)
            // Revert if insufficient balance.
            if gt(amount, fromBalance) {
                mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
                revert(0x1c, 0x04)
            }
            // Subtract and store the updated balance.
            sstore(fromBalanceSlot, sub(fromBalance, amount))
            // Compute the balance slot of `to`.
            mstore(0x00, to)
            let toBalanceSlot := keccak256(0x0c, 0x20)
            // Add and store the updated balance of `to`.
            // Will not overflow because the sum of all user balances
            // cannot exceed the maximum uint256 value.
            sstore(toBalanceSlot, add(sload(toBalanceSlot), amount))
            // Emit the {Transfer} event.
            mstore(0x20, amount)
            log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, from_), shr(96, mload(0x0c)))
        }
        _afterTokenTransfer(from, to, amount);
        return true;
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                          EIP-2612                          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev For more performance, override to return the constant value
    /// of `keccak256(bytes(name()))` if `name()` will never change.
    function _constantNameHash() internal view virtual returns (bytes32 result) {}

    /// @dev Returns the current nonce for `owner`.
    /// This value is used to compute the signature for EIP-2612 permit.
    function nonces(address owner) public view virtual returns (uint256 result) {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute the nonce slot and load its value.
            mstore(0x0c, _NONCES_SLOT_SEED)
            mstore(0x00, owner)
            result := sload(keccak256(0x0c, 0x20))
        }
    }

    /// @dev Sets `value` as the allowance of `spender` over the tokens of `owner`,
    /// authorized by a signed approval by `owner`.
    ///
    /// Emits a {Approval} event.
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) public virtual {
        bytes32 nameHash = _constantNameHash();
        //  We simply calculate it on-the-fly to allow for cases where the `name` may change.
        if (nameHash == bytes32(0)) nameHash = keccak256(bytes(name()));
        /// @solidity memory-safe-assembly
        assembly {
            // Revert if the block timestamp greater than `deadline`.
            if gt(timestamp(), deadline) {
                mstore(0x00, 0x1a15a3cc) // `PermitExpired()`.
                revert(0x1c, 0x04)
            }
            let m := mload(0x40) // Grab the free memory pointer.
            // Clean the upper 96 bits.
            owner := shr(96, shl(96, owner))
            spender := shr(96, shl(96, spender))
            // Compute the nonce slot and load its value.
            mstore(0x0e, _NONCES_SLOT_SEED_WITH_SIGNATURE_PREFIX)
            mstore(0x00, owner)
            let nonceSlot := keccak256(0x0c, 0x20)
            let nonceValue := sload(nonceSlot)
            // Prepare the domain separator.
            mstore(m, _DOMAIN_TYPEHASH)
            mstore(add(m, 0x20), nameHash)
            mstore(add(m, 0x40), _VERSION_HASH)
            mstore(add(m, 0x60), chainid())
            mstore(add(m, 0x80), address())
            mstore(0x2e, keccak256(m, 0xa0))
            // Prepare the struct hash.
            mstore(m, _PERMIT_TYPEHASH)
            mstore(add(m, 0x20), owner)
            mstore(add(m, 0x40), spender)
            mstore(add(m, 0x60), value)
            mstore(add(m, 0x80), nonceValue)
            mstore(add(m, 0xa0), deadline)
            mstore(0x4e, keccak256(m, 0xc0))
            // Prepare the ecrecover calldata.
            mstore(0x00, keccak256(0x2c, 0x42))
            mstore(0x20, and(0xff, v))
            mstore(0x40, r)
            mstore(0x60, s)
            let t := staticcall(gas(), 1, 0, 0x80, 0x20, 0x20)
            // If the ecrecover fails, the returndatasize will be 0x00,
            // `owner` will be be checked if it equals the hash at 0x00,
            // which evaluates to false (i.e. 0), and we will revert.
            // If the ecrecover succeeds, the returndatasize will be 0x20,
            // `owner` will be compared against the returned address at 0x20.
            if iszero(eq(mload(returndatasize()), owner)) {
                mstore(0x00, 0xddafbaef) // `InvalidPermit()`.
                revert(0x1c, 0x04)
            }
            // Increment and store the updated nonce.
            sstore(nonceSlot, add(nonceValue, t)) // `t` is 1 if ecrecover succeeds.
            // Compute the allowance slot and store the value.
            // The `owner` is already at slot 0x20.
            mstore(0x40, or(shl(160, _ALLOWANCE_SLOT_SEED), spender))
            sstore(keccak256(0x2c, 0x34), value)
            // Emit the {Approval} event.
            log3(add(m, 0x60), 0x20, _APPROVAL_EVENT_SIGNATURE, owner, spender)
            mstore(0x40, m) // Restore the free memory pointer.
            mstore(0x60, 0) // Restore the zero pointer.
        }
    }

    /// @dev Returns the EIP-712 domain separator for the EIP-2612 permit.
    function DOMAIN_SEPARATOR() public view virtual returns (bytes32 result) {
        bytes32 nameHash = _constantNameHash();
        //  We simply calculate it on-the-fly to allow for cases where the `name` may change.
        if (nameHash == bytes32(0)) nameHash = keccak256(bytes(name()));
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Grab the free memory pointer.
            mstore(m, _DOMAIN_TYPEHASH)
            mstore(add(m, 0x20), nameHash)
            mstore(add(m, 0x40), _VERSION_HASH)
            mstore(add(m, 0x60), chainid())
            mstore(add(m, 0x80), address())
            result := keccak256(m, 0xa0)
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                  INTERNAL MINT FUNCTIONS                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Mints `amount` tokens to `to`, increasing the total supply.
    ///
    /// Emits a {Transfer} event.
    function _mint(address to, uint256 amount) internal virtual {
        _beforeTokenTransfer(address(0), to, amount);
        /// @solidity memory-safe-assembly
        assembly {
            let totalSupplyBefore := sload(_TOTAL_SUPPLY_SLOT)
            let totalSupplyAfter := add(totalSupplyBefore, amount)
            // Revert if the total supply overflows.
            if lt(totalSupplyAfter, totalSupplyBefore) {
                mstore(0x00, 0xe5cfe957) // `TotalSupplyOverflow()`.
                revert(0x1c, 0x04)
            }
            // Store the updated total supply.
            sstore(_TOTAL_SUPPLY_SLOT, totalSupplyAfter)
            // Compute the balance slot and load its value.
            mstore(0x0c, _BALANCE_SLOT_SEED)
            mstore(0x00, to)
            let toBalanceSlot := keccak256(0x0c, 0x20)
            // Add and store the updated balance.
            sstore(toBalanceSlot, add(sload(toBalanceSlot), amount))
            // Emit the {Transfer} event.
            mstore(0x20, amount)
            log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, 0, shr(96, mload(0x0c)))
        }
        _afterTokenTransfer(address(0), to, amount);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                  INTERNAL BURN FUNCTIONS                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Burns `amount` tokens from `from`, reducing the total supply.
    ///
    /// Emits a {Transfer} event.
    function _burn(address from, uint256 amount) internal virtual {
        _beforeTokenTransfer(from, address(0), amount);
        /// @solidity memory-safe-assembly
        assembly {
            // Compute the balance slot and load its value.
            mstore(0x0c, _BALANCE_SLOT_SEED)
            mstore(0x00, from)
            let fromBalanceSlot := keccak256(0x0c, 0x20)
            let fromBalance := sload(fromBalanceSlot)
            // Revert if insufficient balance.
            if gt(amount, fromBalance) {
                mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
                revert(0x1c, 0x04)
            }
            // Subtract and store the updated balance.
            sstore(fromBalanceSlot, sub(fromBalance, amount))
            // Subtract and store the updated total supply.
            sstore(_TOTAL_SUPPLY_SLOT, sub(sload(_TOTAL_SUPPLY_SLOT), amount))
            // Emit the {Transfer} event.
            mstore(0x00, amount)
            log3(0x00, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, shl(96, from)), 0)
        }
        _afterTokenTransfer(from, address(0), amount);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                INTERNAL TRANSFER FUNCTIONS                 */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Moves `amount` of tokens from `from` to `to`.
    function _transfer(address from, address to, uint256 amount) internal virtual {
        _beforeTokenTransfer(from, to, amount);
        /// @solidity memory-safe-assembly
        assembly {
            let from_ := shl(96, from)
            // Compute the balance slot and load its value.
            mstore(0x0c, or(from_, _BALANCE_SLOT_SEED))
            let fromBalanceSlot := keccak256(0x0c, 0x20)
            let fromBalance := sload(fromBalanceSlot)
            // Revert if insufficient balance.
            if gt(amount, fromBalance) {
                mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
                revert(0x1c, 0x04)
            }
            // Subtract and store the updated balance.
            sstore(fromBalanceSlot, sub(fromBalance, amount))
            // Compute the balance slot of `to`.
            mstore(0x00, to)
            let toBalanceSlot := keccak256(0x0c, 0x20)
            // Add and store the updated balance of `to`.
            // Will not overflow because the sum of all user balances
            // cannot exceed the maximum uint256 value.
            sstore(toBalanceSlot, add(sload(toBalanceSlot), amount))
            // Emit the {Transfer} event.
            mstore(0x20, amount)
            log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, from_), shr(96, mload(0x0c)))
        }
        _afterTokenTransfer(from, to, amount);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                INTERNAL ALLOWANCE FUNCTIONS                */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Updates the allowance of `owner` for `spender` based on spent `amount`.
    function _spendAllowance(address owner, address spender, uint256 amount) internal virtual {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute the allowance slot and load its value.
            mstore(0x20, spender)
            mstore(0x0c, _ALLOWANCE_SLOT_SEED)
            mstore(0x00, owner)
            let allowanceSlot := keccak256(0x0c, 0x34)
            let allowance_ := sload(allowanceSlot)
            // If the allowance is not the maximum uint256 value.
            if add(allowance_, 1) {
                // Revert if the amount to be transferred exceeds the allowance.
                if gt(amount, allowance_) {
                    mstore(0x00, 0x13be252b) // `InsufficientAllowance()`.
                    revert(0x1c, 0x04)
                }
                // Subtract and store the updated allowance.
                sstore(allowanceSlot, sub(allowance_, amount))
            }
        }
    }

    /// @dev Sets `amount` as the allowance of `spender` over the tokens of `owner`.
    ///
    /// Emits a {Approval} event.
    function _approve(address owner, address spender, uint256 amount) internal virtual {
        /// @solidity memory-safe-assembly
        assembly {
            let owner_ := shl(96, owner)
            // Compute the allowance slot and store the amount.
            mstore(0x20, spender)
            mstore(0x0c, or(owner_, _ALLOWANCE_SLOT_SEED))
            sstore(keccak256(0x0c, 0x34), amount)
            // Emit the {Approval} event.
            mstore(0x00, amount)
            log3(0x00, 0x20, _APPROVAL_EVENT_SIGNATURE, shr(96, owner_), shr(96, mload(0x2c)))
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                     HOOKS TO OVERRIDE                      */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Hook that is called before any transfer of tokens.
    /// This includes minting and burning.
    function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual {}

    /// @dev Hook that is called after any transfer of tokens.
    /// This includes minting and burning.
    function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {}
}

File 19 of 22 : IBlast.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.18;

enum YieldMode {
    AUTOMATIC,
    VOID,
    CLAIMABLE
}

enum GasMode {
    VOID,
    CLAIMABLE
}

interface IBlast {
    // configure
    function configureContract(address contractAddress, YieldMode _yield, GasMode gasMode, address governor) external;
    function configure(YieldMode _yield, GasMode gasMode, address governor) external;

    // base configuration options
    function configureClaimableYield() external;
    function configureClaimableYieldOnBehalf(address contractAddress) external;
    function configureAutomaticYield() external;
    function configureAutomaticYieldOnBehalf(address contractAddress) external;
    function configureVoidYield() external;
    function configureVoidYieldOnBehalf(address contractAddress) external;
    function configureClaimableGas() external;
    function configureClaimableGasOnBehalf(address contractAddress) external;
    function configureVoidGas() external;
    function configureVoidGasOnBehalf(address contractAddress) external;
    function configureGovernor(address _governor) external;
    function configureGovernorOnBehalf(address _newGovernor, address contractAddress) external;

    // claim yield
    function claimYield(address contractAddress, address recipientOfYield, uint256 amount) external returns (uint256);
    function claimAllYield(address contractAddress, address recipientOfYield) external returns (uint256);

    // claim gas
    function claimAllGas(address contractAddress, address recipientOfGas) external returns (uint256);
    function claimGasAtMinClaimRate(
        address contractAddress,
        address recipientOfGas,
        uint256 minClaimRateBips
    )
        external
        returns (uint256);
    function claimMaxGas(address contractAddress, address recipientOfGas) external returns (uint256);
    function claimGas(
        address contractAddress,
        address recipientOfGas,
        uint256 gasToClaim,
        uint256 gasSecondsToConsume
    )
        external
        returns (uint256);

    // read functions
    function readClaimableYield(address contractAddress) external view returns (uint256);
    function readYieldConfiguration(address contractAddress) external view returns (uint8);
    function readGasParams(address contractAddress)
        external
        view
        returns (uint256 etherSeconds, uint256 etherBalance, uint256 lastUpdated, GasMode);
}

File 20 of 22 : Math.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)

pragma solidity ^0.8.0;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    enum Rounding {
        Down, // Toward negative infinity
        Up, // Toward infinity
        Zero // Toward zero
    }

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

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

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

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds up instead
     * of rounding down.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a == 0 ? 0 : (a - 1) / b + 1;
    }

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

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

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            require(denominator > prod1, "Math: mulDiv overflow");

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

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

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

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

            // Does not overflow because the denominator cannot be zero at this stage in the function.
            uint256 twos = denominator & (~denominator + 1);
            assembly {
                // Divide denominator by twos.
                denominator := div(denominator, twos)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

File 21 of 22 : SignedMath.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol)

pragma solidity ^0.8.0;

/**
 * @dev Standard signed math utilities missing in the Solidity language.
 */
library SignedMath {
    /**
     * @dev Returns the largest of two signed numbers.
     */
    function max(int256 a, int256 b) internal pure returns (int256) {
        return a > b ? a : b;
    }

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

    /**
     * @dev Returns the average of two signed numbers without overflow.
     * The result is rounded towards zero.
     */
    function average(int256 a, int256 b) internal pure returns (int256) {
        // Formula from the book "Hacker's Delight"
        int256 x = (a & b) + ((a ^ b) >> 1);
        return x + (int256(uint256(x) >> 255) & (a ^ b));
    }

    /**
     * @dev Returns the absolute unsigned value of a signed value.
     */
    function abs(int256 n) internal pure returns (uint256) {
        unchecked {
            // must be unchecked in order to support `n = type(int256).min`
            return uint256(n >= 0 ? n : -n);
        }
    }
}

File 22 of 22 : StorageSlot.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/StorageSlot.sol)
// This file was procedurally generated from scripts/generate/templates/StorageSlot.js.

pragma solidity ^0.8.0;

/**
 * @dev Library for reading and writing primitive types to specific storage slots.
 *
 * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
 * This library helps with reading and writing to such slots without the need for inline assembly.
 *
 * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
 *
 * Example usage to set ERC1967 implementation slot:
 * ```solidity
 * contract ERC1967 {
 *     bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
 *
 *     function _getImplementation() internal view returns (address) {
 *         return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
 *     }
 *
 *     function _setImplementation(address newImplementation) internal {
 *         require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
 *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
 *     }
 * }
 * ```
 *
 * _Available since v4.1 for `address`, `bool`, `bytes32`, `uint256`._
 * _Available since v4.9 for `string`, `bytes`._
 */
library StorageSlot {
    struct AddressSlot {
        address value;
    }

    struct BooleanSlot {
        bool value;
    }

    struct Bytes32Slot {
        bytes32 value;
    }

    struct Uint256Slot {
        uint256 value;
    }

    struct StringSlot {
        string value;
    }

    struct BytesSlot {
        bytes value;
    }

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

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

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

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

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

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

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

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

Settings
{
  "remappings": [
    "ds-test/=contracts/lib/forge-std/lib/ds-test/src/",
    "erc4626-tests/=contracts/lib/openzeppelin-contracts/lib/erc4626-tests/",
    "forge-std/=contracts/lib/forge-std/src/",
    "openzeppelin-contracts/=contracts/lib/openzeppelin-contracts/contracts/",
    "solady/=contracts/lib/solady/src/",
    "openzeppelin/=contracts/lib/openzeppelin-contracts/contracts/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 10000
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "paris",
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"address","name":"_judge","type":"address"},{"internalType":"address","name":"_withdrawJudge","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"BattleAlreadyClaimed","type":"error"},{"inputs":[],"name":"BattleAlreadyMatched","type":"error"},{"inputs":[],"name":"BattleAlreadyNullified","type":"error"},{"inputs":[],"name":"BattleNotMatched","type":"error"},{"inputs":[],"name":"BattleSelfForbidden","type":"error"},{"inputs":[],"name":"ClaimFromNonParticipant","type":"error"},{"inputs":[],"name":"EthFeeNumerTooHigh","type":"error"},{"inputs":[],"name":"EthStakeMustMatchMsgValue","type":"error"},{"inputs":[],"name":"EthStakeNotUnique","type":"error"},{"inputs":[],"name":"ExceedsMaximumUniqueStakes","type":"error"},{"inputs":[],"name":"ExistingStakes","type":"error"},{"inputs":[],"name":"FeeTooLow","type":"error"},{"inputs":[],"name":"ForgedSignature","type":"error"},{"inputs":[],"name":"ForgedWithdrawSignature","type":"error"},{"inputs":[],"name":"InvalidShortString","type":"error"},{"inputs":[],"name":"MustHaveStake","type":"error"},{"inputs":[],"name":"NotAllowedParticipant","type":"error"},{"inputs":[],"name":"OnlyInitiatorCanNullify","type":"error"},{"inputs":[{"internalType":"string","name":"str","type":"string"}],"name":"StringTooLong","type":"error"},{"inputs":[],"name":"TransferFailed","type":"error"},{"inputs":[],"name":"UniqueJudge","type":"error"},{"inputs":[],"name":"UserAlreadyWithdrawn","type":"error"},{"inputs":[],"name":"WithdrawFromNonParticipant","type":"error"},{"inputs":[],"name":"ZeroAddressNotAllowed","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"battleId","type":"uint256"},{"indexed":true,"internalType":"address","name":"caller","type":"address"}],"name":"BattleCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"battleId","type":"uint256"},{"indexed":true,"internalType":"address","name":"playerOne","type":"address"},{"indexed":true,"internalType":"address","name":"playerTwo","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"assetEnum","type":"uint256[]"},{"indexed":false,"internalType":"address[]","name":"contractAddr","type":"address[]"},{"indexed":false,"internalType":"uint256[]","name":"amtOrTokenId","type":"uint256[]"}],"name":"BattleInitialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"battleId","type":"uint256"},{"indexed":true,"internalType":"address","name":"playerOne","type":"address"},{"indexed":true,"internalType":"address","name":"playerTwo","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"assetEnum","type":"uint256[]"},{"indexed":false,"internalType":"address[]","name":"contractAddr","type":"address[]"},{"indexed":false,"internalType":"uint256[]","name":"amtOrTokenId","type":"uint256[]"}],"name":"BattleJoined","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"battleId","type":"uint256"},{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"address","name":"counterparty","type":"address"}],"name":"BattleNullified","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"battleId","type":"uint256"},{"indexed":true,"internalType":"address","name":"winner","type":"address"}],"name":"BattleWon","type":"event"},{"anonymous":false,"inputs":[],"name":"EIP712DomainChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"ethFeeNumer","type":"uint256"}],"name":"EthFeeNumerChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"feeChanged","type":"uint256"}],"name":"FeeChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"newJudge","type":"address"}],"name":"JudgeChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"newWithdrawJudge","type":"address"}],"name":"WithdrawJudgeChanged","type":"event"},{"inputs":[],"name":"BLAST","outputs":[{"internalType":"contract IBlast","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"battles","outputs":[{"internalType":"bool","name":"claimed","type":"bool"},{"internalType":"bool","name":"matched","type":"bool"},{"internalType":"bool","name":"nullified","type":"bool"},{"internalType":"uint256","name":"value","type":"uint256"},{"components":[{"internalType":"bool","name":"isWinner","type":"bool"},{"internalType":"bool","name":"hasWithdrawn","type":"bool"},{"internalType":"address","name":"user","type":"address"},{"components":[{"internalType":"enum IBatchEscrow.AssetType","name":"asset","type":"uint8"},{"internalType":"address","name":"contractAddr","type":"address"},{"internalType":"uint256","name":"amtOrTokenId","type":"uint256"}],"internalType":"struct IBatchEscrow.BatchStake[]","name":"stakes","type":"tuple[]"}],"internalType":"struct IBatchBattle.BattleParticipant","name":"one","type":"tuple"},{"components":[{"internalType":"bool","name":"isWinner","type":"bool"},{"internalType":"bool","name":"hasWithdrawn","type":"bool"},{"internalType":"address","name":"user","type":"address"},{"components":[{"internalType":"enum IBatchEscrow.AssetType","name":"asset","type":"uint8"},{"internalType":"address","name":"contractAddr","type":"address"},{"internalType":"uint256","name":"amtOrTokenId","type":"uint256"}],"internalType":"struct IBatchEscrow.BatchStake[]","name":"stakes","type":"tuple[]"}],"internalType":"struct IBatchBattle.BattleParticipant","name":"two","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"battleId","type":"uint256"}],"name":"calcEthFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"feeNumer","type":"uint256"}],"name":"changeEthFeeNumer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"fee","type":"uint256"}],"name":"changeFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newJudge","type":"address"}],"name":"changeJudge","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newWithdrawJudge","type":"address"}],"name":"changeWithdrawJudge","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"battleId","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"claimProceeds","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"eip712Domain","outputs":[{"internalType":"bytes1","name":"fields","type":"bytes1"},{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"version","type":"string"},{"internalType":"uint256","name":"chainId","type":"uint256"},{"internalType":"address","name":"verifyingContract","type":"address"},{"internalType":"bytes32","name":"salt","type":"bytes32"},{"internalType":"uint256[]","name":"extensions","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"battleId","type":"uint256"}],"name":"emergencyRefund","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address payable","name":"payee","type":"address"}],"name":"emergencyWithdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address payable","name":"payee","type":"address"},{"internalType":"address","name":"token","type":"address"}],"name":"emergencyWithdrawERC20","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address payable","name":"payee","type":"address"},{"internalType":"address","name":"nft","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"emergencyWithdrawERC721","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"escrowAddr","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ethFeeNumer","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"battleId","type":"uint256"}],"name":"getBattle","outputs":[{"components":[{"internalType":"bool","name":"claimed","type":"bool"},{"internalType":"bool","name":"matched","type":"bool"},{"internalType":"bool","name":"nullified","type":"bool"},{"internalType":"uint256","name":"value","type":"uint256"},{"components":[{"internalType":"bool","name":"isWinner","type":"bool"},{"internalType":"bool","name":"hasWithdrawn","type":"bool"},{"internalType":"address","name":"user","type":"address"},{"components":[{"internalType":"enum IBatchEscrow.AssetType","name":"asset","type":"uint8"},{"internalType":"address","name":"contractAddr","type":"address"},{"internalType":"uint256","name":"amtOrTokenId","type":"uint256"}],"internalType":"struct IBatchEscrow.BatchStake[]","name":"stakes","type":"tuple[]"}],"internalType":"struct IBatchBattle.BattleParticipant","name":"one","type":"tuple"},{"components":[{"internalType":"bool","name":"isWinner","type":"bool"},{"internalType":"bool","name":"hasWithdrawn","type":"bool"},{"internalType":"address","name":"user","type":"address"},{"components":[{"internalType":"enum IBatchEscrow.AssetType","name":"asset","type":"uint8"},{"internalType":"address","name":"contractAddr","type":"address"},{"internalType":"uint256","name":"amtOrTokenId","type":"uint256"}],"internalType":"struct IBatchEscrow.BatchStake[]","name":"stakes","type":"tuple[]"}],"internalType":"struct IBatchBattle.BattleParticipant","name":"two","type":"tuple"}],"internalType":"struct IBatchBattle.Battle","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"battleId","type":"uint256"},{"internalType":"address","name":"account","type":"address"}],"internalType":"struct IBatchBattle.Claim","name":"claim","type":"tuple"}],"name":"getClaimTypedDataHash","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"challenging","type":"address"},{"components":[{"internalType":"enum IBatchEscrow.AssetType","name":"asset","type":"uint8"},{"internalType":"address","name":"contractAddr","type":"address"},{"internalType":"uint256","name":"amtOrTokenId","type":"uint256"}],"internalType":"struct IBatchEscrow.BatchStake[]","name":"stakes","type":"tuple[]"}],"name":"initBattle","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"battleId","type":"uint256"},{"components":[{"internalType":"enum IBatchEscrow.AssetType","name":"asset","type":"uint8"},{"internalType":"address","name":"contractAddr","type":"address"},{"internalType":"uint256","name":"amtOrTokenId","type":"uint256"}],"internalType":"struct IBatchEscrow.BatchStake[]","name":"stakes","type":"tuple[]"}],"name":"joinBattle","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"judge","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nextBattleId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"battleId","type":"uint256"}],"name":"nullifyBattle","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC721Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"withdrawJudge","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"battleId","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"withdrawStake","outputs":[],"stateMutability":"nonpayable","type":"function"}]

6101a06040523480156200001257600080fd5b5060405162006aa138038062006aa18339810160408190526200003591620004a8565b6040518060400160405280601081526020016f43616d627269614475656c4172656e6160801b815250604051806040016040528060018152602001603160f81b81525060016000819055506200009a620000946200038860201b60201c565b6200038c565b620000b5600283620003de60201b620032891790919060201c565b61012052620000d2816003620003de602090811b6200328917901c565b61014052815160208084019190912060e052815190820120610100524660a0526200016060e05161010051604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60208201529081019290925260608201524660808201523060a082015260009060c00160405160208183030381529060405280519060200120905090565b60805250503060c0526001600160a01b03831615806200018757506001600160a01b038216155b806200019a57506001600160a01b038116155b15620001b9576040516342bcdf7f60e11b815260040160405180910390fd5b82604051620001c8906200047d565b6001600160a01b039091168152602001604051809103906000f080158015620001f5573d6000803e3d6000fd5b506001600160a01b0390811661016081905261018052600480546001600160a01b031990811685841617825560058054909116928416929092179091556040805163784c3b3d60e11b815290517343000000000000000000000000000000000000029263f098767a9280820192600092909182900301818387803b1580156200027d57600080fd5b505af115801562000292573d6000803e3d6000fd5b505050507343000000000000000000000000000000000000026001600160a01b0316634e606c476040518163ffffffff1660e01b8152600401600060405180830381600087803b158015620002e657600080fd5b505af1158015620002fb573d6000803e3d6000fd5b5050604051631d70c8d360e31b81526001600160a01b0386166004820152734300000000000000000000000000000000000002925063eb8646989150602401600060405180830381600087803b1580156200035557600080fd5b505af11580156200036a573d6000803e3d6000fd5b505050506200037f836200038c60201b60201c565b505050620006d8565b3390565b600180546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b6000602083511015620003fe57620003f6836200042e565b905062000428565b8262000415836200047a60201b620032ba1760201c565b9062000422908262000597565b5060ff90505b92915050565b600080829050601f8151111562000465578260405163305a27a960e01b81526004016200045c919062000663565b60405180910390fd5b80516200047282620006b3565b179392505050565b90565b6119ad80620050f483390190565b80516001600160a01b0381168114620004a357600080fd5b919050565b600080600060608486031215620004be57600080fd5b620004c9846200048b565b9250620004d9602085016200048b565b9150620004e9604085016200048b565b90509250925092565b634e487b7160e01b600052604160045260246000fd5b600181811c908216806200051d57607f821691505b6020821081036200053e57634e487b7160e01b600052602260045260246000fd5b50919050565b601f8211156200059257600081815260208120601f850160051c810160208610156200056d5750805b601f850160051c820191505b818110156200058e5782815560010162000579565b5050505b505050565b81516001600160401b03811115620005b357620005b3620004f2565b620005cb81620005c4845462000508565b8462000544565b602080601f831160018114620006035760008415620005ea5750858301515b600019600386901b1c1916600185901b1785556200058e565b600085815260208120601f198616915b82811015620006345788860151825594840194600190910190840162000613565b5085821015620006535787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b600060208083528351808285015260005b81811015620006925785810183015185820160400152820162000674565b506000604082860101526040601f19601f8301168501019250505092915050565b805160208083015191908110156200053e5760001960209190910360031b1b16919050565b60805160a05160c05160e05161010051610120516101405161016051610180516149576200079d6000396000818161045301528181610b8901528181610cfe0152818161281b0152612981015260008181610db801528181611833015281816118e401528181611b56015281816122880152818161233701528181612a3b0152818161311d01526131cd01526000611bbe01526000611b93015260006138f5015260006138cd01526000613828015260006138520152600061387c01526149576000f3fe6080604052600436106101c25760003560e01c806380d9c5f3116100f7578063b7a2a78111610095578063e0caf57811610064578063e0caf57814610557578063e625215c14610577578063e941fa7814610597578063f2fde38b146105ad57600080fd5b8063b7a2a781146104f1578063ba12121714610504578063cdd7eb5214610524578063ce55803f1461054457600080fd5b80638da5cb5b116100d15780638da5cb5b1461047557806397d7577614610493578063b5462347146104bb578063b72b85a1146104d157600080fd5b806380d9c5f31461040657806384b0196e146104195780638b6cf6681461044157600080fd5b8063606d2e96116101645780636ff1c9bc1161013e5780636ff1c9bc14610391578063701b1e5a146103b157806370e5deb9146103d1578063715018a6146103f157600080fd5b8063606d2e96146103125780636a1db1bf146103445780636dd0ce7a1461036457600080fd5b8063297bf1a5116101a0578063297bf1a5146102a757806331a6ff66146102c957806337d8b79d146102df578063573255f4146102f257600080fd5b8063150b7a02146101c7578063160d6f3c146102415780632828eea814610279575b600080fd5b3480156101d357600080fd5b5061020b6101e2366004613d7b565b7f150b7a0200000000000000000000000000000000000000000000000000000000949350505050565b6040517fffffffff0000000000000000000000000000000000000000000000000000000090911681526020015b60405180910390f35b34801561024d57600080fd5b50600554610261906001600160a01b031681565b6040516001600160a01b039091168152602001610238565b34801561028557600080fd5b50610299610294366004613de7565b6105cd565b604051908152602001610238565b3480156102b357600080fd5b506102c76102c2366004613e00565b61066d565b005b3480156102d557600080fd5b5061029960065481565b6102c76102ed366004613e7e565b610760565b3480156102fe57600080fd5b50600454610261906001600160a01b031681565b34801561031e57600080fd5b5061033261032d366004613de7565b610ec6565b60405161023896959493929190613fcd565b34801561035057600080fd5b506102c761035f366004613de7565b6110ec565b34801561037057600080fd5b5061038461037f366004613de7565b611127565b604051610238919061401d565b34801561039d57600080fd5b506102c76103ac36600461408d565b6113d5565b3480156103bd57600080fd5b506102c76103cc36600461408d565b6114c1565b3480156103dd57600080fd5b506102c76103ec366004613de7565b6115b3565b3480156103fd57600080fd5b506102c761194d565b6102c7610414366004613de7565b611961565b34801561042557600080fd5b5061042e611b85565b604051610238979695949392919061412b565b34801561044d57600080fd5b506102617f000000000000000000000000000000000000000000000000000000000000000081565b34801561048157600080fd5b506001546001600160a01b0316610261565b34801561049f57600080fd5b5061026173430000000000000000000000000000000000000281565b3480156104c757600080fd5b5061029960085481565b3480156104dd57600080fd5b506102c76104ec3660046141b5565b611c2a565b6102c76104ff3660046141f6565b611d0e565b34801561051057600080fd5b506102c761051f366004613de7565b6123a1565b34801561053057600080fd5b5061029961053f36600461423d565b612417565b610299610552366004614293565b61248f565b34801561056357600080fd5b506102c761057236600461408d565b612b44565b34801561058357600080fd5b506102c76105923660046141f6565b612c36565b3480156105a357600080fd5b5061029960075481565b3480156105b957600080fd5b506102c76105c836600461408d565b6131fc565b60008160065481106106265760405162461bcd60e51b815260206004820152601260248201527f426174746c65206d75737420657869737421000000000000000000000000000060448201526064015b60405180910390fd5b600083815260096020526040902060010154600854610649576000925050610667565b61271060085461065990836142fe565b6106639190614315565b9250505b50919050565b6106756132bd565b61067d613317565b6001600160a01b0382166106bd576040517f8579befe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015281906000906001600160a01b038316906370a0823190602401602060405180830381865afa15801561071f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107439190614350565b9050610750828583613370565b505061075c6001600055565b5050565b8260065481106107b25760405162461bcd60e51b815260206004820152601260248201527f426174746c65206d757374206578697374210000000000000000000000000000604482015260640161061d565b6107ba613317565b81806107f2576040517f071e948200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600881111561082d576040517f4f87596100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000858152600960205260408120805434929190610100900460ff1615610880576040517f864a886500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b805462010000900460ff16156108c2576040517fc3d8a04200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60048101546201000090046001600160a01b0316331461090e576040517f87b1f39900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000888152600960205260409020600501805415610958576040517f75fd3c5400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b858110156109b9578189898381811061097657610976614369565b8354600181018555600094855260209094206060909102929092019260020290910190506109a482826143a5565b505080806109b19061445a565b91505061095b565b50600089815260096020526040902081546109d991600501908390613a50565b506000898152600960205260408120805461ff0019166101001781556001018054869290610a08908490614492565b90915550600090505b85811015610db0576000898983818110610a2d57610a2d614369565b610a4392602060609092020190810191506144a5565b6002811115610a5457610a54613eca565b03610aee57888882818110610a6b57610a6b614369565b905060600201604001358514610aad576040517ff601a14400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8315610ae5576040517ff544b27200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60019350610d9e565b6001898983818110610b0257610b02614369565b610b1892602060609092020190810191506144a5565b6002811115610b2957610b29613eca565b03610bcf576000898983818110610b4257610b42614369565b9050606002016020016020810190610b5a919061408d565b9050610b838133308d8d87818110610b7457610b74614369565b905060600201604001356133bf565b610bc9817f00000000000000000000000000000000000000000000000000000000000000008c8c86818110610bba57610bba614369565b9050606002016040013561341c565b50610d9e565b6002898983818110610be357610be3614369565b610bf992602060609092020190810191506144a5565b6002811115610c0a57610c0a613eca565b03610d9e576000898983818110610c2357610c23614369565b9050606002016020016020810190610c3b919061408d565b9050806001600160a01b03166342842e0e33308d8d87818110610c6057610c60614369565b604080517fffffffff0000000000000000000000000000000000000000000000000000000060e089901b1681526001600160a01b039687166004820152959094166024860152606002919091019190910135604483015250606401600060405180830381600087803b158015610cd557600080fd5b505af1158015610ce9573d6000803e3d6000fd5b50505050806001600160a01b031663095ea7b37f00000000000000000000000000000000000000000000000000000000000000008c8c86818110610d2f57610d2f614369565b905060600201604001356040518363ffffffff1660e01b8152600401610d6a9291906001600160a01b03929092168252602082015260400190565b600060405180830381600087803b158015610d8457600080fd5b505af1158015610d98573d6000803e3d6000fd5b50505050505b80610da88161445a565b915050610a11565b5060008060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166349373a1988338f8f8f6040518663ffffffff1660e01b8152600401610e0994939291906144c2565b60006040518083038185885af1158015610e27573d6000803e3d6000fd5b50505050506040513d6000823e601f3d908101601f19168201604052610e5091908101906145dc565b600288015460405193965091945092503391620100009091046001600160a01b0316908e907f09afbfed4f33a53c1bff738c8909e5f07a302af749605de0b786f92e40be97f890610ea6908890889088906146c2565b60405180910390a45050505050505050610ec06001600055565b50505050565b600960209081526000918252604080832080546001820154835160808101855260028401805460ff8181161515845261010080830482161515858b01526001600160a01b03620100009384900416858a01526003880180548a51818d0281018d01909b52808b528389169c92890484169b94909804909216989597969495939460608701949390919084015b82821015610fe257838290600052602060002090600202016040518060600160405290816000820160009054906101000a900460ff166002811115610f9957610f99613eca565b6002811115610faa57610faa613eca565b8152815461010090046001600160a01b0316602080830191909152600192830154604090920191909152918352929092019101610f52565b505050915250506040805160808101825260048401805460ff808216151584526101008204161515602080850191909152620100009091046001600160a01b0316838501526005860180548551818402810184019096528086529596959394929360608601939260009084015b828210156110df57838290600052602060002090600202016040518060600160405290816000820160009054906101000a900460ff16600281111561109657611096613eca565b60028111156110a7576110a7613eca565b8152815461010090046001600160a01b031660208083019190915260019283015460409092019190915291835292909201910161104f565b5050505081525050905086565b6110f46132bd565b600781905560405181907f6bbc57480a46553fa4d156ce702beef5f3ad66303b0ed1a5d4cb44966c6584c390600090a250565b61112f613b15565b8160065481106111815760405162461bcd60e51b815260206004820152601260248201527f426174746c65206d757374206578697374210000000000000000000000000000604482015260640161061d565b6000838152600960209081526040808320815160c081018352815460ff808216151583526101008083048216151584880152620100009283900482161515848701526001850154606080860191909152865160808181018952600288018054808716151584529485049095161515828b01526001600160a01b039590930494909416848801526003860180548851818b0281018b019099528089529598969792890196949593949186019390918a9084015b828210156112c357838290600052602060002090600202016040518060600160405290816000820160009054906101000a900460ff16600281111561127a5761127a613eca565b600281111561128b5761128b613eca565b8152815461010090046001600160a01b0316602080830191909152600192830154604090920191909152918352929092019101611233565b5050509152505081526040805160808101825260048401805460ff808216151584526101008204161515602080850191909152620100009091046001600160a01b031683850152600586018054855181840281018401909652808652958201959394929360608601939260009084015b828210156113c357838290600052602060002090600202016040518060600160405290816000820160009054906101000a900460ff16600281111561137a5761137a613eca565b600281111561138b5761138b613eca565b8152815461010090046001600160a01b0316602080830191909152600192830154604090920191909152918352929092019101611333565b50505091525050905250949350505050565b6113dd6132bd565b6113e5613317565b6001600160a01b038116611425576040517f8579befe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60405147906000906001600160a01b0384169083908381818185875af1925050503d8060008114611472576040519150601f19603f3d011682016040523d82523d6000602084013e611477565b606091505b50509050806114b2576040517f90b8ec1800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50506114be6001600055565b50565b6114c96132bd565b6001600160a01b038116611509576040517f8579befe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6004546001600160a01b0390811690821603611551576040517f62dd97a500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600580547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383169081179091556040517fb577a105c65e3df7015d800e52e963b9a45d2ccf117aded28201271748c8832490600090a250565b8060065481106116055760405162461bcd60e51b815260206004820152601260248201527f426174746c65206d757374206578697374210000000000000000000000000000604482015260640161061d565b61160d6132bd565b611615613317565b6000828152600960205260409020805460ff1661194257805460ff1990811660019081178355600085815260096020526040902080548084168317825584546101009081900460ff9081161515820261ffff19938416178517808555875462010000908190048316151581027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffff90921691909117855587860154958501959095556002808801805491860180549284161515988316891781558154859004909316151590930261ff0019979097169316929092179490941780825584546001600160a01b0390859004169093027fffffffffffffffffffff0000000000000000000000000000000000000000ffff9093169290921782556003808501805486959394926117459290860191613ba3565b50505060048281018054918301805460ff938416151560ff19821681178355835461010090819004909516151590940261ff001990941661ffff19909116179290921780835581546001600160a01b03620100009182900416027fffffffffffffffffffff0000000000000000000000000000000000000000ffff909116178255600580850180549293926117dd9286019190613ba3565b5050505060028201546040517fd9caed12000000000000000000000000000000000000000000000000000000008152620100009091046001600160a01b03908116600483018190526024830152604482018690527f000000000000000000000000000000000000000000000000000000000000000016915063d9caed1290606401600060405180830381600087803b15801561187857600080fd5b505af115801561188c573d6000803e3d6000fd5b505050506004818101546040517fd9caed120000000000000000000000000000000000000000000000000000000081526001600160a01b036201000090920482169281018390526024810192909252604482018590527f0000000000000000000000000000000000000000000000000000000000000000169063d9caed12906064015b600060405180830381600087803b15801561192957600080fd5b505af115801561193d573d6000803e3d6000fd5b505050505b5061075c6001600055565b6119556132bd565b61195f6000613461565b565b8060065481106119b35760405162461bcd60e51b815260206004820152601260248201527f426174746c65206d757374206578697374210000000000000000000000000000604482015260640161061d565b6119bb613317565b60008281526009602052604090208054610100900460ff1615611a0a576040517f864a886500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b805462010000900460ff1615611a4c576040517fc3d8a04200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60028101546201000090046001600160a01b03163314611a98576040517fb3153ae600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8054620100007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffff9091168117825560048201546040519190046001600160a01b031690339085907f1adcfbcfae7625b5bbaf696c3898a3f28b8d91300b9f0950d5a72abf3af79fc490600090a460028101546040517fd9caed12000000000000000000000000000000000000000000000000000000008152620100009091046001600160a01b039081166004830152336024830152604482018590527f0000000000000000000000000000000000000000000000000000000000000000169063d9caed129060640161190f565b600060608082808083611bb97f000000000000000000000000000000000000000000000000000000000000000060026134cb565b611be47f000000000000000000000000000000000000000000000000000000000000000060036134cb565b604080516000808252602082019092527f0f000000000000000000000000000000000000000000000000000000000000009b939a50919850469750309650945092509050565b611c326132bd565b611c3a613317565b6001600160a01b038316611c7a576040517f8579befe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f42842e0e0000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b038481166024830152604482018390528391908216906342842e0e90606401600060405180830381600087803b158015611ce657600080fd5b505af1158015611cfa573d6000803e3d6000fd5b5050505050611d096001600055565b505050565b611d16613317565b816006548110611d685760405162461bcd60e51b815260206004820152601260248201527f426174746c65206d757374206578697374210000000000000000000000000000604482015260640161061d565b600754341015611da4576040517f732f941300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611dad836105cd565b341015611de6576040517f732f941300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008381526009602052604090205462010000900460ff1615611e35576040517fc3d8a04200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600083815260096020526040902054610100900460ff16611e82576040517f60e178dc00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008381526009602052604090205460ff1615611ecb576040517f5f7d9fd600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600083815260096020526040902060020154610100900460ff1680611f065750600083815260096020526040902060040154610100900460ff165b15611f3d576040517f8783db4b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000611f6e611f686040518060400160405280878152602001336001600160a01b0316815250612417565b8461356f565b6004549091506001600160a01b03808316911614611fb8576040517f67e0646f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600084815260096020526040902060028101546201000090046001600160a01b03163303611ff45760028101805460ff19166001179055612054565b60048101546201000090046001600160a01b031633036120225760048101805460ff19166001179055612054565b6040517f5756bdf200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b805460ff1990811660019081178355600087815260096020526040902080548084168317825584546101009081900460ff9081161515820261ffff19938416178517808555875462010000908190048316151581027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffff90921691909117855587860154958501959095556002808801805491860180549284161515988316891781558154859004909316151590930261ff0019979097169316929092179490941780825584546001600160a01b0390859004169093027fffffffffffffffffffff0000000000000000000000000000000000000000ffff90931692909217825560038085018054869593949261216d9290860191613ba3565b50505060048281018054918301805460ff938416151560ff19821681178355835461010090819004909516151590940261ff001990941661ffff19909116179290921780835581546001600160a01b03620100009182900416027fffffffffffffffffffff0000000000000000000000000000000000000000ffff909116178255600580850180549293926122059286019190613ba3565b50506040513393508892507f5caaae5c6b57b49f652b073fdbcfee86d217e8071f04500c8cd368e57ffa909a9150600090a360028101546040517fd9caed12000000000000000000000000000000000000000000000000000000008152620100009091046001600160a01b039081166004830152336024830152604482018790527f0000000000000000000000000000000000000000000000000000000000000000169063d9caed1290606401600060405180830381600087803b1580156122cc57600080fd5b505af11580156122e0573d6000803e3d6000fd5b505050506004818101546040517fd9caed120000000000000000000000000000000000000000000000000000000081526001600160a01b0362010000909204821692810192909252336024830152604482018790527f0000000000000000000000000000000000000000000000000000000000000000169063d9caed12906064015b600060405180830381600087803b15801561237c57600080fd5b505af1158015612390573d6000803e3d6000fd5b5050505050505061075c6001600055565b6123a96132bd565b61271081106123e4576040517f6cb2e31100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600881905560405181907febd01212c8e07d8dfc99de72a08a3d0f4f8c05806b947a90c3c8e00129b7806590600090a250565b60006124897fe88e9621e199fc30ba59586e4e0bc08a8f9b622445f68a447c0cc0b8366d4047835160208086015160405161246e9493920192835260208301919091526001600160a01b0316604082015260600190565b60405160208183030381529060405280519060200120613593565b92915050565b6000612499613317565b81806124d1576040517f071e948200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600881111561250c576040517f4f87596100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b038516330361254e576040517fbf6f1bf100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600680543491819060006125618361445a565b909155505060008181526009602052604081206002810180547fffffffffffffffffffff0000000000000000000000000000000000000000ffff163362010000021790556003018054156125e1576040517f75fd3c5400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b8581101561264257818989838181106125ff576125ff614369565b83546001810185556000948552602090942060609091029290920192600202909101905061262d82826143a5565b5050808061263a9061445a565b9150506125e4565b506000838152600960205260409020815461266291600301908390613a50565b5060008381526009602052604081206004810180547fffffffffffffffffffff0000000000000000000000000000000000000000ffff16620100006001600160a01b038e16021790556001018590555b85811015612a335760008989838181106126ce576126ce614369565b6126e492602060609092020190810191506144a5565b60028111156126f5576126f5613eca565b0361278f5788888281811061270c5761270c614369565b90506060020160400135851461274e576040517ff601a14400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8215612786576040517ff544b27200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60019250612a21565b60018989838181106127a3576127a3614369565b6127b992602060609092020190810191506144a5565b60028111156127ca576127ca613eca565b036128525760008989838181106127e3576127e3614369565b90506060020160200160208101906127fb919061408d565b90506128158133308d8d87818110610b7457610b74614369565b61284c817f00000000000000000000000000000000000000000000000000000000000000008c8c86818110610bba57610bba614369565b50612a21565b600289898381811061286657612866614369565b61287c92602060609092020190810191506144a5565b600281111561288d5761288d613eca565b03612a215760008989838181106128a6576128a6614369565b90506060020160200160208101906128be919061408d565b9050806001600160a01b03166342842e0e33308d8d878181106128e3576128e3614369565b604080517fffffffff0000000000000000000000000000000000000000000000000000000060e089901b1681526001600160a01b039687166004820152959094166024860152606002919091019190910135604483015250606401600060405180830381600087803b15801561295857600080fd5b505af115801561296c573d6000803e3d6000fd5b50505050806001600160a01b031663095ea7b37f00000000000000000000000000000000000000000000000000000000000000008c8c868181106129b2576129b2614369565b905060600201604001356040518363ffffffff1660e01b81526004016129ed9291906001600160a01b03929092168252602082015260400190565b600060405180830381600087803b158015612a0757600080fd5b505af1158015612a1b573d6000803e3d6000fd5b50505050505b80612a2b8161445a565b9150506126b2565b5060008060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166349373a198833898f8f6040518663ffffffff1660e01b8152600401612a8c94939291906144c2565b60006040518083038185885af1158015612aaa573d6000803e3d6000fd5b50505050506040513d6000823e601f3d908101601f19168201604052612ad391908101906145dc565b9250925092508b6001600160a01b0316336001600160a01b0316877fe856d9c527bc013d9955698be44d4c7e13739fa87594a8933c0d942daf41e483868686604051612b21939291906146c2565b60405180910390a450939650505050505050612b3d6001600055565b9392505050565b612b4c6132bd565b6001600160a01b038116612b8c576040517f8579befe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6005546001600160a01b0390811690821603612bd4576040517f62dd97a500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600480547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383169081179091556040517f5ea4303afd5550bc5eb446bc464116608045996cc44a0d3ec3ccfa9650aa56ff90600090a250565b612c3e613317565b816006548110612c905760405162461bcd60e51b815260206004820152601260248201527f426174746c65206d757374206578697374210000000000000000000000000000604482015260640161061d565b6000838152600960205260409020805462010000900460ff1615612ce0576040517fc3d8a04200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600084815260096020526040902054610100900460ff16612d2d576040517f60e178dc00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b805460ff1615612d69576040517f5f7d9fd600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600281015433620100009091046001600160a01b031603612dcd576002810154610100900460ff1615612dc8576040517f8783db4b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612e5e565b600481015433620100009091046001600160a01b031603612e2c576004810154610100900460ff1615612dc8576040517f8783db4b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f38db7f2800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000612e8f612e896040518060400160405280888152602001336001600160a01b0316815250612417565b8561356f565b6005549091506001600160a01b03808316911614612ed9576040517fb4f0c44f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6002808301805461ff0019908116610100908117835560048601805483168217905560008981526009602052604090208654815460ff918216151560ff19808316821785558a5486900484161515860291871661ffff1993841617919091178085558a5462010000908190048516151581027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffff90921691909117855560018b81015490860155875498850180549985161515928a16831781558854879004909416151590950295169616959095179290921780855583546001600160a01b0390839004169091027fffffffffffffffffffff0000000000000000000000000000000000000000ffff909116178355600380860180548795939493926130019290860191613ba3565b50505060048281018054918301805460ff938416151560ff19821681178355835461010090819004909516151590940261ff001990941661ffff19909116179290921780835581546001600160a01b03620100009182900416027fffffffffffffffffffff0000000000000000000000000000000000000000ffff909116178255600580850180549293926130999286019190613ba3565b50506040513393508892507f8d7df0ced4aeda0e69d998705dddbf1eac70be8d7fb991edc80ddf6f29424a739150600090a360028201546040517fd9caed12000000000000000000000000000000000000000000000000000000008152620100009091046001600160a01b03908116600483018190526024830152604482018790527f0000000000000000000000000000000000000000000000000000000000000000169063d9caed1290606401600060405180830381600087803b15801561316157600080fd5b505af1158015613175573d6000803e3d6000fd5b505050506004828101546040517fd9caed120000000000000000000000000000000000000000000000000000000081526001600160a01b036201000090920482169281018390526024810192909252604482018790527f0000000000000000000000000000000000000000000000000000000000000000169063d9caed1290606401612362565b6132046132bd565b6001600160a01b0381166132805760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f6464726573730000000000000000000000000000000000000000000000000000606482015260840161061d565b6114be81613461565b60006020835110156132a55761329e836135db565b9050612489565b816132b084826147d0565b5060ff9392505050565b90565b6001546001600160a01b0316331461195f5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161061d565b6002600054036133695760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015260640161061d565b6002600055565b81601452806034526fa9059cbb00000000000000000000000060005260206000604460106000875af13d1560016000511417166133b5576390b8ec186000526004601cfd5b6000603452505050565b60405181606052826040528360601b602c526f23b872dd000000000000000000000000600c52602060006064601c6000895af13d15600160005114171661340e57637939f4246000526004601cfd5b600060605260405250505050565b81601452806034526f095ea7b300000000000000000000000060005260206000604460106000875af13d1560016000511417166133b557633e3f8f736000526004601cfd5b600180546001600160a01b038381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b606060ff83146134de5761329e83613632565b8180546134ea90614735565b80601f016020809104026020016040519081016040528092919081815260200182805461351690614735565b80156135635780601f1061353857610100808354040283529160200191613563565b820191906000526020600020905b81548152906001019060200180831161354657829003601f168201915b50505050509050612489565b600080600061357e8585613671565b9150915061358b816136b6565b509392505050565b60006124896135a061381b565b836040517f19010000000000000000000000000000000000000000000000000000000000008152600281019290925260228201526042902090565b600080829050601f8151111561361f57826040517f305a27a900000000000000000000000000000000000000000000000000000000815260040161061d91906148cc565b805161362a826148df565b179392505050565b6060600061363f8361394b565b604080516020808252818301909252919250600091906020820181803683375050509182525060208101929092525090565b60008082516041036136a75760208301516040840151606085015160001a61369b8782858561398c565b945094505050506136af565b506000905060025b9250929050565b60008160048111156136ca576136ca613eca565b036136d25750565b60018160048111156136e6576136e6613eca565b036137335760405162461bcd60e51b815260206004820152601860248201527f45434453413a20696e76616c6964207369676e61747572650000000000000000604482015260640161061d565b600281600481111561374757613747613eca565b036137945760405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e67746800604482015260640161061d565b60038160048111156137a8576137a8613eca565b036114be5760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c60448201527f7565000000000000000000000000000000000000000000000000000000000000606482015260840161061d565b6000306001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614801561387457507f000000000000000000000000000000000000000000000000000000000000000046145b1561389e57507f000000000000000000000000000000000000000000000000000000000000000090565b613946604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60208201527f0000000000000000000000000000000000000000000000000000000000000000918101919091527f000000000000000000000000000000000000000000000000000000000000000060608201524660808201523060a082015260009060c00160405160208183030381529060405280519060200120905090565b905090565b600060ff8216601f811115612489576040517fb3512b0c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a08311156139c35750600090506003613a47565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa158015613a17573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b038116613a4057600060019250925050613a47565b9150600090505b94509492505050565b828054828255906000526020600020906002028101928215613b055760005260206000209160020282015b82811115613b0557825482548491849160ff90911690829060ff19166001836002811115613aab57613aab613eca565b0217905550815481547fffffffffffffffffffffff0000000000000000000000000000000000000000ff16610100918290046001600160a01b03169091021781556001918201549101556002928301929190910190613a7b565b50613b11929150613c58565b5090565b6040518060c0016040528060001515815260200160001515815260200160001515815260200160008152602001613b7a604051806080016040528060001515815260200160001515815260200160006001600160a01b03168152602001606081525090565b815260408051608081018252600080825260208281018290529282015260608082015291015290565b828054828255906000526020600020906002028101928215613b055760005260206000209160020282015b82811115613b0557825482548491849160ff90911690829060ff19166001836002811115613bfe57613bfe613eca565b0217905550815481547fffffffffffffffffffffff0000000000000000000000000000000000000000ff16610100918290046001600160a01b03169091021781556001918201549101556002928301929190910190613bce565b5b80821115613b115780547fffffffffffffffffffffff00000000000000000000000000000000000000000016815560006001820155600201613c59565b6001600160a01b03811681146114be57600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff81118282101715613d0357613d03613cab565b604052919050565b600082601f830112613d1c57600080fd5b813567ffffffffffffffff811115613d3657613d36613cab565b613d496020601f19601f84011601613cda565b818152846020838601011115613d5e57600080fd5b816020850160208301376000918101602001919091529392505050565b60008060008060808587031215613d9157600080fd5b8435613d9c81613c96565b93506020850135613dac81613c96565b925060408501359150606085013567ffffffffffffffff811115613dcf57600080fd5b613ddb87828801613d0b565b91505092959194509250565b600060208284031215613df957600080fd5b5035919050565b60008060408385031215613e1357600080fd5b8235613e1e81613c96565b91506020830135613e2e81613c96565b809150509250929050565b60008083601f840112613e4b57600080fd5b50813567ffffffffffffffff811115613e6357600080fd5b6020830191508360206060830285010111156136af57600080fd5b600080600060408486031215613e9357600080fd5b83359250602084013567ffffffffffffffff811115613eb157600080fd5b613ebd86828701613e39565b9497909650939450505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b60038110613f30577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b9052565b6000608083018251151584526020808401511515818601526040808501516001600160a01b038082168389015260609150818701516080838a015285815180885260a08b0191508683019750600092505b80831015613fbf578751613f9a838251613ef9565b8088015185168389015286015186830152968601966001929092019190840190613f85565b509998505050505050505050565b86151581528515156020820152841515604082015283606082015260c060808201526000613ffe60c0830185613f34565b82810360a08401526140108185613f34565b9998505050505050505050565b60208152815115156020820152602082015115156040820152604082015115156060820152606082015160808201526000608083015160c060a084015261406760e0840182613f34565b905060a0840151601f198483030160c08501526140848282613f34565b95945050505050565b60006020828403121561409f57600080fd5b8135612b3d81613c96565b6000815180845260005b818110156140d0576020818501810151868301820152016140b4565b506000602082860101526020601f19601f83011685010191505092915050565b600081518084526020808501945080840160005b8381101561412057815187529582019590820190600101614104565b509495945050505050565b7fff000000000000000000000000000000000000000000000000000000000000008816815260e06020820152600061416660e08301896140aa565b828103604084015261417881896140aa565b90508660608401526001600160a01b03861660808401528460a084015282810360c08401526141a781856140f0565b9a9950505050505050505050565b6000806000606084860312156141ca57600080fd5b83356141d581613c96565b925060208401356141e581613c96565b929592945050506040919091013590565b6000806040838503121561420957600080fd5b82359150602083013567ffffffffffffffff81111561422757600080fd5b61423385828601613d0b565b9150509250929050565b60006040828403121561424f57600080fd5b6040516040810181811067ffffffffffffffff8211171561427257614272613cab565b60405282358152602083013561428781613c96565b60208201529392505050565b6000806000604084860312156142a857600080fd5b83356142b381613c96565b9250602084013567ffffffffffffffff811115613eb157600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b8082028115828204841417612489576124896142cf565b60008261434b577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b60006020828403121561436257600080fd5b5051919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600381106114be57600080fd5b81356143b081614398565b600381106143e7577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b815460ff821691508160ff198216178355602084013561440681613c96565b74ffffffffffffffffffffffffffffffffffffffff008160081b16837fffffffffffffffffffffff000000000000000000000000000000000000000000841617178455505050604082013560018201555050565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361448b5761448b6142cf565b5060010190565b80820180821115612489576124896142cf565b6000602082840312156144b757600080fd5b8135612b3d81614398565b600060608083016001600160a01b03808916855260208881870152604084818801528388855260808801905089945060005b8981101561453d57853561450781614398565b6145118382613ef9565b508386013561451f81613c96565b851682850152858301358383015294860194908601906001016144f4565b509b9a5050505050505050505050565b600067ffffffffffffffff82111561456757614567613cab565b5060051b60200190565b600082601f83011261458257600080fd5b815160206145976145928361454d565b613cda565b82815260059290921b840181019181810190868411156145b657600080fd5b8286015b848110156145d157805183529183019183016145ba565b509695505050505050565b6000806000606084860312156145f157600080fd5b835167ffffffffffffffff8082111561460957600080fd5b61461587838801614571565b945060209150818601518181111561462c57600080fd5b8601601f8101881361463d57600080fd5b805161464b6145928261454d565b81815260059190911b8201840190848101908a83111561466a57600080fd5b928501925b8284101561469157835161468281613c96565b8252928501929085019061466f565b60408a01519097509450505050808211156146ab57600080fd5b506146b886828701614571565b9150509250925092565b6060815260006146d560608301866140f0565b82810360208481019190915285518083528682019282019060005b818110156147155784516001600160a01b0316835293830193918301916001016146f0565b5050848103604086015261472981876140f0565b98975050505050505050565b600181811c9082168061474957607f821691505b602082108103610667577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b601f821115611d0957600081815260208120601f850160051c810160208610156147a95750805b601f850160051c820191505b818110156147c8578281556001016147b5565b505050505050565b815167ffffffffffffffff8111156147ea576147ea613cab565b6147fe816147f88454614735565b84614782565b602080601f831160018114614851576000841561481b5750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b1785556147c8565b600085815260208120601f198616915b8281101561488057888601518255948401946001909101908401614861565b50858210156148bc57878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b01905550565b602081526000612b3d60208301846140aa565b80516020808301519190811015610667577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60209190910360031b1b1691905056fea2646970667358221220156769e0a178c2060aeb8c7d6387738abc8cf9ba62c1a5a21b07bac628e8d48064736f6c6343000812003360806040523480156200001157600080fd5b50604051620019ad380380620019ad8339810160408190526200003491620001ff565b6200003f33620001af565b6001600160a01b03811662000067576040516342bcdf7f60e11b815260040160405180910390fd5b7343000000000000000000000000000000000000026001600160a01b031663f098767a6040518163ffffffff1660e01b8152600401600060405180830381600087803b158015620000b757600080fd5b505af1158015620000cc573d6000803e3d6000fd5b505050507343000000000000000000000000000000000000026001600160a01b0316634e606c476040518163ffffffff1660e01b8152600401600060405180830381600087803b1580156200012057600080fd5b505af115801562000135573d6000803e3d6000fd5b5050604051631d70c8d360e31b81526001600160a01b0384166004820152734300000000000000000000000000000000000002925063eb8646989150602401600060405180830381600087803b1580156200018f57600080fd5b505af1158015620001a4573d6000803e3d6000fd5b505050505062000231565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6000602082840312156200021257600080fd5b81516001600160a01b03811681146200022a57600080fd5b9392505050565b61176c80620002416000396000f3fe60806040526004361061007b5760003560e01c806397d757761161004e57806397d757761461017f578063ae22192e146101a7578063d9caed12146101d4578063f2fde38b146101f457600080fd5b8063150b7a021461008057806349373a19146100fa578063715018a61461011c5780638da5cb5b14610133575b600080fd5b34801561008c57600080fd5b506100c461009b366004611289565b7f150b7a0200000000000000000000000000000000000000000000000000000000949350505050565b6040517fffffffff0000000000000000000000000000000000000000000000000000000090911681526020015b60405180910390f35b61010d610108366004611387565b610214565b6040516100f193929190611494565b34801561012857600080fd5b5061013161088c565b005b34801561013f57600080fd5b5060005473ffffffffffffffffffffffffffffffffffffffff165b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016100f1565b34801561018b57600080fd5b5061015a73430000000000000000000000000000000000000281565b3480156101b357600080fd5b506101c76101c23660046114d7565b6108a0565b6040516100f19190611532565b3480156101e057600080fd5b506101316101ef3660046115d8565b610994565b34801561020057600080fd5b5061013161020f366004611619565b610e7b565b6060806060610221610f37565b833460008267ffffffffffffffff81111561023e5761023e61125a565b604051908082528060200260200182016040528015610267578160200160208202803683370190505b50905060008367ffffffffffffffff8111156102855761028561125a565b6040519080825280602002602001820160405280156102ae578160200160208202803683370190505b50905060008467ffffffffffffffff8111156102cc576102cc61125a565b6040519080825280602002602001820160405280156102f5578160200160208202803683370190505b50905060005b858110156105b3578a8a828181106103155761031561163d565b61032b926020606090920201908101915061166c565b600281111561033c5761033c611503565b84828151811061034e5761034e61163d565b6020026020010181815250508a8a8281811061036c5761036c61163d565b90506060020160200160208101906103849190611619565b8382815181106103965761039661163d565b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff16815250508a8a828181106103e2576103e261163d565b905060600201604001358282815181106103fe576103fe61163d565b60209081029190910181019190915273ffffffffffffffffffffffffffffffffffffffff8e1660009081526001825260408082208f835290925281902081516060810190925290808d8d858181106104585761045861163d565b61046e926020606090920201908101915061166c565b600281111561047f5761047f611503565b81526020018d8d858181106104965761049661163d565b90506060020160200160208101906104ae9190611619565b73ffffffffffffffffffffffffffffffffffffffff1681526020018d8d858181106104db576104db61163d565b6060029190910160400135909152508154600180820184556000938452602090932082516002928302909101805493949093919284927fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169190849081111561054657610546611503565b02179055506020820151815473ffffffffffffffffffffffffffffffffffffffff909116610100027fffffffffffffffffffffff0000000000000000000000000000000000000000ff909116178155604090910151600190910155806105ab8161168d565b9150506102fb565b5060005b858110156108255760008b8b838181106105d3576105d361163d565b6105e9926020606090920201908101915061166c565b60028111156105fa576105fa611503565b0361063e5760008511610639576040517fb2d71ce000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610813565b60018b8b838181106106525761065261163d565b610668926020606090920201908101915061166c565b600281111561067957610679611503565b036106d95760008b8b838181106106925761069261163d565b90506060020160200160208101906106aa9190611619565b90506106d38133308f8f878181106106c4576106c461163d565b90506060020160400135610fb8565b50610813565b60028b8b838181106106ed576106ed61163d565b610703926020606090920201908101915061166c565b600281111561071457610714611503565b036108135760008b8b8381811061072d5761072d61163d565b90506060020160200160208101906107459190611619565b90508073ffffffffffffffffffffffffffffffffffffffff166342842e0e33308f8f878181106107775761077761163d565b604080517fffffffff0000000000000000000000000000000000000000000000000000000060e089901b16815273ffffffffffffffffffffffffffffffffffffffff9687166004820152959094166024860152606002919091019190910135604483015250606401600060405180830381600087803b1580156107f957600080fd5b505af115801561080d573d6000803e3d6000fd5b50505050505b8061081d8161168d565b9150506105b7565b508b73ffffffffffffffffffffffffffffffffffffffff167fb0107a6f6949cc8456afb31c37416271495e207fc54f9c78060932524721f2388c85858560405161087294939291906116ec565b60405180910390a2919b909a509098509650505050505050565b610894610f37565b61089e6000611015565b565b73ffffffffffffffffffffffffffffffffffffffff821660009081526001602090815260408083208484528252808320805482518185028101850190935280835260609493849084015b8282101561098757838290600052602060002090600202016040518060600160405290816000820160009054906101000a900460ff16600281111561093157610931611503565b600281111561094257610942611503565b81528154610100900473ffffffffffffffffffffffffffffffffffffffff166020808301919091526001928301546040909201919091529183529290920191016108ea565b5092979650505050505050565b61099c610f37565b73ffffffffffffffffffffffffffffffffffffffff83166000908152600160209081526040808320848452825280832080548251818502810185019093528083529192909190849084015b82821015610a8457838290600052602060002090600202016040518060600160405290816000820160009054906101000a900460ff166002811115610a2e57610a2e611503565b6002811115610a3f57610a3f611503565b81528154610100900473ffffffffffffffffffffffffffffffffffffffff166020808301919091526001928301546040909201919091529183529290920191016109e7565b50505050905060008151905060008167ffffffffffffffff811115610aab57610aab61125a565b604051908082528060200260200182016040528015610ad4578160200160208202803683370190505b50905060008267ffffffffffffffff811115610af257610af261125a565b604051908082528060200260200182016040528015610b1b578160200160208202803683370190505b50905060008367ffffffffffffffff811115610b3957610b3961125a565b604051908082528060200260200182016040528015610b62578160200160208202803683370190505b50905060005b84811015610e1c5773ffffffffffffffffffffffffffffffffffffffff891660009081526001602090815260408083208a84529091528120805483908110610bb257610bb261163d565b90600052602060002090600202016040518060600160405290816000820160009054906101000a900460ff166002811115610bef57610bef611503565b6002811115610c0057610c00611503565b81528154610100900473ffffffffffffffffffffffffffffffffffffffff1660208201526001909101546040909101529050600081516002811115610c4757610c47611503565b03610c77576040810151610c729073ffffffffffffffffffffffffffffffffffffffff8b169061108a565b610d65565b600181516002811115610c8c57610c8c611503565b03610caf57600081602001519050610ca9818b84604001516111e9565b50610d65565b600281516002811115610cc457610cc4611503565b03610d6557602081015160408083015190517f42842e0e00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff8c811660248301526044820192909252908216906342842e0e90606401600060405180830381600087803b158015610d4b57600080fd5b505af1158015610d5f573d6000803e3d6000fd5b50505050505b80516002811115610d7857610d78611503565b858381518110610d8a57610d8a61163d565b6020026020010181815250508060200151848381518110610dad57610dad61163d565b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff16815250508060400151838381518110610dfe57610dfe61163d565b60209081029190910101525080610e148161168d565b915050610b68565b508773ffffffffffffffffffffffffffffffffffffffff167f7b224de6515d87d7a66df408bfbcf4050c68efef906081cff527d45bf3fdd39787858585604051610e6994939291906116ec565b60405180910390a25050505050505050565b610e83610f37565b73ffffffffffffffffffffffffffffffffffffffff8116610f2b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f646472657373000000000000000000000000000000000000000000000000000060648201526084015b60405180910390fd5b610f3481611015565b50565b60005473ffffffffffffffffffffffffffffffffffffffff16331461089e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610f22565b60405181606052826040528360601b602c526f23b872dd000000000000000000000000600c52602060006064601c6000895af13d15600160005114171661100757637939f4246000526004601cfd5b600060605260405250505050565b6000805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b804710156110f4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a20696e73756666696369656e742062616c616e63650000006044820152606401610f22565b60008273ffffffffffffffffffffffffffffffffffffffff168260405160006040518083038185875af1925050503d806000811461114e576040519150601f19603f3d011682016040523d82523d6000602084013e611153565b606091505b50509050806111e4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603a60248201527f416464726573733a20756e61626c6520746f2073656e642076616c75652c207260448201527f6563697069656e74206d617920686176652072657665727465640000000000006064820152608401610f22565b505050565b81601452806034526fa9059cbb00000000000000000000000060005260206000604460106000875af13d15600160005114171661122e576390b8ec186000526004601cfd5b6000603452505050565b73ffffffffffffffffffffffffffffffffffffffff81168114610f3457600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6000806000806080858703121561129f57600080fd5b84356112aa81611238565b935060208501356112ba81611238565b925060408501359150606085013567ffffffffffffffff808211156112de57600080fd5b818701915087601f8301126112f257600080fd5b8135818111156113045761130461125a565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f0116810190838211818310171561134a5761134a61125a565b816040528281528a602084870101111561136357600080fd5b82602086016020830137600060208483010152809550505050505092959194509250565b6000806000806060858703121561139d57600080fd5b84356113a881611238565b935060208501359250604085013567ffffffffffffffff808211156113cc57600080fd5b818701915087601f8301126113e057600080fd5b8135818111156113ef57600080fd5b88602060608302850101111561140457600080fd5b95989497505060200194505050565b600081518084526020808501945080840160005b8381101561144357815187529582019590820190600101611427565b509495945050505050565b600081518084526020808501945080840160005b8381101561144357815173ffffffffffffffffffffffffffffffffffffffff1687529582019590820190600101611462565b6060815260006114a76060830186611413565b82810360208401526114b9818661144e565b905082810360408401526114cd8185611413565b9695505050505050565b600080604083850312156114ea57600080fd5b82356114f581611238565b946020939093013593505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b60208082528251828201819052600091906040908185019086840185805b838110156115ca578251805160038110611591577f4e487b710000000000000000000000000000000000000000000000000000000084526021600452602484fd5b86528088015173ffffffffffffffffffffffffffffffffffffffff16888701528601518686015260609094019391860191600101611550565b509298975050505050505050565b6000806000606084860312156115ed57600080fd5b83356115f881611238565b9250602084013561160881611238565b929592945050506040919091013590565b60006020828403121561162b57600080fd5b813561163681611238565b9392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60006020828403121561167e57600080fd5b81356003811061163657600080fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036116e5577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b5060010190565b8481526080602082015260006117056080830186611413565b8281036040840152611717818661144e565b9050828103606084015261172b8185611413565b97965050505050505056fea2646970667358221220d834dfd547a553535b51f42da9bdbbe3bea884de8554b77e5a39ea8aeead879464736f6c63430008120033000000000000000000000000be16d5c286aa885a2261a6b99a518e5effd2f3880000000000000000000000001d28206358d30e84f985ac6edbc6ae49f25ffa7f000000000000000000000000b0e7d4175be8e83a8075538f2dbe6f87d5a69013

Deployed Bytecode

0x6080604052600436106101c25760003560e01c806380d9c5f3116100f7578063b7a2a78111610095578063e0caf57811610064578063e0caf57814610557578063e625215c14610577578063e941fa7814610597578063f2fde38b146105ad57600080fd5b8063b7a2a781146104f1578063ba12121714610504578063cdd7eb5214610524578063ce55803f1461054457600080fd5b80638da5cb5b116100d15780638da5cb5b1461047557806397d7577614610493578063b5462347146104bb578063b72b85a1146104d157600080fd5b806380d9c5f31461040657806384b0196e146104195780638b6cf6681461044157600080fd5b8063606d2e96116101645780636ff1c9bc1161013e5780636ff1c9bc14610391578063701b1e5a146103b157806370e5deb9146103d1578063715018a6146103f157600080fd5b8063606d2e96146103125780636a1db1bf146103445780636dd0ce7a1461036457600080fd5b8063297bf1a5116101a0578063297bf1a5146102a757806331a6ff66146102c957806337d8b79d146102df578063573255f4146102f257600080fd5b8063150b7a02146101c7578063160d6f3c146102415780632828eea814610279575b600080fd5b3480156101d357600080fd5b5061020b6101e2366004613d7b565b7f150b7a0200000000000000000000000000000000000000000000000000000000949350505050565b6040517fffffffff0000000000000000000000000000000000000000000000000000000090911681526020015b60405180910390f35b34801561024d57600080fd5b50600554610261906001600160a01b031681565b6040516001600160a01b039091168152602001610238565b34801561028557600080fd5b50610299610294366004613de7565b6105cd565b604051908152602001610238565b3480156102b357600080fd5b506102c76102c2366004613e00565b61066d565b005b3480156102d557600080fd5b5061029960065481565b6102c76102ed366004613e7e565b610760565b3480156102fe57600080fd5b50600454610261906001600160a01b031681565b34801561031e57600080fd5b5061033261032d366004613de7565b610ec6565b60405161023896959493929190613fcd565b34801561035057600080fd5b506102c761035f366004613de7565b6110ec565b34801561037057600080fd5b5061038461037f366004613de7565b611127565b604051610238919061401d565b34801561039d57600080fd5b506102c76103ac36600461408d565b6113d5565b3480156103bd57600080fd5b506102c76103cc36600461408d565b6114c1565b3480156103dd57600080fd5b506102c76103ec366004613de7565b6115b3565b3480156103fd57600080fd5b506102c761194d565b6102c7610414366004613de7565b611961565b34801561042557600080fd5b5061042e611b85565b604051610238979695949392919061412b565b34801561044d57600080fd5b506102617f000000000000000000000000c9438f95aa8d9ee1b5edea15c7fa4b2cac723dce81565b34801561048157600080fd5b506001546001600160a01b0316610261565b34801561049f57600080fd5b5061026173430000000000000000000000000000000000000281565b3480156104c757600080fd5b5061029960085481565b3480156104dd57600080fd5b506102c76104ec3660046141b5565b611c2a565b6102c76104ff3660046141f6565b611d0e565b34801561051057600080fd5b506102c761051f366004613de7565b6123a1565b34801561053057600080fd5b5061029961053f36600461423d565b612417565b610299610552366004614293565b61248f565b34801561056357600080fd5b506102c761057236600461408d565b612b44565b34801561058357600080fd5b506102c76105923660046141f6565b612c36565b3480156105a357600080fd5b5061029960075481565b3480156105b957600080fd5b506102c76105c836600461408d565b6131fc565b60008160065481106106265760405162461bcd60e51b815260206004820152601260248201527f426174746c65206d75737420657869737421000000000000000000000000000060448201526064015b60405180910390fd5b600083815260096020526040902060010154600854610649576000925050610667565b61271060085461065990836142fe565b6106639190614315565b9250505b50919050565b6106756132bd565b61067d613317565b6001600160a01b0382166106bd576040517f8579befe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015281906000906001600160a01b038316906370a0823190602401602060405180830381865afa15801561071f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107439190614350565b9050610750828583613370565b505061075c6001600055565b5050565b8260065481106107b25760405162461bcd60e51b815260206004820152601260248201527f426174746c65206d757374206578697374210000000000000000000000000000604482015260640161061d565b6107ba613317565b81806107f2576040517f071e948200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600881111561082d576040517f4f87596100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000858152600960205260408120805434929190610100900460ff1615610880576040517f864a886500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b805462010000900460ff16156108c2576040517fc3d8a04200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60048101546201000090046001600160a01b0316331461090e576040517f87b1f39900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000888152600960205260409020600501805415610958576040517f75fd3c5400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b858110156109b9578189898381811061097657610976614369565b8354600181018555600094855260209094206060909102929092019260020290910190506109a482826143a5565b505080806109b19061445a565b91505061095b565b50600089815260096020526040902081546109d991600501908390613a50565b506000898152600960205260408120805461ff0019166101001781556001018054869290610a08908490614492565b90915550600090505b85811015610db0576000898983818110610a2d57610a2d614369565b610a4392602060609092020190810191506144a5565b6002811115610a5457610a54613eca565b03610aee57888882818110610a6b57610a6b614369565b905060600201604001358514610aad576040517ff601a14400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8315610ae5576040517ff544b27200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60019350610d9e565b6001898983818110610b0257610b02614369565b610b1892602060609092020190810191506144a5565b6002811115610b2957610b29613eca565b03610bcf576000898983818110610b4257610b42614369565b9050606002016020016020810190610b5a919061408d565b9050610b838133308d8d87818110610b7457610b74614369565b905060600201604001356133bf565b610bc9817f000000000000000000000000c9438f95aa8d9ee1b5edea15c7fa4b2cac723dce8c8c86818110610bba57610bba614369565b9050606002016040013561341c565b50610d9e565b6002898983818110610be357610be3614369565b610bf992602060609092020190810191506144a5565b6002811115610c0a57610c0a613eca565b03610d9e576000898983818110610c2357610c23614369565b9050606002016020016020810190610c3b919061408d565b9050806001600160a01b03166342842e0e33308d8d87818110610c6057610c60614369565b604080517fffffffff0000000000000000000000000000000000000000000000000000000060e089901b1681526001600160a01b039687166004820152959094166024860152606002919091019190910135604483015250606401600060405180830381600087803b158015610cd557600080fd5b505af1158015610ce9573d6000803e3d6000fd5b50505050806001600160a01b031663095ea7b37f000000000000000000000000c9438f95aa8d9ee1b5edea15c7fa4b2cac723dce8c8c86818110610d2f57610d2f614369565b905060600201604001356040518363ffffffff1660e01b8152600401610d6a9291906001600160a01b03929092168252602082015260400190565b600060405180830381600087803b158015610d8457600080fd5b505af1158015610d98573d6000803e3d6000fd5b50505050505b80610da88161445a565b915050610a11565b5060008060007f000000000000000000000000c9438f95aa8d9ee1b5edea15c7fa4b2cac723dce6001600160a01b03166349373a1988338f8f8f6040518663ffffffff1660e01b8152600401610e0994939291906144c2565b60006040518083038185885af1158015610e27573d6000803e3d6000fd5b50505050506040513d6000823e601f3d908101601f19168201604052610e5091908101906145dc565b600288015460405193965091945092503391620100009091046001600160a01b0316908e907f09afbfed4f33a53c1bff738c8909e5f07a302af749605de0b786f92e40be97f890610ea6908890889088906146c2565b60405180910390a45050505050505050610ec06001600055565b50505050565b600960209081526000918252604080832080546001820154835160808101855260028401805460ff8181161515845261010080830482161515858b01526001600160a01b03620100009384900416858a01526003880180548a51818d0281018d01909b52808b528389169c92890484169b94909804909216989597969495939460608701949390919084015b82821015610fe257838290600052602060002090600202016040518060600160405290816000820160009054906101000a900460ff166002811115610f9957610f99613eca565b6002811115610faa57610faa613eca565b8152815461010090046001600160a01b0316602080830191909152600192830154604090920191909152918352929092019101610f52565b505050915250506040805160808101825260048401805460ff808216151584526101008204161515602080850191909152620100009091046001600160a01b0316838501526005860180548551818402810184019096528086529596959394929360608601939260009084015b828210156110df57838290600052602060002090600202016040518060600160405290816000820160009054906101000a900460ff16600281111561109657611096613eca565b60028111156110a7576110a7613eca565b8152815461010090046001600160a01b031660208083019190915260019283015460409092019190915291835292909201910161104f565b5050505081525050905086565b6110f46132bd565b600781905560405181907f6bbc57480a46553fa4d156ce702beef5f3ad66303b0ed1a5d4cb44966c6584c390600090a250565b61112f613b15565b8160065481106111815760405162461bcd60e51b815260206004820152601260248201527f426174746c65206d757374206578697374210000000000000000000000000000604482015260640161061d565b6000838152600960209081526040808320815160c081018352815460ff808216151583526101008083048216151584880152620100009283900482161515848701526001850154606080860191909152865160808181018952600288018054808716151584529485049095161515828b01526001600160a01b039590930494909416848801526003860180548851818b0281018b019099528089529598969792890196949593949186019390918a9084015b828210156112c357838290600052602060002090600202016040518060600160405290816000820160009054906101000a900460ff16600281111561127a5761127a613eca565b600281111561128b5761128b613eca565b8152815461010090046001600160a01b0316602080830191909152600192830154604090920191909152918352929092019101611233565b5050509152505081526040805160808101825260048401805460ff808216151584526101008204161515602080850191909152620100009091046001600160a01b031683850152600586018054855181840281018401909652808652958201959394929360608601939260009084015b828210156113c357838290600052602060002090600202016040518060600160405290816000820160009054906101000a900460ff16600281111561137a5761137a613eca565b600281111561138b5761138b613eca565b8152815461010090046001600160a01b0316602080830191909152600192830154604090920191909152918352929092019101611333565b50505091525050905250949350505050565b6113dd6132bd565b6113e5613317565b6001600160a01b038116611425576040517f8579befe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60405147906000906001600160a01b0384169083908381818185875af1925050503d8060008114611472576040519150601f19603f3d011682016040523d82523d6000602084013e611477565b606091505b50509050806114b2576040517f90b8ec1800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50506114be6001600055565b50565b6114c96132bd565b6001600160a01b038116611509576040517f8579befe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6004546001600160a01b0390811690821603611551576040517f62dd97a500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600580547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383169081179091556040517fb577a105c65e3df7015d800e52e963b9a45d2ccf117aded28201271748c8832490600090a250565b8060065481106116055760405162461bcd60e51b815260206004820152601260248201527f426174746c65206d757374206578697374210000000000000000000000000000604482015260640161061d565b61160d6132bd565b611615613317565b6000828152600960205260409020805460ff1661194257805460ff1990811660019081178355600085815260096020526040902080548084168317825584546101009081900460ff9081161515820261ffff19938416178517808555875462010000908190048316151581027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffff90921691909117855587860154958501959095556002808801805491860180549284161515988316891781558154859004909316151590930261ff0019979097169316929092179490941780825584546001600160a01b0390859004169093027fffffffffffffffffffff0000000000000000000000000000000000000000ffff9093169290921782556003808501805486959394926117459290860191613ba3565b50505060048281018054918301805460ff938416151560ff19821681178355835461010090819004909516151590940261ff001990941661ffff19909116179290921780835581546001600160a01b03620100009182900416027fffffffffffffffffffff0000000000000000000000000000000000000000ffff909116178255600580850180549293926117dd9286019190613ba3565b5050505060028201546040517fd9caed12000000000000000000000000000000000000000000000000000000008152620100009091046001600160a01b03908116600483018190526024830152604482018690527f000000000000000000000000c9438f95aa8d9ee1b5edea15c7fa4b2cac723dce16915063d9caed1290606401600060405180830381600087803b15801561187857600080fd5b505af115801561188c573d6000803e3d6000fd5b505050506004818101546040517fd9caed120000000000000000000000000000000000000000000000000000000081526001600160a01b036201000090920482169281018390526024810192909252604482018590527f000000000000000000000000c9438f95aa8d9ee1b5edea15c7fa4b2cac723dce169063d9caed12906064015b600060405180830381600087803b15801561192957600080fd5b505af115801561193d573d6000803e3d6000fd5b505050505b5061075c6001600055565b6119556132bd565b61195f6000613461565b565b8060065481106119b35760405162461bcd60e51b815260206004820152601260248201527f426174746c65206d757374206578697374210000000000000000000000000000604482015260640161061d565b6119bb613317565b60008281526009602052604090208054610100900460ff1615611a0a576040517f864a886500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b805462010000900460ff1615611a4c576040517fc3d8a04200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60028101546201000090046001600160a01b03163314611a98576040517fb3153ae600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8054620100007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffff9091168117825560048201546040519190046001600160a01b031690339085907f1adcfbcfae7625b5bbaf696c3898a3f28b8d91300b9f0950d5a72abf3af79fc490600090a460028101546040517fd9caed12000000000000000000000000000000000000000000000000000000008152620100009091046001600160a01b039081166004830152336024830152604482018590527f000000000000000000000000c9438f95aa8d9ee1b5edea15c7fa4b2cac723dce169063d9caed129060640161190f565b600060608082808083611bb97f43616d627269614475656c4172656e610000000000000000000000000000001060026134cb565b611be47f310000000000000000000000000000000000000000000000000000000000000160036134cb565b604080516000808252602082019092527f0f000000000000000000000000000000000000000000000000000000000000009b939a50919850469750309650945092509050565b611c326132bd565b611c3a613317565b6001600160a01b038316611c7a576040517f8579befe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f42842e0e0000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b038481166024830152604482018390528391908216906342842e0e90606401600060405180830381600087803b158015611ce657600080fd5b505af1158015611cfa573d6000803e3d6000fd5b5050505050611d096001600055565b505050565b611d16613317565b816006548110611d685760405162461bcd60e51b815260206004820152601260248201527f426174746c65206d757374206578697374210000000000000000000000000000604482015260640161061d565b600754341015611da4576040517f732f941300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611dad836105cd565b341015611de6576040517f732f941300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008381526009602052604090205462010000900460ff1615611e35576040517fc3d8a04200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600083815260096020526040902054610100900460ff16611e82576040517f60e178dc00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008381526009602052604090205460ff1615611ecb576040517f5f7d9fd600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600083815260096020526040902060020154610100900460ff1680611f065750600083815260096020526040902060040154610100900460ff165b15611f3d576040517f8783db4b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000611f6e611f686040518060400160405280878152602001336001600160a01b0316815250612417565b8461356f565b6004549091506001600160a01b03808316911614611fb8576040517f67e0646f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600084815260096020526040902060028101546201000090046001600160a01b03163303611ff45760028101805460ff19166001179055612054565b60048101546201000090046001600160a01b031633036120225760048101805460ff19166001179055612054565b6040517f5756bdf200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b805460ff1990811660019081178355600087815260096020526040902080548084168317825584546101009081900460ff9081161515820261ffff19938416178517808555875462010000908190048316151581027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffff90921691909117855587860154958501959095556002808801805491860180549284161515988316891781558154859004909316151590930261ff0019979097169316929092179490941780825584546001600160a01b0390859004169093027fffffffffffffffffffff0000000000000000000000000000000000000000ffff90931692909217825560038085018054869593949261216d9290860191613ba3565b50505060048281018054918301805460ff938416151560ff19821681178355835461010090819004909516151590940261ff001990941661ffff19909116179290921780835581546001600160a01b03620100009182900416027fffffffffffffffffffff0000000000000000000000000000000000000000ffff909116178255600580850180549293926122059286019190613ba3565b50506040513393508892507f5caaae5c6b57b49f652b073fdbcfee86d217e8071f04500c8cd368e57ffa909a9150600090a360028101546040517fd9caed12000000000000000000000000000000000000000000000000000000008152620100009091046001600160a01b039081166004830152336024830152604482018790527f000000000000000000000000c9438f95aa8d9ee1b5edea15c7fa4b2cac723dce169063d9caed1290606401600060405180830381600087803b1580156122cc57600080fd5b505af11580156122e0573d6000803e3d6000fd5b505050506004818101546040517fd9caed120000000000000000000000000000000000000000000000000000000081526001600160a01b0362010000909204821692810192909252336024830152604482018790527f000000000000000000000000c9438f95aa8d9ee1b5edea15c7fa4b2cac723dce169063d9caed12906064015b600060405180830381600087803b15801561237c57600080fd5b505af1158015612390573d6000803e3d6000fd5b5050505050505061075c6001600055565b6123a96132bd565b61271081106123e4576040517f6cb2e31100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600881905560405181907febd01212c8e07d8dfc99de72a08a3d0f4f8c05806b947a90c3c8e00129b7806590600090a250565b60006124897fe88e9621e199fc30ba59586e4e0bc08a8f9b622445f68a447c0cc0b8366d4047835160208086015160405161246e9493920192835260208301919091526001600160a01b0316604082015260600190565b60405160208183030381529060405280519060200120613593565b92915050565b6000612499613317565b81806124d1576040517f071e948200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600881111561250c576040517f4f87596100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b038516330361254e576040517fbf6f1bf100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600680543491819060006125618361445a565b909155505060008181526009602052604081206002810180547fffffffffffffffffffff0000000000000000000000000000000000000000ffff163362010000021790556003018054156125e1576040517f75fd3c5400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b8581101561264257818989838181106125ff576125ff614369565b83546001810185556000948552602090942060609091029290920192600202909101905061262d82826143a5565b5050808061263a9061445a565b9150506125e4565b506000838152600960205260409020815461266291600301908390613a50565b5060008381526009602052604081206004810180547fffffffffffffffffffff0000000000000000000000000000000000000000ffff16620100006001600160a01b038e16021790556001018590555b85811015612a335760008989838181106126ce576126ce614369565b6126e492602060609092020190810191506144a5565b60028111156126f5576126f5613eca565b0361278f5788888281811061270c5761270c614369565b90506060020160400135851461274e576040517ff601a14400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8215612786576040517ff544b27200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60019250612a21565b60018989838181106127a3576127a3614369565b6127b992602060609092020190810191506144a5565b60028111156127ca576127ca613eca565b036128525760008989838181106127e3576127e3614369565b90506060020160200160208101906127fb919061408d565b90506128158133308d8d87818110610b7457610b74614369565b61284c817f000000000000000000000000c9438f95aa8d9ee1b5edea15c7fa4b2cac723dce8c8c86818110610bba57610bba614369565b50612a21565b600289898381811061286657612866614369565b61287c92602060609092020190810191506144a5565b600281111561288d5761288d613eca565b03612a215760008989838181106128a6576128a6614369565b90506060020160200160208101906128be919061408d565b9050806001600160a01b03166342842e0e33308d8d878181106128e3576128e3614369565b604080517fffffffff0000000000000000000000000000000000000000000000000000000060e089901b1681526001600160a01b039687166004820152959094166024860152606002919091019190910135604483015250606401600060405180830381600087803b15801561295857600080fd5b505af115801561296c573d6000803e3d6000fd5b50505050806001600160a01b031663095ea7b37f000000000000000000000000c9438f95aa8d9ee1b5edea15c7fa4b2cac723dce8c8c868181106129b2576129b2614369565b905060600201604001356040518363ffffffff1660e01b81526004016129ed9291906001600160a01b03929092168252602082015260400190565b600060405180830381600087803b158015612a0757600080fd5b505af1158015612a1b573d6000803e3d6000fd5b50505050505b80612a2b8161445a565b9150506126b2565b5060008060007f000000000000000000000000c9438f95aa8d9ee1b5edea15c7fa4b2cac723dce6001600160a01b03166349373a198833898f8f6040518663ffffffff1660e01b8152600401612a8c94939291906144c2565b60006040518083038185885af1158015612aaa573d6000803e3d6000fd5b50505050506040513d6000823e601f3d908101601f19168201604052612ad391908101906145dc565b9250925092508b6001600160a01b0316336001600160a01b0316877fe856d9c527bc013d9955698be44d4c7e13739fa87594a8933c0d942daf41e483868686604051612b21939291906146c2565b60405180910390a450939650505050505050612b3d6001600055565b9392505050565b612b4c6132bd565b6001600160a01b038116612b8c576040517f8579befe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6005546001600160a01b0390811690821603612bd4576040517f62dd97a500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600480547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383169081179091556040517f5ea4303afd5550bc5eb446bc464116608045996cc44a0d3ec3ccfa9650aa56ff90600090a250565b612c3e613317565b816006548110612c905760405162461bcd60e51b815260206004820152601260248201527f426174746c65206d757374206578697374210000000000000000000000000000604482015260640161061d565b6000838152600960205260409020805462010000900460ff1615612ce0576040517fc3d8a04200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600084815260096020526040902054610100900460ff16612d2d576040517f60e178dc00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b805460ff1615612d69576040517f5f7d9fd600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600281015433620100009091046001600160a01b031603612dcd576002810154610100900460ff1615612dc8576040517f8783db4b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612e5e565b600481015433620100009091046001600160a01b031603612e2c576004810154610100900460ff1615612dc8576040517f8783db4b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f38db7f2800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000612e8f612e896040518060400160405280888152602001336001600160a01b0316815250612417565b8561356f565b6005549091506001600160a01b03808316911614612ed9576040517fb4f0c44f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6002808301805461ff0019908116610100908117835560048601805483168217905560008981526009602052604090208654815460ff918216151560ff19808316821785558a5486900484161515860291871661ffff1993841617919091178085558a5462010000908190048516151581027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffff90921691909117855560018b81015490860155875498850180549985161515928a16831781558854879004909416151590950295169616959095179290921780855583546001600160a01b0390839004169091027fffffffffffffffffffff0000000000000000000000000000000000000000ffff909116178355600380860180548795939493926130019290860191613ba3565b50505060048281018054918301805460ff938416151560ff19821681178355835461010090819004909516151590940261ff001990941661ffff19909116179290921780835581546001600160a01b03620100009182900416027fffffffffffffffffffff0000000000000000000000000000000000000000ffff909116178255600580850180549293926130999286019190613ba3565b50506040513393508892507f8d7df0ced4aeda0e69d998705dddbf1eac70be8d7fb991edc80ddf6f29424a739150600090a360028201546040517fd9caed12000000000000000000000000000000000000000000000000000000008152620100009091046001600160a01b03908116600483018190526024830152604482018790527f000000000000000000000000c9438f95aa8d9ee1b5edea15c7fa4b2cac723dce169063d9caed1290606401600060405180830381600087803b15801561316157600080fd5b505af1158015613175573d6000803e3d6000fd5b505050506004828101546040517fd9caed120000000000000000000000000000000000000000000000000000000081526001600160a01b036201000090920482169281018390526024810192909252604482018790527f000000000000000000000000c9438f95aa8d9ee1b5edea15c7fa4b2cac723dce169063d9caed1290606401612362565b6132046132bd565b6001600160a01b0381166132805760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f6464726573730000000000000000000000000000000000000000000000000000606482015260840161061d565b6114be81613461565b60006020835110156132a55761329e836135db565b9050612489565b816132b084826147d0565b5060ff9392505050565b90565b6001546001600160a01b0316331461195f5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161061d565b6002600054036133695760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015260640161061d565b6002600055565b81601452806034526fa9059cbb00000000000000000000000060005260206000604460106000875af13d1560016000511417166133b5576390b8ec186000526004601cfd5b6000603452505050565b60405181606052826040528360601b602c526f23b872dd000000000000000000000000600c52602060006064601c6000895af13d15600160005114171661340e57637939f4246000526004601cfd5b600060605260405250505050565b81601452806034526f095ea7b300000000000000000000000060005260206000604460106000875af13d1560016000511417166133b557633e3f8f736000526004601cfd5b600180546001600160a01b038381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b606060ff83146134de5761329e83613632565b8180546134ea90614735565b80601f016020809104026020016040519081016040528092919081815260200182805461351690614735565b80156135635780601f1061353857610100808354040283529160200191613563565b820191906000526020600020905b81548152906001019060200180831161354657829003601f168201915b50505050509050612489565b600080600061357e8585613671565b9150915061358b816136b6565b509392505050565b60006124896135a061381b565b836040517f19010000000000000000000000000000000000000000000000000000000000008152600281019290925260228201526042902090565b600080829050601f8151111561361f57826040517f305a27a900000000000000000000000000000000000000000000000000000000815260040161061d91906148cc565b805161362a826148df565b179392505050565b6060600061363f8361394b565b604080516020808252818301909252919250600091906020820181803683375050509182525060208101929092525090565b60008082516041036136a75760208301516040840151606085015160001a61369b8782858561398c565b945094505050506136af565b506000905060025b9250929050565b60008160048111156136ca576136ca613eca565b036136d25750565b60018160048111156136e6576136e6613eca565b036137335760405162461bcd60e51b815260206004820152601860248201527f45434453413a20696e76616c6964207369676e61747572650000000000000000604482015260640161061d565b600281600481111561374757613747613eca565b036137945760405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e67746800604482015260640161061d565b60038160048111156137a8576137a8613eca565b036114be5760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c60448201527f7565000000000000000000000000000000000000000000000000000000000000606482015260840161061d565b6000306001600160a01b037f000000000000000000000000c15568330926e2a6f1519992b0364ca00faf6a7a1614801561387457507f0000000000000000000000000000000000000000000000000000000000013e3146145b1561389e57507fd46e728c38c18eb1a89c4f0a8e89d44a0146840660ab185dfb2d23aa9eb8881790565b613946604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60208201527f3ed963a35bd590a6005a9cfd187471e7f8ff4daa48015921ccbad855561c627d918101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260009060c00160405160208183030381529060405280519060200120905090565b905090565b600060ff8216601f811115612489576040517fb3512b0c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a08311156139c35750600090506003613a47565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa158015613a17573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b038116613a4057600060019250925050613a47565b9150600090505b94509492505050565b828054828255906000526020600020906002028101928215613b055760005260206000209160020282015b82811115613b0557825482548491849160ff90911690829060ff19166001836002811115613aab57613aab613eca565b0217905550815481547fffffffffffffffffffffff0000000000000000000000000000000000000000ff16610100918290046001600160a01b03169091021781556001918201549101556002928301929190910190613a7b565b50613b11929150613c58565b5090565b6040518060c0016040528060001515815260200160001515815260200160001515815260200160008152602001613b7a604051806080016040528060001515815260200160001515815260200160006001600160a01b03168152602001606081525090565b815260408051608081018252600080825260208281018290529282015260608082015291015290565b828054828255906000526020600020906002028101928215613b055760005260206000209160020282015b82811115613b0557825482548491849160ff90911690829060ff19166001836002811115613bfe57613bfe613eca565b0217905550815481547fffffffffffffffffffffff0000000000000000000000000000000000000000ff16610100918290046001600160a01b03169091021781556001918201549101556002928301929190910190613bce565b5b80821115613b115780547fffffffffffffffffffffff00000000000000000000000000000000000000000016815560006001820155600201613c59565b6001600160a01b03811681146114be57600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff81118282101715613d0357613d03613cab565b604052919050565b600082601f830112613d1c57600080fd5b813567ffffffffffffffff811115613d3657613d36613cab565b613d496020601f19601f84011601613cda565b818152846020838601011115613d5e57600080fd5b816020850160208301376000918101602001919091529392505050565b60008060008060808587031215613d9157600080fd5b8435613d9c81613c96565b93506020850135613dac81613c96565b925060408501359150606085013567ffffffffffffffff811115613dcf57600080fd5b613ddb87828801613d0b565b91505092959194509250565b600060208284031215613df957600080fd5b5035919050565b60008060408385031215613e1357600080fd5b8235613e1e81613c96565b91506020830135613e2e81613c96565b809150509250929050565b60008083601f840112613e4b57600080fd5b50813567ffffffffffffffff811115613e6357600080fd5b6020830191508360206060830285010111156136af57600080fd5b600080600060408486031215613e9357600080fd5b83359250602084013567ffffffffffffffff811115613eb157600080fd5b613ebd86828701613e39565b9497909650939450505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b60038110613f30577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b9052565b6000608083018251151584526020808401511515818601526040808501516001600160a01b038082168389015260609150818701516080838a015285815180885260a08b0191508683019750600092505b80831015613fbf578751613f9a838251613ef9565b8088015185168389015286015186830152968601966001929092019190840190613f85565b509998505050505050505050565b86151581528515156020820152841515604082015283606082015260c060808201526000613ffe60c0830185613f34565b82810360a08401526140108185613f34565b9998505050505050505050565b60208152815115156020820152602082015115156040820152604082015115156060820152606082015160808201526000608083015160c060a084015261406760e0840182613f34565b905060a0840151601f198483030160c08501526140848282613f34565b95945050505050565b60006020828403121561409f57600080fd5b8135612b3d81613c96565b6000815180845260005b818110156140d0576020818501810151868301820152016140b4565b506000602082860101526020601f19601f83011685010191505092915050565b600081518084526020808501945080840160005b8381101561412057815187529582019590820190600101614104565b509495945050505050565b7fff000000000000000000000000000000000000000000000000000000000000008816815260e06020820152600061416660e08301896140aa565b828103604084015261417881896140aa565b90508660608401526001600160a01b03861660808401528460a084015282810360c08401526141a781856140f0565b9a9950505050505050505050565b6000806000606084860312156141ca57600080fd5b83356141d581613c96565b925060208401356141e581613c96565b929592945050506040919091013590565b6000806040838503121561420957600080fd5b82359150602083013567ffffffffffffffff81111561422757600080fd5b61423385828601613d0b565b9150509250929050565b60006040828403121561424f57600080fd5b6040516040810181811067ffffffffffffffff8211171561427257614272613cab565b60405282358152602083013561428781613c96565b60208201529392505050565b6000806000604084860312156142a857600080fd5b83356142b381613c96565b9250602084013567ffffffffffffffff811115613eb157600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b8082028115828204841417612489576124896142cf565b60008261434b577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b60006020828403121561436257600080fd5b5051919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600381106114be57600080fd5b81356143b081614398565b600381106143e7577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b815460ff821691508160ff198216178355602084013561440681613c96565b74ffffffffffffffffffffffffffffffffffffffff008160081b16837fffffffffffffffffffffff000000000000000000000000000000000000000000841617178455505050604082013560018201555050565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361448b5761448b6142cf565b5060010190565b80820180821115612489576124896142cf565b6000602082840312156144b757600080fd5b8135612b3d81614398565b600060608083016001600160a01b03808916855260208881870152604084818801528388855260808801905089945060005b8981101561453d57853561450781614398565b6145118382613ef9565b508386013561451f81613c96565b851682850152858301358383015294860194908601906001016144f4565b509b9a5050505050505050505050565b600067ffffffffffffffff82111561456757614567613cab565b5060051b60200190565b600082601f83011261458257600080fd5b815160206145976145928361454d565b613cda565b82815260059290921b840181019181810190868411156145b657600080fd5b8286015b848110156145d157805183529183019183016145ba565b509695505050505050565b6000806000606084860312156145f157600080fd5b835167ffffffffffffffff8082111561460957600080fd5b61461587838801614571565b945060209150818601518181111561462c57600080fd5b8601601f8101881361463d57600080fd5b805161464b6145928261454d565b81815260059190911b8201840190848101908a83111561466a57600080fd5b928501925b8284101561469157835161468281613c96565b8252928501929085019061466f565b60408a01519097509450505050808211156146ab57600080fd5b506146b886828701614571565b9150509250925092565b6060815260006146d560608301866140f0565b82810360208481019190915285518083528682019282019060005b818110156147155784516001600160a01b0316835293830193918301916001016146f0565b5050848103604086015261472981876140f0565b98975050505050505050565b600181811c9082168061474957607f821691505b602082108103610667577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b601f821115611d0957600081815260208120601f850160051c810160208610156147a95750805b601f850160051c820191505b818110156147c8578281556001016147b5565b505050505050565b815167ffffffffffffffff8111156147ea576147ea613cab565b6147fe816147f88454614735565b84614782565b602080601f831160018114614851576000841561481b5750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b1785556147c8565b600085815260208120601f198616915b8281101561488057888601518255948401946001909101908401614861565b50858210156148bc57878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b01905550565b602081526000612b3d60208301846140aa565b80516020808301519190811015610667577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60209190910360031b1b1691905056fea2646970667358221220156769e0a178c2060aeb8c7d6387738abc8cf9ba62c1a5a21b07bac628e8d48064736f6c63430008120033

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

000000000000000000000000be16d5c286aa885a2261a6b99a518e5effd2f3880000000000000000000000001d28206358d30e84f985ac6edbc6ae49f25ffa7f000000000000000000000000b0e7d4175be8e83a8075538f2dbe6f87d5a69013

-----Decoded View---------------
Arg [0] : _owner (address): 0xbE16D5c286aa885A2261a6b99A518E5eFFD2f388
Arg [1] : _judge (address): 0x1D28206358d30E84F985ac6EdbC6Ae49F25FfA7F
Arg [2] : _withdrawJudge (address): 0xB0e7d4175BE8E83a8075538f2DBe6F87d5a69013

-----Encoded View---------------
3 Constructor Arguments found :
Arg [0] : 000000000000000000000000be16d5c286aa885a2261a6b99a518e5effd2f388
Arg [1] : 0000000000000000000000001d28206358d30e84f985ac6edbc6ae49f25ffa7f
Arg [2] : 000000000000000000000000b0e7d4175be8e83a8075538f2dbe6f87d5a69013


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
[ 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.