Sepolia Testnet

Contract

0xb24696afAe649023D95F46ECa348E4482aD77dfd

Overview

ETH Balance

0 ETH

Token Holdings

Multichain Info

N/A
Transaction Hash
Method
Block
From
To
Approve76449272025-02-05 12:34:362 days ago1738758876IN
0xb24696af...82aD77dfd
0 ETH0.0008606635.44171444
Permit76319082025-02-03 14:58:364 days ago1738594716IN
0xb24696af...82aD77dfd
0 ETH0.002198140.34629883
Approve72711662024-12-13 15:09:4856 days ago1734102588IN
0xb24696af...82aD77dfd
0 ETH0.0006666727.45335774
Approve72435952024-12-09 12:46:3660 days ago1733748396IN
0xb24696af...82aD77dfd
0 ETH0.000586624.15586377
Approve68142922024-10-04 16:30:00126 days ago1728059400IN
0xb24696af...82aD77dfd
0 ETH0.01082922445.94053727
Approve67903662024-09-30 22:12:12129 days ago1727734332IN
0xb24696af...82aD77dfd
0 ETH0.003833157.8405903
Permit67402542024-09-22 17:03:36138 days ago1727024616IN
0xb24696af...82aD77dfd
0 ETH0.0010889319.98734928
Approve66762932024-09-12 6:21:00148 days ago1726122060IN
0xb24696af...82aD77dfd
0 ETH0.0006166823.46249482
Approve66762932024-09-12 6:21:00148 days ago1726122060IN
0xb24696af...82aD77dfd
0 ETH0.0010927845
Approve65558182024-08-23 12:17:12168 days ago1724415432IN
0xb24696af...82aD77dfd
0 ETH0.000149646.16210454
Approve64444652024-08-05 23:18:00185 days ago1722899880IN
0xb24696af...82aD77dfd
0 ETH0.0006212413.44444346
Permit63611282024-07-23 9:16:00199 days ago1721726160IN
0xb24696af...82aD77dfd
0 ETH0.0008320522.25873089
Approve63605562024-07-23 6:52:36199 days ago1721717556IN
0xb24696af...82aD77dfd
0 ETH0.000033491.27430753
Mint63570752024-07-22 16:28:00200 days ago1721665680IN
0xb24696af...82aD77dfd
0 ETH0.000763486.29629436
Mint63570132024-07-22 16:11:24200 days ago1721664684IN
0xb24696af...82aD77dfd
0 ETH0.000840686.93825975
Mint63569952024-07-22 16:07:36200 days ago1721664456IN
0xb24696af...82aD77dfd
0 ETH0.000848436.99679572
Mint63427212024-07-20 3:53:24202 days ago1721447604IN
0xb24696af...82aD77dfd
0 ETH0.000181851.50073144
Mint63422582024-07-20 2:01:00202 days ago1721440860IN
0xb24696af...82aD77dfd
0 ETH0.000181851.50089046
Mint63410582024-07-19 21:01:24202 days ago1721422884IN
0xb24696af...82aD77dfd
0 ETH0.000186031.53414883
Permit63174532024-07-15 22:01:12206 days ago1721080872IN
0xb24696af...82aD77dfd
0 ETH0.000057091.52731671
Permit62910512024-07-11 17:28:12211 days ago1720718892IN
0xb24696af...82aD77dfd
0 ETH0.000230936.17977216
Approve62380262024-07-03 12:41:00219 days ago1720010460IN
0xb24696af...82aD77dfd
0 ETH0.0009887737.61895382
Approve62380232024-07-03 12:40:24219 days ago1720010424IN
0xb24696af...82aD77dfd
0 ETH0.0009379235.68435366
Approve62380082024-07-03 12:37:00219 days ago1720010220IN
0xb24696af...82aD77dfd
0 ETH0.000629823.96157639
Approve62379992024-07-03 12:35:00219 days ago1720010100IN
0xb24696af...82aD77dfd
0 ETH0.0006275423.87565344
View all transactions

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
lnBTC

Compiler Version
v0.8.19+commit.7dd6d404

Optimization Enabled:
Yes with 200 runs

Other Settings:
paris EvmVersion

Contract Source Code (Solidity Standard Json-Input format)

File 1 of 22 : lnBTC.sol
// SPDX-FileCopyrightText: 2022 STROOM <[email protected]>

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.18;

// NOTE: We're not using OZ ERC20 implementation because it's not compatible with solmate
import {ERC20} from "solmate/tokens/ERC20.sol";

import "openzeppelin-contracts/contracts/access/Ownable.sol";
import "openzeppelin-contracts/contracts/security/Pausable.sol";

import "openzeppelin-contracts/contracts/utils/cryptography/ECDSA.sol";
import "openzeppelin-contracts/contracts/utils/cryptography/SignatureChecker.sol";

import "./lib/BitcoinUtils.sol";
import "./lib/ValidatorMessageReceiver.sol";
import "./lib/LnBtcUtils.sol";

import "./ILnBTC.sol";

/**
 * @title lnBTC
 * @dev The lnBTC contract is an ERC20 implementation representing Bitcoins staked into Lightning Network via Stroom.network
 */
contract lnBTC is
    ERC20,
    ValidatorMessageReceiver,
    BitcoinUtils,
    Pausable,
    ILnBTC
{
    BitcoinUtils.Network public network;

    event RedeemBtcEvent(
        address indexed _from,
        string _BTCAddress,
        uint256 _value,
        uint256 _id
    );
    event MintBtcEvent(
        address indexed _to,
        uint256 _value,
        bytes32 _btcDepositId
    );

    uint256 public constant DUST_LIMIT = 546; // sat

    /**
     * @dev `minWithdrawAmount` represents the minimum amount of satoshis that can be withdrawn.
     */
    uint256 public minWithdrawAmount = 100_000; // 0.001 BTC

    uint256 public redeemCounter;

    address public minter;

    mapping(bytes32 => bool) public btcDepositIds;

    constructor(
        BitcoinUtils.Network _network
    ) ERC20("Stroom Bitcoin", "stBTC", 8) {
        redeemCounter = 0;

        // TODO: deduce from chain id
        network = _network;

        minter = msg.sender;
    }

    // ========= Minting Signature ======

    /**
     * @dev Calculates the hash of an invoice.
     * @return The hash of the invoice.
     */
    function getMintInvoiceHash(
        MintInvoice calldata invoice
    ) public pure returns (bytes32) {
        return getInvoiceHash(invoice);
    }

    // ========= Owner-only ========

    /**
     * @dev Function to stop the contract (Pausable pattern).
     */
    function pause() public onlyOwner {
        _pause();
    }

    /**
     * @dev Function to resume the contract (Pausable pattern).
     */
    function unpause() public onlyOwner {
        _unpause();
    }

    /**
     * @dev Sets the minimum withdrawal amount.
     * @param _minWithdrawAmount The new minimum withdrawal amount.
     * @notice Only the contract owner can call this function.
     */
    function setMinWithdrawAmount(uint256 _minWithdrawAmount) public onlyOwner {
        require(
            _minWithdrawAmount >= DUST_LIMIT,
            "Min withdraw amount should be greater or equal to dust limit"
        );

        minWithdrawAmount = _minWithdrawAmount;
    }

    /**
     * @dev Sets the minter address.
     * @param _minter The address of the minter.
     * @notice Only the contract owner can call this function.
     */
    function setMinter(address _minter) public onlyOwner {
        minter = _minter;
    }

    /**
     * @dev Mint new tokens.
     * Only the minter can call this function.
     * @param _amount The amount of tokens to mint.
     * @param _recipient The address that will receive the minted tokens.
     * @param _btcDepositId The id of the BTC deposit = keccak256(txHash, vout)
     */
    function mint(
        uint256 _amount,
        address _recipient,
        bytes32 _btcDepositId
    ) public whenNotPaused {
        require(msg.sender == minter, "lnBTC: only minter allowed to mint");
        _mint(_amount, _recipient, _btcDepositId);
    }

    /**
     * @dev Mints `_amount` of BTC to `_recipient` with a signature.
     * Anyone can use if they have valid signature from the owner.
     */
    function mint(
        MintInvoice calldata invoice,
        bytes calldata signature
    ) public whenNotPaused onlyValidator(getMintInvoiceHash(invoice), signature) {
        // TODO: maybe emit different event when minted by signature
        _mint(invoice.amount, invoice.recipient, invoice.btcDepositId);
    }

    /**
     * @dev Mint new tokens.
     * @param _amount The amount of tokens to mint.
     * @param _recipient The address that will receive the minted tokens.
     * @param _btcDepositId The id of the BTC deposit = keccak256(txHash, vout)
     */
    function _mint(
        uint256 _amount,
        address _recipient,
        bytes32 _btcDepositId
    ) internal {
        require(_amount > 0, "MINT_AMOUNT_ZERO");
        require(_amount < 21_000_000 * BTC, "MINT_AMOUNT_TOO_BIG");

        require(_recipient != address(this), "MINT_TO_THE_CONTRACT_ADDRESS");

        require(
            btcDepositIds[_btcDepositId] == false,
            "MINT_ALREADY_PROCESSED"
        );
        btcDepositIds[_btcDepositId] = true;

        _mint(_recipient, _amount);

        emit MintBtcEvent(_recipient, _amount, _btcDepositId);
    }

    /**
     * @dev Burns `_amount` of BTC.
     * Function is called by the user.
     * @param _amount The amount of tokens to burn.
     * @param BTCAddress The BTC address to send the BTC to.
     * @notice The BTC address must be valid.
     */
    function redeem(
        uint256 _amount,
        string calldata BTCAddress
    ) public whenNotPaused {
        require(
            _amount >= minWithdrawAmount,
            "The sent value must be greater or equal to min withdraw amount"
        );
        require(
            validateBitcoinAddress(network, BTCAddress),
            "The sent BTC address is not valid"
        );

        // balance check in the following function
        _burn(msg.sender, _amount);

        redeemCounter += 1;
        emit RedeemBtcEvent(msg.sender, BTCAddress, _amount, redeemCounter);
    }
}

File 2 of 22 : ERC20.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)
/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)
/// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.
abstract contract ERC20 {
    /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

    event Transfer(address indexed from, address indexed to, uint256 amount);

    event Approval(address indexed owner, address indexed spender, uint256 amount);

    /*//////////////////////////////////////////////////////////////
                            METADATA STORAGE
    //////////////////////////////////////////////////////////////*/

    string public name;

    string public symbol;

    uint8 public immutable decimals;

    /*//////////////////////////////////////////////////////////////
                              ERC20 STORAGE
    //////////////////////////////////////////////////////////////*/

    uint256 public totalSupply;

    mapping(address => uint256) public balanceOf;

    mapping(address => mapping(address => uint256)) public allowance;

    /*//////////////////////////////////////////////////////////////
                            EIP-2612 STORAGE
    //////////////////////////////////////////////////////////////*/

    uint256 internal immutable INITIAL_CHAIN_ID;

    bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;

    mapping(address => uint256) public nonces;

    /*//////////////////////////////////////////////////////////////
                               CONSTRUCTOR
    //////////////////////////////////////////////////////////////*/

    constructor(
        string memory _name,
        string memory _symbol,
        uint8 _decimals
    ) {
        name = _name;
        symbol = _symbol;
        decimals = _decimals;

        INITIAL_CHAIN_ID = block.chainid;
        INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
    }

    /*//////////////////////////////////////////////////////////////
                               ERC20 LOGIC
    //////////////////////////////////////////////////////////////*/

    function approve(address spender, uint256 amount) public virtual returns (bool) {
        allowance[msg.sender][spender] = amount;

        emit Approval(msg.sender, spender, amount);

        return true;
    }

    function transfer(address to, uint256 amount) public virtual returns (bool) {
        balanceOf[msg.sender] -= amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(msg.sender, to, amount);

        return true;
    }

    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) public virtual returns (bool) {
        uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.

        if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;

        balanceOf[from] -= amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(from, to, amount);

        return true;
    }

    /*//////////////////////////////////////////////////////////////
                             EIP-2612 LOGIC
    //////////////////////////////////////////////////////////////*/

    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) public virtual {
        require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");

        // Unchecked because the only math done is incrementing
        // the owner's nonce which cannot realistically overflow.
        unchecked {
            address recoveredAddress = ecrecover(
                keccak256(
                    abi.encodePacked(
                        "\x19\x01",
                        DOMAIN_SEPARATOR(),
                        keccak256(
                            abi.encode(
                                keccak256(
                                    "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
                                ),
                                owner,
                                spender,
                                value,
                                nonces[owner]++,
                                deadline
                            )
                        )
                    )
                ),
                v,
                r,
                s
            );

            require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");

            allowance[recoveredAddress][spender] = value;
        }

        emit Approval(owner, spender, value);
    }

    function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
        return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
    }

    function computeDomainSeparator() internal view virtual returns (bytes32) {
        return
            keccak256(
                abi.encode(
                    keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
                    keccak256(bytes(name)),
                    keccak256("1"),
                    block.chainid,
                    address(this)
                )
            );
    }

    /*//////////////////////////////////////////////////////////////
                        INTERNAL MINT/BURN LOGIC
    //////////////////////////////////////////////////////////////*/

    function _mint(address to, uint256 amount) internal virtual {
        totalSupply += amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(address(0), to, amount);
    }

    function _burn(address from, uint256 amount) internal virtual {
        balanceOf[from] -= amount;

        // Cannot underflow because a user's balance
        // will never be larger than the total supply.
        unchecked {
            totalSupply -= amount;
        }

        emit Transfer(from, address(0), amount);
    }
}

File 3 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 4 of 22 : Pausable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol)

pragma solidity ^0.8.0;

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

/**
 * @dev Contract module which allows children to implement an emergency stop
 * mechanism that can be triggered by an authorized account.
 *
 * This module is used through inheritance. It will make available the
 * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
 * the functions of your contract. Note that they will not be pausable by
 * simply including this module, only once the modifiers are put in place.
 */
abstract contract Pausable is Context {
    /**
     * @dev Emitted when the pause is triggered by `account`.
     */
    event Paused(address account);

    /**
     * @dev Emitted when the pause is lifted by `account`.
     */
    event Unpaused(address account);

    bool private _paused;

    /**
     * @dev Initializes the contract in unpaused state.
     */
    constructor() {
        _paused = false;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is not paused.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    modifier whenNotPaused() {
        _requireNotPaused();
        _;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is paused.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    modifier whenPaused() {
        _requirePaused();
        _;
    }

    /**
     * @dev Returns true if the contract is paused, and false otherwise.
     */
    function paused() public view virtual returns (bool) {
        return _paused;
    }

    /**
     * @dev Throws if the contract is paused.
     */
    function _requireNotPaused() internal view virtual {
        require(!paused(), "Pausable: paused");
    }

    /**
     * @dev Throws if the contract is not paused.
     */
    function _requirePaused() internal view virtual {
        require(paused(), "Pausable: not paused");
    }

    /**
     * @dev Triggers stopped state.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    function _pause() internal virtual whenNotPaused {
        _paused = true;
        emit Paused(_msgSender());
    }

    /**
     * @dev Returns to normal state.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    function _unpause() internal virtual whenPaused {
        _paused = false;
        emit Unpaused(_msgSender());
    }
}

File 5 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 6 of 22 : SignatureChecker.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/cryptography/SignatureChecker.sol)

pragma solidity ^0.8.0;

import "./ECDSA.sol";
import "../../interfaces/IERC1271.sol";

/**
 * @dev Signature verification helper that can be used instead of `ECDSA.recover` to seamlessly support both ECDSA
 * signatures from externally owned accounts (EOAs) as well as ERC1271 signatures from smart contract wallets like
 * Argent and Gnosis Safe.
 *
 * _Available since v4.1._
 */
library SignatureChecker {
    /**
     * @dev Checks if a signature is valid for a given signer and data hash. If the signer is a smart contract, the
     * signature is validated against that smart contract using ERC1271, otherwise it's validated using `ECDSA.recover`.
     *
     * NOTE: Unlike ECDSA signatures, contract signatures are revocable, and the outcome of this function can thus
     * change through time. It could return true at block N and false at block N+1 (or the opposite).
     */
    function isValidSignatureNow(address signer, bytes32 hash, bytes memory signature) internal view returns (bool) {
        (address recovered, ECDSA.RecoverError error) = ECDSA.tryRecover(hash, signature);
        return
            (error == ECDSA.RecoverError.NoError && recovered == signer) ||
            isValidERC1271SignatureNow(signer, hash, signature);
    }

    /**
     * @dev Checks if a signature is valid for a given signer and data hash. The signature is validated
     * against the signer smart contract using ERC1271.
     *
     * NOTE: Unlike ECDSA signatures, contract signatures are revocable, and the outcome of this function can thus
     * change through time. It could return true at block N and false at block N+1 (or the opposite).
     */
    function isValidERC1271SignatureNow(
        address signer,
        bytes32 hash,
        bytes memory signature
    ) internal view returns (bool) {
        (bool success, bytes memory result) = signer.staticcall(
            abi.encodeWithSelector(IERC1271.isValidSignature.selector, hash, signature)
        );
        return (success &&
            result.length >= 32 &&
            abi.decode(result, (bytes32)) == bytes32(IERC1271.isValidSignature.selector));
    }
}

File 7 of 22 : BitcoinUtils.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.4;

import "forge-std/console.sol";

// TODO: import from submodules instead including in the project?
import "./Base58.sol";

// TODO: make it a library, and auto-link inside go-bindings
contract BitcoinUtils {
    // There are currently three invoice address formats in use:

    // P2PKH which begin with the number 1, eg: 1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2
    // P2SH type starting with the number 3, eg: 3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy
    // Bech32 type starting with bc1, eg: bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq

    // Testnet:
    // P2PKH which begin with the number m or n, eg: mipcBbFg9gMiCh81Kj8tqqdgoZub1ZJRfn
    // P2SH type starting with the number 2, eg: 2MzQwSSnBHWHqSAqtTVQ6v47XtaisrJa1Vc
    // Bech32 type starting with tb1, eg: tb1qw508d6qejxtdg4y5r3zarvary0c5xw7kxpjzsx

    // Regtest:
    // P2PKH which begin with the number m or n, eg: mipcBbFg9gMiCh81Kj8tqqdgoZub1ZJRfn
    // P2SH type starting with the number 2, eg: 2MzQwSSnBHWHqSAqtTVQ6v47XtaisrJa1Vc
    // Bech32 type starting with bcrt1, eg: bcrt1qw508d6qejxtdg4y5r3zarvary0c5xw7kxpjzsx

    //NB: don't forget to update `lnbtc_ext.go` when changing this enum!
    enum Network {
        Mainnet,
        Testnet,
        Regtest
    }

    string constant BECH32_ALPHABET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";

    function BECH32_ALPHABET_MAP(bytes1 char) public view returns (uint8) {
        // '{"0":15,"2":10,"3":17,"4":21,"5":20,"6":26,"7":30,"8":7,"9":5,"q":0,"p":1,"z":2,"r":3,"y":4,"x":6,"g":8,"f":9,"t":11,"v":12,"d":13,"w":14,"s":16,"j":18,"n":19,"k":22,"h":23,"c":24,"e":25,"m":27,"u":28,"a":29,"l":31}'

        if (char == bytes1("0")) return 15;
        if (char == bytes1("2")) return 10;
        if (char == bytes1("3")) return 17;
        if (char == bytes1("4")) return 21;
        if (char == bytes1("5")) return 20;
        if (char == bytes1("6")) return 26;
        if (char == bytes1("8")) return 7;
        if (char == bytes1("7")) return 30;
        if (char == bytes1("9")) return 5;

        if (char == bytes1("q")) return 0;
        if (char == bytes1("p")) return 1;
        if (char == bytes1("z")) return 2;
        if (char == bytes1("r")) return 3;
        if (char == bytes1("y")) return 4;
        if (char == bytes1("x")) return 6;
        if (char == bytes1("g")) return 8;
        if (char == bytes1("f")) return 9;
        if (char == bytes1("t")) return 11;
        if (char == bytes1("v")) return 12;
        if (char == bytes1("d")) return 13;
        if (char == bytes1("w")) return 14;
        if (char == bytes1("s")) return 16;
        if (char == bytes1("j")) return 18;
        if (char == bytes1("n")) return 19;
        if (char == bytes1("k")) return 22;
        if (char == bytes1("h")) return 23;
        if (char == bytes1("c")) return 24;
        if (char == bytes1("e")) return 25;
        if (char == bytes1("m")) return 27;
        if (char == bytes1("u")) return 28;
        if (char == bytes1("a")) return 29;
        if (char == bytes1("l")) return 31;

        console.log("Invalid character");
        console.logBytes1(char);
        // revert("Invalid character");

        return type(uint8).max;
    }

    // const ALPHABET_MAP: { [key: string]: number } = {};
    // for (let z = 0; z < ALPHABET.length; z++) {
    //   const x = ALPHABET.charAt(z);
    //   ALPHABET_MAP[x] = z;
    // }

    bytes constant BTC_P2PKH = hex"31"; // prefix = 1
    bytes constant BTC_P2SH = hex"33"; // prefix = 3

    bytes constant BTC_BECH32_MAINNET = hex"626331"; // prefix = bc1
    bytes constant BTC_BECH32_REGTEST = hex"6263727431"; // prefix = bcrt1
    bytes constant BTC_BECH32_TESTNET = hex"746231"; // prefix = tb1

    function getBtcBech32Prefix(Network network) public pure returns (bytes memory) {
        if (network == Network.Mainnet) {
            return BTC_BECH32_MAINNET;
        } else if (network == Network.Regtest) {
            return BTC_BECH32_REGTEST;
        } else if (network == Network.Testnet) {
            return BTC_BECH32_TESTNET;
        } else {
            revert("Unknown network type");
        }
    }

    function validateBitcoinAddress(Network network, string calldata BTCAddress) public view returns (bool) {
        bytes memory empty;

        if (equalBytes(bytes(BTCAddress), empty)) return false;

        if (equalBytes(bytes(BTCAddress)[: 1], BTC_P2PKH) || equalBytes(bytes(BTCAddress)[: 1], BTC_P2SH)) {
            if (bytes(BTCAddress).length < 26 || bytes(BTCAddress).length > 35 || !alphabetCheck(bytes(BTCAddress))) {
                return false;
            }

            // check base58 checksum and encoding
            return validateBase58Checksum(BTCAddress);
        }

        bytes memory prefix = getBtcBech32Prefix(network);
        if (equalBytes(bytes(BTCAddress)[: prefix.length], prefix)) {
            if (network == Network.Regtest) {
                if (bytes(BTCAddress).length < 43 || bytes(BTCAddress).length > 63) return false;
            } else {
                if (bytes(BTCAddress).length < 42 || bytes(BTCAddress).length > 62) return false;
            }

            // check bech32 checksum and encoding
            return validateBech32Checksum(BTCAddress);
        }

        return false;
    }

    function equalBytes(bytes memory one, bytes memory two) public pure returns (bool) {
        if (!(one.length == two.length)) {
            return false;
        }
        for (uint256 i = 0; i < one.length; i++) {
            if (!(one[i] == two[i])) {
                return false;
            }
        }
        return true;
    }

    function alphabetCheck(bytes memory BTCAddress) public pure returns (bool) {
        for (uint256 i = 0; i < BTCAddress.length; i++) {
            uint8 charCode = uint8(BTCAddress[i]);
            bool contains = isLetter(charCode);
            if (!contains) return false;
        }

        return true;
    }

    function isLetter(uint8 charCode) internal pure returns (bool) {
        if (charCode == 73 || charCode == 79 || charCode == 108) {
            return false;
        }
        if (charCode >= 49 && charCode <= 57) {
            return true;
        }
        if (charCode >= 65 && charCode <= 90) {
            return true;
        }
        if (charCode >= 97 && charCode <= 122) {
            return true;
        }
        return false;
    }

    function validateBech32Checksum(string memory btcAddress) public view returns (bool) {
        // TODO: DOESNT SUPPORT TAPROOT ADDRESSES
        // from https://github.com/bitcoinjs/bech32/blob/master/src/index.ts

        //   function __decode(str: string, LIMIT?: number): Decoded | string {
        //     LIMIT = LIMIT || 90;
        //     if (str.length < 8) return str + ' too short';
        //     if (str.length > LIMIT) return 'Exceeds length limit';

        //     // don't allow mixed case
        //     const lowered = str.toLowerCase();
        //     const uppered = str.toUpperCase();
        //     if (str !== lowered && str !== uppered) return 'Mixed-case string ' + str;
        //     str = lowered;

        //     const split = str.lastIndexOf('1');
        //     if (split === -1) return 'No separator character for ' + str;
        //     if (split === 0) return 'Missing prefix for ' + str;

        //     const prefix = str.slice(0, split);
        //     const wordChars = str.slice(split + 1);
        //     if (wordChars.length < 6) return 'Data too short';

        //     let chk = prefixChk(prefix);
        //     if (typeof chk === 'string') return chk;

        //     const words = [];
        //     for (let i = 0; i < wordChars.length; ++i) {
        //       const c = wordChars.charAt(i);
        //       const v = ALPHABET_MAP[c];
        //       if (v === undefined) return 'Unknown character ' + c;
        //       chk = polymodStep(chk) ^ v;

        //       // not in the checksum?
        //       if (i + 6 >= wordChars.length) continue;
        //       words.push(v);
        //     }

        //     if (chk !== ENCODING_CONST) return 'Invalid checksum for ' + str;
        //     return { prefix, words };
        //   }

        console.log("\nvalidate bech32 checksum");

        console.log("address");
        console.log(btcAddress);

        bytes memory _btcAddress = bytes(btcAddress);

        if (_btcAddress.length < 8) {
            console.log("too short");
            return false;
        }

        if (_btcAddress.length > 90) {
            console.log("too long");
            return false;
        }

        // TODO: don't allow mixed case
        // bytes memory lowered = bytes(toLower(btcAddress));
        // bytes memory uppered = bytes(toUpper(btcAddress));

        // if (
        //     !equalBytes(_btcAddress, lowered) &&
        //     !equalBytes(_btcAddress, uppered)
        // ) {
        //     console.log("mixed case");
        //     return false;
        // }

        _btcAddress = bytes(btcAddress);

        uint256 split = 0;

        for (uint256 i = 0; i < _btcAddress.length; i++) {
            if (_btcAddress[i] == "1") {
                split = i;
                break;
            }
        }

        if (split == 0) {
            console.log("no separator");
            return false;
        }

        if (split == 1) {
            console.log("missing prefix");
            return false;
        }

        bytes memory prefix = new bytes(split);
        bytes memory wordChars = new bytes(_btcAddress.length - split - 1);

        for (uint256 i = 0; i < split; i++) {
            prefix[i] = _btcAddress[i];
        }

        for (uint256 i = 0; i < wordChars.length; i++) {
            wordChars[i] = _btcAddress[i + split + 1];
        }

        console.log("prefix");
        console.logBytes(prefix);

        // console.log("wordChars");
        // console.logBytes(wordChars);

        if (wordChars.length < 6) {
            console.log("data too short");
            return false;
        }

        uint256 chk = prefixChk(bytes(prefix));

        if (chk == 0) {
            console.log("invalid prefix");
            return false;
        }

        bytes memory words = new bytes(wordChars.length);

        for (uint256 i = 0; i < wordChars.length; i++) {
            bytes1 c = wordChars[i];
            uint8 v = BECH32_ALPHABET_MAP(c);

            // ALPHABET_MAP reverts if the character is not in the map, so this is not needed
            if (v == type(uint8).max) {
                console.log("unknown character");
                console.log(i);
                console.logBytes1(c);
                console.log("char", string(abi.encodePacked(c)));
                return false;
            }

            // console.log("v", v);
            // console.log("char", string(abi.encodePacked(c)));

            chk = polymodStep(chk) ^ v;

            // not in the checksum?
            if (i + 6 >= wordChars.length) continue;

            words[i] = bytes1(v);
        }

        console.log("words");
        console.logBytes(words);

        // ENCODING_CONST is bech32 or bech32m
        if (chk != uint256(0x2bc830a3) && chk != 1) {
            console.log("invalid checksum", chk);
            return false;
        }

        console.log("valid checksum", chk);

        return true;
    }

    function polymodStep(uint256 pre) public pure returns (uint256) {
        uint256 b = pre >> 25;

        return (
            ((pre & 0x1ffffff) << 5) ^ ((b >> 0) & 1 == 1 ? 0x3b6a57b2 : 0) ^ ((b >> 1) & 1 == 1 ? 0x26508e6d : 0)
            ^ ((b >> 2) & 1 == 1 ? 0x1ea119fa : 0) ^ ((b >> 3) & 1 == 1 ? 0x3d4233dd : 0)
            ^ ((b >> 4) & 1 == 1 ? 0x2a1462b3 : 0)
        );
    }

    function prefixChk(bytes memory prefix) public pure returns (uint256) {
        uint256 chk = 1;
        for (uint256 i = 0; i < bytes(prefix).length; ++i) {
            uint256 c = uint8(prefix[i]);
            if (c < 33 || c > 126) revert("Invalid prefix");

            chk = polymodStep(chk) ^ (c >> 5);
        }
        chk = polymodStep(chk);

        for (uint256 i = 0; i < prefix.length; ++i) {
            uint256 v = uint8(prefix[i]);
            chk = polymodStep(chk) ^ (v & 0x1f);
        }
        return chk;
    }

    function validateBase58Checksum(string calldata btcAddress) public view returns (bool) {
        bytes memory rawData = decodeFromString(btcAddress);

        console.log("validateBase58Checksum");

        console.log("payload");
        console.logBytes(rawData);

        // raw data is: 1 byte version + 20 bytes of data + 4 bytes of checksum
        if (rawData.length != 25) return false;

        // version is 1 byte

        bytes memory version = new bytes(1);
        version[0] = rawData[0];

        console.log("version");
        console.logBytes(version);

        bytes memory payload = new bytes(rawData.length - 1 - 4);
        for (uint256 i = 0; i < rawData.length - 1 - 4; i++) {
            payload[i] = rawData[i + 1];
        }

        console.log("payload");
        console.logBytes(payload);

        if (payload.length != 20) return false;

        bytes memory checksum = new bytes(4);
        for (uint256 i = 0; i < 4; i++) {
            checksum[i] = rawData[rawData.length - 4 + i];
        }

        console.log("checksum");
        console.logBytes(checksum);

        bytes32 calculateChecksum = sha256(abi.encodePacked(sha256(abi.encodePacked(version, payload))));

        console.log("calculated checksum");
        console.logBytes32(calculateChecksum);

        // if (checksum[0] ^ newChecksum[0] |
        //     checksum[1] ^ newChecksum[1] |
        //     checksum[2] ^ newChecksum[2] |
        //     checksum[3] ^ newChecksum[3]) return

        if (
            (checksum[0] ^ calculateChecksum[0]) | (checksum[1] ^ calculateChecksum[1])
            | (checksum[2] ^ calculateChecksum[2]) | (checksum[3] ^ calculateChecksum[3]) != 0
        ) return false;

        return true;
    }
}

File 8 of 22 : ValidatorMessageReceiver.sol
// SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: 2023 STROOM <[email protected]>

pragma solidity ^0.8.18;

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

import "bip340-solidity/src/Bip340Ecrec.sol";

contract ValidatorMessageReceiver is Ownable, Bip340Ecrec {
    // public key of combined FROST based validator consensus network
    bytes32 public validatorPublicKey;

    constructor() {}

    function getUpdateValidatorPublicKeyMessage(
        bytes32 _validatorPublicKey
    ) public pure returns (bytes32) {
        return keccak256(abi.encodePacked("STROOM_UPDATE_VALIDATOR_PUBLIC_KEY", _validatorPublicKey));
    }

    function setValidatorPublicKey(
        bytes32 _validatorPublicKey
    ) public onlyOwner {
        validatorPublicKey = _validatorPublicKey;
    }

    function setValidatorPublicKeySigned(
        bytes32 _validatorPublicKey,
        bytes calldata signature
    ) public onlyValidator(getUpdateValidatorPublicKeyMessage(_validatorPublicKey), signature) {
        validatorPublicKey = _validatorPublicKey;
    }

    modifier onlyValidator(
        bytes32 hash,
        bytes calldata signature
    ) {
        require(validateMessage(hash, signature), "ValidatorMessageReceiver: INVALID_SIGNATURE");
        _;
    }

    function validateMessage(
        bytes32 hash,
        bytes calldata signature
    ) public view returns (bool) {
        require(validatorPublicKey != 0, "ValidatorMessageReceiver: NO_VALIDATOR_PUBLIC_KEY");

        // slice signature in two bytes32 blocks, rx and s
        uint256 rx = uint256(bytes32(signature[0 : 32]));
        uint256 s = uint256(bytes32(signature[32 : 64]));

        return verify(uint256(validatorPublicKey), rx, s, hash);
    }
}

File 9 of 22 : LnBtcUtils.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

bool constant ADD_SUPPLY = false;
bool constant SUB_SUPPLY = true;

uint256 constant BTC = 1e8; // sat
uint256 constant SHARES_PER_BTC = 1e6;

struct MintInvoice {
    bytes32 btcDepositId;
    address recipient;
    uint256 amount;
}

/**
 * @dev The prefix used for minting invoices.
 */
bytes32 constant MINT_INVOICE_PREFIX = keccak256("STROOM_MINT_INVOICE");

/**
 * @dev Calculates the hash of an invoice.
 * @return The hash of the invoice.
 */
function getInvoiceHash(MintInvoice calldata invoice) pure returns (bytes32) {
    return
        keccak256(
            abi.encodePacked(
                MINT_INVOICE_PREFIX,
                invoice.recipient,
                invoice.amount,
                invoice.btcDepositId
            )
        );
}

function roundDiv(uint256 a, uint256 b) pure returns (uint256) {
    return (a + b / 2) / b;
}

File 10 of 22 : ILnBTC.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

interface ILnBTC {
    function mint(uint256 amount, address to, bytes32 hash) external;

    function redeem(uint256 _amount, string calldata BTCAddress) external;
}

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 : IERC1271.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (interfaces/IERC1271.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC1271 standard signature validation method for
 * contracts as defined in https://eips.ethereum.org/EIPS/eip-1271[ERC-1271].
 *
 * _Available since v4.1._
 */
interface IERC1271 {
    /**
     * @dev Should return whether the signature provided is valid for the provided data
     * @param hash      Hash of the data to be signed
     * @param signature Signature byte array associated with _data
     */
    function isValidSignature(bytes32 hash, bytes memory signature) external view returns (bytes4 magicValue);
}

File 14 of 22 : console.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.4.22 <0.9.0;

library console {
    address constant CONSOLE_ADDRESS = address(0x000000000000000000636F6e736F6c652e6c6f67);

    function _sendLogPayload(bytes memory payload) private view {
        uint256 payloadLength = payload.length;
        address consoleAddress = CONSOLE_ADDRESS;
        /// @solidity memory-safe-assembly
        assembly {
            let payloadStart := add(payload, 32)
            let r := staticcall(gas(), consoleAddress, payloadStart, payloadLength, 0, 0)
        }
    }

    function log() internal view {
        _sendLogPayload(abi.encodeWithSignature("log()"));
    }

    function logInt(int p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(int)", p0));
    }

    function logUint(uint p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint)", p0));
    }

    function logString(string memory p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string)", p0));
    }

    function logBool(bool p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool)", p0));
    }

    function logAddress(address p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address)", p0));
    }

    function logBytes(bytes memory p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes)", p0));
    }

    function logBytes1(bytes1 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes1)", p0));
    }

    function logBytes2(bytes2 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes2)", p0));
    }

    function logBytes3(bytes3 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes3)", p0));
    }

    function logBytes4(bytes4 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes4)", p0));
    }

    function logBytes5(bytes5 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes5)", p0));
    }

    function logBytes6(bytes6 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes6)", p0));
    }

    function logBytes7(bytes7 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes7)", p0));
    }

    function logBytes8(bytes8 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes8)", p0));
    }

    function logBytes9(bytes9 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes9)", p0));
    }

    function logBytes10(bytes10 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes10)", p0));
    }

    function logBytes11(bytes11 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes11)", p0));
    }

    function logBytes12(bytes12 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes12)", p0));
    }

    function logBytes13(bytes13 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes13)", p0));
    }

    function logBytes14(bytes14 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes14)", p0));
    }

    function logBytes15(bytes15 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes15)", p0));
    }

    function logBytes16(bytes16 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes16)", p0));
    }

    function logBytes17(bytes17 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes17)", p0));
    }

    function logBytes18(bytes18 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes18)", p0));
    }

    function logBytes19(bytes19 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes19)", p0));
    }

    function logBytes20(bytes20 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes20)", p0));
    }

    function logBytes21(bytes21 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes21)", p0));
    }

    function logBytes22(bytes22 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes22)", p0));
    }

    function logBytes23(bytes23 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes23)", p0));
    }

    function logBytes24(bytes24 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes24)", p0));
    }

    function logBytes25(bytes25 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes25)", p0));
    }

    function logBytes26(bytes26 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes26)", p0));
    }

    function logBytes27(bytes27 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes27)", p0));
    }

    function logBytes28(bytes28 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes28)", p0));
    }

    function logBytes29(bytes29 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes29)", p0));
    }

    function logBytes30(bytes30 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes30)", p0));
    }

    function logBytes31(bytes31 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes31)", p0));
    }

    function logBytes32(bytes32 p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bytes32)", p0));
    }

    function log(uint p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint)", p0));
    }

    function log(string memory p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string)", p0));
    }

    function log(bool p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool)", p0));
    }

    function log(address p0) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address)", p0));
    }

    function log(uint p0, uint p1) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,uint)", p0, p1));
    }

    function log(uint p0, string memory p1) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,string)", p0, p1));
    }

    function log(uint p0, bool p1) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,bool)", p0, p1));
    }

    function log(uint p0, address p1) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,address)", p0, p1));
    }

    function log(string memory p0, uint p1) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint)", p0, p1));
    }

    function log(string memory p0, string memory p1) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,string)", p0, p1));
    }

    function log(string memory p0, bool p1) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool)", p0, p1));
    }

    function log(string memory p0, address p1) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,address)", p0, p1));
    }

    function log(bool p0, uint p1) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint)", p0, p1));
    }

    function log(bool p0, string memory p1) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string)", p0, p1));
    }

    function log(bool p0, bool p1) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool)", p0, p1));
    }

    function log(bool p0, address p1) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address)", p0, p1));
    }

    function log(address p0, uint p1) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint)", p0, p1));
    }

    function log(address p0, string memory p1) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,string)", p0, p1));
    }

    function log(address p0, bool p1) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool)", p0, p1));
    }

    function log(address p0, address p1) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,address)", p0, p1));
    }

    function log(uint p0, uint p1, uint p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint)", p0, p1, p2));
    }

    function log(uint p0, uint p1, string memory p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,uint,string)", p0, p1, p2));
    }

    function log(uint p0, uint p1, bool p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool)", p0, p1, p2));
    }

    function log(uint p0, uint p1, address p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,uint,address)", p0, p1, p2));
    }

    function log(uint p0, string memory p1, uint p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,string,uint)", p0, p1, p2));
    }

    function log(uint p0, string memory p1, string memory p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,string,string)", p0, p1, p2));
    }

    function log(uint p0, string memory p1, bool p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,string,bool)", p0, p1, p2));
    }

    function log(uint p0, string memory p1, address p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,string,address)", p0, p1, p2));
    }

    function log(uint p0, bool p1, uint p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint)", p0, p1, p2));
    }

    function log(uint p0, bool p1, string memory p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,bool,string)", p0, p1, p2));
    }

    function log(uint p0, bool p1, bool p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool)", p0, p1, p2));
    }

    function log(uint p0, bool p1, address p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,bool,address)", p0, p1, p2));
    }

    function log(uint p0, address p1, uint p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,address,uint)", p0, p1, p2));
    }

    function log(uint p0, address p1, string memory p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,address,string)", p0, p1, p2));
    }

    function log(uint p0, address p1, bool p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,address,bool)", p0, p1, p2));
    }

    function log(uint p0, address p1, address p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,address,address)", p0, p1, p2));
    }

    function log(string memory p0, uint p1, uint p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint,uint)", p0, p1, p2));
    }

    function log(string memory p0, uint p1, string memory p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint,string)", p0, p1, p2));
    }

    function log(string memory p0, uint p1, bool p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint,bool)", p0, p1, p2));
    }

    function log(string memory p0, uint p1, address p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint,address)", p0, p1, p2));
    }

    function log(string memory p0, string memory p1, uint p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,uint)", p0, p1, p2));
    }

    function log(string memory p0, string memory p1, string memory p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,string)", p0, p1, p2));
    }

    function log(string memory p0, string memory p1, bool p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,bool)", p0, p1, p2));
    }

    function log(string memory p0, string memory p1, address p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,address)", p0, p1, p2));
    }

    function log(string memory p0, bool p1, uint p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint)", p0, p1, p2));
    }

    function log(string memory p0, bool p1, string memory p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,string)", p0, p1, p2));
    }

    function log(string memory p0, bool p1, bool p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool)", p0, p1, p2));
    }

    function log(string memory p0, bool p1, address p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,address)", p0, p1, p2));
    }

    function log(string memory p0, address p1, uint p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,uint)", p0, p1, p2));
    }

    function log(string memory p0, address p1, string memory p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,string)", p0, p1, p2));
    }

    function log(string memory p0, address p1, bool p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,bool)", p0, p1, p2));
    }

    function log(string memory p0, address p1, address p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,address)", p0, p1, p2));
    }

    function log(bool p0, uint p1, uint p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint)", p0, p1, p2));
    }

    function log(bool p0, uint p1, string memory p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint,string)", p0, p1, p2));
    }

    function log(bool p0, uint p1, bool p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool)", p0, p1, p2));
    }

    function log(bool p0, uint p1, address p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint,address)", p0, p1, p2));
    }

    function log(bool p0, string memory p1, uint p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint)", p0, p1, p2));
    }

    function log(bool p0, string memory p1, string memory p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,string)", p0, p1, p2));
    }

    function log(bool p0, string memory p1, bool p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool)", p0, p1, p2));
    }

    function log(bool p0, string memory p1, address p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,address)", p0, p1, p2));
    }

    function log(bool p0, bool p1, uint p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint)", p0, p1, p2));
    }

    function log(bool p0, bool p1, string memory p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string)", p0, p1, p2));
    }

    function log(bool p0, bool p1, bool p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool)", p0, p1, p2));
    }

    function log(bool p0, bool p1, address p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address)", p0, p1, p2));
    }

    function log(bool p0, address p1, uint p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint)", p0, p1, p2));
    }

    function log(bool p0, address p1, string memory p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,string)", p0, p1, p2));
    }

    function log(bool p0, address p1, bool p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool)", p0, p1, p2));
    }

    function log(bool p0, address p1, address p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,address)", p0, p1, p2));
    }

    function log(address p0, uint p1, uint p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint,uint)", p0, p1, p2));
    }

    function log(address p0, uint p1, string memory p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint,string)", p0, p1, p2));
    }

    function log(address p0, uint p1, bool p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint,bool)", p0, p1, p2));
    }

    function log(address p0, uint p1, address p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint,address)", p0, p1, p2));
    }

    function log(address p0, string memory p1, uint p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,uint)", p0, p1, p2));
    }

    function log(address p0, string memory p1, string memory p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,string)", p0, p1, p2));
    }

    function log(address p0, string memory p1, bool p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,bool)", p0, p1, p2));
    }

    function log(address p0, string memory p1, address p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,address)", p0, p1, p2));
    }

    function log(address p0, bool p1, uint p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint)", p0, p1, p2));
    }

    function log(address p0, bool p1, string memory p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,string)", p0, p1, p2));
    }

    function log(address p0, bool p1, bool p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool)", p0, p1, p2));
    }

    function log(address p0, bool p1, address p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,address)", p0, p1, p2));
    }

    function log(address p0, address p1, uint p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,uint)", p0, p1, p2));
    }

    function log(address p0, address p1, string memory p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,string)", p0, p1, p2));
    }

    function log(address p0, address p1, bool p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,bool)", p0, p1, p2));
    }

    function log(address p0, address p1, address p2) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,address)", p0, p1, p2));
    }

    function log(uint p0, uint p1, uint p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,uint)", p0, p1, p2, p3));
    }

    function log(uint p0, uint p1, uint p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,string)", p0, p1, p2, p3));
    }

    function log(uint p0, uint p1, uint p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,bool)", p0, p1, p2, p3));
    }

    function log(uint p0, uint p1, uint p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,address)", p0, p1, p2, p3));
    }

    function log(uint p0, uint p1, string memory p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,uint)", p0, p1, p2, p3));
    }

    function log(uint p0, uint p1, string memory p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,string)", p0, p1, p2, p3));
    }

    function log(uint p0, uint p1, string memory p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,bool)", p0, p1, p2, p3));
    }

    function log(uint p0, uint p1, string memory p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,address)", p0, p1, p2, p3));
    }

    function log(uint p0, uint p1, bool p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,uint)", p0, p1, p2, p3));
    }

    function log(uint p0, uint p1, bool p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,string)", p0, p1, p2, p3));
    }

    function log(uint p0, uint p1, bool p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,bool)", p0, p1, p2, p3));
    }

    function log(uint p0, uint p1, bool p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,address)", p0, p1, p2, p3));
    }

    function log(uint p0, uint p1, address p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,uint)", p0, p1, p2, p3));
    }

    function log(uint p0, uint p1, address p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,string)", p0, p1, p2, p3));
    }

    function log(uint p0, uint p1, address p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,bool)", p0, p1, p2, p3));
    }

    function log(uint p0, uint p1, address p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,address)", p0, p1, p2, p3));
    }

    function log(uint p0, string memory p1, uint p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,uint)", p0, p1, p2, p3));
    }

    function log(uint p0, string memory p1, uint p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,string)", p0, p1, p2, p3));
    }

    function log(uint p0, string memory p1, uint p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,bool)", p0, p1, p2, p3));
    }

    function log(uint p0, string memory p1, uint p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,address)", p0, p1, p2, p3));
    }

    function log(uint p0, string memory p1, string memory p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,string,string,uint)", p0, p1, p2, p3));
    }

    function log(uint p0, string memory p1, string memory p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,string,string,string)", p0, p1, p2, p3));
    }

    function log(uint p0, string memory p1, string memory p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,string,string,bool)", p0, p1, p2, p3));
    }

    function log(uint p0, string memory p1, string memory p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,string,string,address)", p0, p1, p2, p3));
    }

    function log(uint p0, string memory p1, bool p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,uint)", p0, p1, p2, p3));
    }

    function log(uint p0, string memory p1, bool p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,string)", p0, p1, p2, p3));
    }

    function log(uint p0, string memory p1, bool p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,bool)", p0, p1, p2, p3));
    }

    function log(uint p0, string memory p1, bool p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,address)", p0, p1, p2, p3));
    }

    function log(uint p0, string memory p1, address p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,string,address,uint)", p0, p1, p2, p3));
    }

    function log(uint p0, string memory p1, address p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,string,address,string)", p0, p1, p2, p3));
    }

    function log(uint p0, string memory p1, address p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,string,address,bool)", p0, p1, p2, p3));
    }

    function log(uint p0, string memory p1, address p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,string,address,address)", p0, p1, p2, p3));
    }

    function log(uint p0, bool p1, uint p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,uint)", p0, p1, p2, p3));
    }

    function log(uint p0, bool p1, uint p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,string)", p0, p1, p2, p3));
    }

    function log(uint p0, bool p1, uint p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,bool)", p0, p1, p2, p3));
    }

    function log(uint p0, bool p1, uint p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,address)", p0, p1, p2, p3));
    }

    function log(uint p0, bool p1, string memory p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,uint)", p0, p1, p2, p3));
    }

    function log(uint p0, bool p1, string memory p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,string)", p0, p1, p2, p3));
    }

    function log(uint p0, bool p1, string memory p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,bool)", p0, p1, p2, p3));
    }

    function log(uint p0, bool p1, string memory p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,address)", p0, p1, p2, p3));
    }

    function log(uint p0, bool p1, bool p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,uint)", p0, p1, p2, p3));
    }

    function log(uint p0, bool p1, bool p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,string)", p0, p1, p2, p3));
    }

    function log(uint p0, bool p1, bool p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,bool)", p0, p1, p2, p3));
    }

    function log(uint p0, bool p1, bool p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,address)", p0, p1, p2, p3));
    }

    function log(uint p0, bool p1, address p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,uint)", p0, p1, p2, p3));
    }

    function log(uint p0, bool p1, address p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,string)", p0, p1, p2, p3));
    }

    function log(uint p0, bool p1, address p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,bool)", p0, p1, p2, p3));
    }

    function log(uint p0, bool p1, address p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,address)", p0, p1, p2, p3));
    }

    function log(uint p0, address p1, uint p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,uint)", p0, p1, p2, p3));
    }

    function log(uint p0, address p1, uint p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,string)", p0, p1, p2, p3));
    }

    function log(uint p0, address p1, uint p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,bool)", p0, p1, p2, p3));
    }

    function log(uint p0, address p1, uint p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,address)", p0, p1, p2, p3));
    }

    function log(uint p0, address p1, string memory p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,address,string,uint)", p0, p1, p2, p3));
    }

    function log(uint p0, address p1, string memory p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,address,string,string)", p0, p1, p2, p3));
    }

    function log(uint p0, address p1, string memory p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,address,string,bool)", p0, p1, p2, p3));
    }

    function log(uint p0, address p1, string memory p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,address,string,address)", p0, p1, p2, p3));
    }

    function log(uint p0, address p1, bool p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,uint)", p0, p1, p2, p3));
    }

    function log(uint p0, address p1, bool p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,string)", p0, p1, p2, p3));
    }

    function log(uint p0, address p1, bool p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,bool)", p0, p1, p2, p3));
    }

    function log(uint p0, address p1, bool p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,address)", p0, p1, p2, p3));
    }

    function log(uint p0, address p1, address p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,address,address,uint)", p0, p1, p2, p3));
    }

    function log(uint p0, address p1, address p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,address,address,string)", p0, p1, p2, p3));
    }

    function log(uint p0, address p1, address p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,address,address,bool)", p0, p1, p2, p3));
    }

    function log(uint p0, address p1, address p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(uint,address,address,address)", p0, p1, p2, p3));
    }

    function log(string memory p0, uint p1, uint p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,uint)", p0, p1, p2, p3));
    }

    function log(string memory p0, uint p1, uint p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,string)", p0, p1, p2, p3));
    }

    function log(string memory p0, uint p1, uint p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,bool)", p0, p1, p2, p3));
    }

    function log(string memory p0, uint p1, uint p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,address)", p0, p1, p2, p3));
    }

    function log(string memory p0, uint p1, string memory p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint,string,uint)", p0, p1, p2, p3));
    }

    function log(string memory p0, uint p1, string memory p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint,string,string)", p0, p1, p2, p3));
    }

    function log(string memory p0, uint p1, string memory p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint,string,bool)", p0, p1, p2, p3));
    }

    function log(string memory p0, uint p1, string memory p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint,string,address)", p0, p1, p2, p3));
    }

    function log(string memory p0, uint p1, bool p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,uint)", p0, p1, p2, p3));
    }

    function log(string memory p0, uint p1, bool p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,string)", p0, p1, p2, p3));
    }

    function log(string memory p0, uint p1, bool p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,bool)", p0, p1, p2, p3));
    }

    function log(string memory p0, uint p1, bool p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,address)", p0, p1, p2, p3));
    }

    function log(string memory p0, uint p1, address p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint,address,uint)", p0, p1, p2, p3));
    }

    function log(string memory p0, uint p1, address p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint,address,string)", p0, p1, p2, p3));
    }

    function log(string memory p0, uint p1, address p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint,address,bool)", p0, p1, p2, p3));
    }

    function log(string memory p0, uint p1, address p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint,address,address)", p0, p1, p2, p3));
    }

    function log(string memory p0, string memory p1, uint p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,uint,uint)", p0, p1, p2, p3));
    }

    function log(string memory p0, string memory p1, uint p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,uint,string)", p0, p1, p2, p3));
    }

    function log(string memory p0, string memory p1, uint p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,uint,bool)", p0, p1, p2, p3));
    }

    function log(string memory p0, string memory p1, uint p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,uint,address)", p0, p1, p2, p3));
    }

    function log(string memory p0, string memory p1, string memory p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,string,uint)", p0, p1, p2, p3));
    }

    function log(string memory p0, string memory p1, string memory p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,string,string)", p0, p1, p2, p3));
    }

    function log(string memory p0, string memory p1, string memory p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,string,bool)", p0, p1, p2, p3));
    }

    function log(string memory p0, string memory p1, string memory p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,string,address)", p0, p1, p2, p3));
    }

    function log(string memory p0, string memory p1, bool p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,uint)", p0, p1, p2, p3));
    }

    function log(string memory p0, string memory p1, bool p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,string)", p0, p1, p2, p3));
    }

    function log(string memory p0, string memory p1, bool p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,bool)", p0, p1, p2, p3));
    }

    function log(string memory p0, string memory p1, bool p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,address)", p0, p1, p2, p3));
    }

    function log(string memory p0, string memory p1, address p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,address,uint)", p0, p1, p2, p3));
    }

    function log(string memory p0, string memory p1, address p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,address,string)", p0, p1, p2, p3));
    }

    function log(string memory p0, string memory p1, address p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,address,bool)", p0, p1, p2, p3));
    }

    function log(string memory p0, string memory p1, address p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,address,address)", p0, p1, p2, p3));
    }

    function log(string memory p0, bool p1, uint p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,uint)", p0, p1, p2, p3));
    }

    function log(string memory p0, bool p1, uint p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,string)", p0, p1, p2, p3));
    }

    function log(string memory p0, bool p1, uint p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,bool)", p0, p1, p2, p3));
    }

    function log(string memory p0, bool p1, uint p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,address)", p0, p1, p2, p3));
    }

    function log(string memory p0, bool p1, string memory p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,uint)", p0, p1, p2, p3));
    }

    function log(string memory p0, bool p1, string memory p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,string)", p0, p1, p2, p3));
    }

    function log(string memory p0, bool p1, string memory p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,bool)", p0, p1, p2, p3));
    }

    function log(string memory p0, bool p1, string memory p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,address)", p0, p1, p2, p3));
    }

    function log(string memory p0, bool p1, bool p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,uint)", p0, p1, p2, p3));
    }

    function log(string memory p0, bool p1, bool p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,string)", p0, p1, p2, p3));
    }

    function log(string memory p0, bool p1, bool p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,bool)", p0, p1, p2, p3));
    }

    function log(string memory p0, bool p1, bool p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,address)", p0, p1, p2, p3));
    }

    function log(string memory p0, bool p1, address p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,uint)", p0, p1, p2, p3));
    }

    function log(string memory p0, bool p1, address p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,string)", p0, p1, p2, p3));
    }

    function log(string memory p0, bool p1, address p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,bool)", p0, p1, p2, p3));
    }

    function log(string memory p0, bool p1, address p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,address)", p0, p1, p2, p3));
    }

    function log(string memory p0, address p1, uint p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,uint,uint)", p0, p1, p2, p3));
    }

    function log(string memory p0, address p1, uint p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,uint,string)", p0, p1, p2, p3));
    }

    function log(string memory p0, address p1, uint p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,uint,bool)", p0, p1, p2, p3));
    }

    function log(string memory p0, address p1, uint p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,uint,address)", p0, p1, p2, p3));
    }

    function log(string memory p0, address p1, string memory p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,string,uint)", p0, p1, p2, p3));
    }

    function log(string memory p0, address p1, string memory p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,string,string)", p0, p1, p2, p3));
    }

    function log(string memory p0, address p1, string memory p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,string,bool)", p0, p1, p2, p3));
    }

    function log(string memory p0, address p1, string memory p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,string,address)", p0, p1, p2, p3));
    }

    function log(string memory p0, address p1, bool p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,uint)", p0, p1, p2, p3));
    }

    function log(string memory p0, address p1, bool p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,string)", p0, p1, p2, p3));
    }

    function log(string memory p0, address p1, bool p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,bool)", p0, p1, p2, p3));
    }

    function log(string memory p0, address p1, bool p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,address)", p0, p1, p2, p3));
    }

    function log(string memory p0, address p1, address p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,address,uint)", p0, p1, p2, p3));
    }

    function log(string memory p0, address p1, address p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,address,string)", p0, p1, p2, p3));
    }

    function log(string memory p0, address p1, address p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,address,bool)", p0, p1, p2, p3));
    }

    function log(string memory p0, address p1, address p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,address,address)", p0, p1, p2, p3));
    }

    function log(bool p0, uint p1, uint p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,uint)", p0, p1, p2, p3));
    }

    function log(bool p0, uint p1, uint p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,string)", p0, p1, p2, p3));
    }

    function log(bool p0, uint p1, uint p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,bool)", p0, p1, p2, p3));
    }

    function log(bool p0, uint p1, uint p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,address)", p0, p1, p2, p3));
    }

    function log(bool p0, uint p1, string memory p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,uint)", p0, p1, p2, p3));
    }

    function log(bool p0, uint p1, string memory p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,string)", p0, p1, p2, p3));
    }

    function log(bool p0, uint p1, string memory p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,bool)", p0, p1, p2, p3));
    }

    function log(bool p0, uint p1, string memory p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,address)", p0, p1, p2, p3));
    }

    function log(bool p0, uint p1, bool p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,uint)", p0, p1, p2, p3));
    }

    function log(bool p0, uint p1, bool p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,string)", p0, p1, p2, p3));
    }

    function log(bool p0, uint p1, bool p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,bool)", p0, p1, p2, p3));
    }

    function log(bool p0, uint p1, bool p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,address)", p0, p1, p2, p3));
    }

    function log(bool p0, uint p1, address p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,uint)", p0, p1, p2, p3));
    }

    function log(bool p0, uint p1, address p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,string)", p0, p1, p2, p3));
    }

    function log(bool p0, uint p1, address p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,bool)", p0, p1, p2, p3));
    }

    function log(bool p0, uint p1, address p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,address)", p0, p1, p2, p3));
    }

    function log(bool p0, string memory p1, uint p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,uint)", p0, p1, p2, p3));
    }

    function log(bool p0, string memory p1, uint p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,string)", p0, p1, p2, p3));
    }

    function log(bool p0, string memory p1, uint p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,bool)", p0, p1, p2, p3));
    }

    function log(bool p0, string memory p1, uint p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,address)", p0, p1, p2, p3));
    }

    function log(bool p0, string memory p1, string memory p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,uint)", p0, p1, p2, p3));
    }

    function log(bool p0, string memory p1, string memory p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,string)", p0, p1, p2, p3));
    }

    function log(bool p0, string memory p1, string memory p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,bool)", p0, p1, p2, p3));
    }

    function log(bool p0, string memory p1, string memory p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,address)", p0, p1, p2, p3));
    }

    function log(bool p0, string memory p1, bool p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,uint)", p0, p1, p2, p3));
    }

    function log(bool p0, string memory p1, bool p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,string)", p0, p1, p2, p3));
    }

    function log(bool p0, string memory p1, bool p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,bool)", p0, p1, p2, p3));
    }

    function log(bool p0, string memory p1, bool p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,address)", p0, p1, p2, p3));
    }

    function log(bool p0, string memory p1, address p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,uint)", p0, p1, p2, p3));
    }

    function log(bool p0, string memory p1, address p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,string)", p0, p1, p2, p3));
    }

    function log(bool p0, string memory p1, address p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,bool)", p0, p1, p2, p3));
    }

    function log(bool p0, string memory p1, address p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,address)", p0, p1, p2, p3));
    }

    function log(bool p0, bool p1, uint p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,uint)", p0, p1, p2, p3));
    }

    function log(bool p0, bool p1, uint p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,string)", p0, p1, p2, p3));
    }

    function log(bool p0, bool p1, uint p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,bool)", p0, p1, p2, p3));
    }

    function log(bool p0, bool p1, uint p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,address)", p0, p1, p2, p3));
    }

    function log(bool p0, bool p1, string memory p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,uint)", p0, p1, p2, p3));
    }

    function log(bool p0, bool p1, string memory p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,string)", p0, p1, p2, p3));
    }

    function log(bool p0, bool p1, string memory p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,bool)", p0, p1, p2, p3));
    }

    function log(bool p0, bool p1, string memory p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,address)", p0, p1, p2, p3));
    }

    function log(bool p0, bool p1, bool p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,uint)", p0, p1, p2, p3));
    }

    function log(bool p0, bool p1, bool p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,string)", p0, p1, p2, p3));
    }

    function log(bool p0, bool p1, bool p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,bool)", p0, p1, p2, p3));
    }

    function log(bool p0, bool p1, bool p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,address)", p0, p1, p2, p3));
    }

    function log(bool p0, bool p1, address p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,uint)", p0, p1, p2, p3));
    }

    function log(bool p0, bool p1, address p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,string)", p0, p1, p2, p3));
    }

    function log(bool p0, bool p1, address p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,bool)", p0, p1, p2, p3));
    }

    function log(bool p0, bool p1, address p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,address)", p0, p1, p2, p3));
    }

    function log(bool p0, address p1, uint p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,uint)", p0, p1, p2, p3));
    }

    function log(bool p0, address p1, uint p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,string)", p0, p1, p2, p3));
    }

    function log(bool p0, address p1, uint p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,bool)", p0, p1, p2, p3));
    }

    function log(bool p0, address p1, uint p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,address)", p0, p1, p2, p3));
    }

    function log(bool p0, address p1, string memory p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,uint)", p0, p1, p2, p3));
    }

    function log(bool p0, address p1, string memory p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,string)", p0, p1, p2, p3));
    }

    function log(bool p0, address p1, string memory p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,bool)", p0, p1, p2, p3));
    }

    function log(bool p0, address p1, string memory p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,address)", p0, p1, p2, p3));
    }

    function log(bool p0, address p1, bool p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,uint)", p0, p1, p2, p3));
    }

    function log(bool p0, address p1, bool p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,string)", p0, p1, p2, p3));
    }

    function log(bool p0, address p1, bool p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,bool)", p0, p1, p2, p3));
    }

    function log(bool p0, address p1, bool p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,address)", p0, p1, p2, p3));
    }

    function log(bool p0, address p1, address p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,uint)", p0, p1, p2, p3));
    }

    function log(bool p0, address p1, address p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,string)", p0, p1, p2, p3));
    }

    function log(bool p0, address p1, address p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,bool)", p0, p1, p2, p3));
    }

    function log(bool p0, address p1, address p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,address)", p0, p1, p2, p3));
    }

    function log(address p0, uint p1, uint p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,uint)", p0, p1, p2, p3));
    }

    function log(address p0, uint p1, uint p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,string)", p0, p1, p2, p3));
    }

    function log(address p0, uint p1, uint p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,bool)", p0, p1, p2, p3));
    }

    function log(address p0, uint p1, uint p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,address)", p0, p1, p2, p3));
    }

    function log(address p0, uint p1, string memory p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint,string,uint)", p0, p1, p2, p3));
    }

    function log(address p0, uint p1, string memory p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint,string,string)", p0, p1, p2, p3));
    }

    function log(address p0, uint p1, string memory p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint,string,bool)", p0, p1, p2, p3));
    }

    function log(address p0, uint p1, string memory p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint,string,address)", p0, p1, p2, p3));
    }

    function log(address p0, uint p1, bool p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,uint)", p0, p1, p2, p3));
    }

    function log(address p0, uint p1, bool p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,string)", p0, p1, p2, p3));
    }

    function log(address p0, uint p1, bool p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,bool)", p0, p1, p2, p3));
    }

    function log(address p0, uint p1, bool p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,address)", p0, p1, p2, p3));
    }

    function log(address p0, uint p1, address p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint,address,uint)", p0, p1, p2, p3));
    }

    function log(address p0, uint p1, address p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint,address,string)", p0, p1, p2, p3));
    }

    function log(address p0, uint p1, address p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint,address,bool)", p0, p1, p2, p3));
    }

    function log(address p0, uint p1, address p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint,address,address)", p0, p1, p2, p3));
    }

    function log(address p0, string memory p1, uint p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,uint,uint)", p0, p1, p2, p3));
    }

    function log(address p0, string memory p1, uint p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,uint,string)", p0, p1, p2, p3));
    }

    function log(address p0, string memory p1, uint p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,uint,bool)", p0, p1, p2, p3));
    }

    function log(address p0, string memory p1, uint p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,uint,address)", p0, p1, p2, p3));
    }

    function log(address p0, string memory p1, string memory p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,string,uint)", p0, p1, p2, p3));
    }

    function log(address p0, string memory p1, string memory p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,string,string)", p0, p1, p2, p3));
    }

    function log(address p0, string memory p1, string memory p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,string,bool)", p0, p1, p2, p3));
    }

    function log(address p0, string memory p1, string memory p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,string,address)", p0, p1, p2, p3));
    }

    function log(address p0, string memory p1, bool p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,uint)", p0, p1, p2, p3));
    }

    function log(address p0, string memory p1, bool p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,string)", p0, p1, p2, p3));
    }

    function log(address p0, string memory p1, bool p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,bool)", p0, p1, p2, p3));
    }

    function log(address p0, string memory p1, bool p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,address)", p0, p1, p2, p3));
    }

    function log(address p0, string memory p1, address p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,address,uint)", p0, p1, p2, p3));
    }

    function log(address p0, string memory p1, address p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,address,string)", p0, p1, p2, p3));
    }

    function log(address p0, string memory p1, address p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,address,bool)", p0, p1, p2, p3));
    }

    function log(address p0, string memory p1, address p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,address,address)", p0, p1, p2, p3));
    }

    function log(address p0, bool p1, uint p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,uint)", p0, p1, p2, p3));
    }

    function log(address p0, bool p1, uint p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,string)", p0, p1, p2, p3));
    }

    function log(address p0, bool p1, uint p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,bool)", p0, p1, p2, p3));
    }

    function log(address p0, bool p1, uint p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,address)", p0, p1, p2, p3));
    }

    function log(address p0, bool p1, string memory p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,uint)", p0, p1, p2, p3));
    }

    function log(address p0, bool p1, string memory p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,string)", p0, p1, p2, p3));
    }

    function log(address p0, bool p1, string memory p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,bool)", p0, p1, p2, p3));
    }

    function log(address p0, bool p1, string memory p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,address)", p0, p1, p2, p3));
    }

    function log(address p0, bool p1, bool p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,uint)", p0, p1, p2, p3));
    }

    function log(address p0, bool p1, bool p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,string)", p0, p1, p2, p3));
    }

    function log(address p0, bool p1, bool p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,bool)", p0, p1, p2, p3));
    }

    function log(address p0, bool p1, bool p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,address)", p0, p1, p2, p3));
    }

    function log(address p0, bool p1, address p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,uint)", p0, p1, p2, p3));
    }

    function log(address p0, bool p1, address p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,string)", p0, p1, p2, p3));
    }

    function log(address p0, bool p1, address p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,bool)", p0, p1, p2, p3));
    }

    function log(address p0, bool p1, address p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,address)", p0, p1, p2, p3));
    }

    function log(address p0, address p1, uint p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,uint,uint)", p0, p1, p2, p3));
    }

    function log(address p0, address p1, uint p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,uint,string)", p0, p1, p2, p3));
    }

    function log(address p0, address p1, uint p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,uint,bool)", p0, p1, p2, p3));
    }

    function log(address p0, address p1, uint p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,uint,address)", p0, p1, p2, p3));
    }

    function log(address p0, address p1, string memory p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,string,uint)", p0, p1, p2, p3));
    }

    function log(address p0, address p1, string memory p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,string,string)", p0, p1, p2, p3));
    }

    function log(address p0, address p1, string memory p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,string,bool)", p0, p1, p2, p3));
    }

    function log(address p0, address p1, string memory p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,string,address)", p0, p1, p2, p3));
    }

    function log(address p0, address p1, bool p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,uint)", p0, p1, p2, p3));
    }

    function log(address p0, address p1, bool p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,string)", p0, p1, p2, p3));
    }

    function log(address p0, address p1, bool p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,bool)", p0, p1, p2, p3));
    }

    function log(address p0, address p1, bool p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,address)", p0, p1, p2, p3));
    }

    function log(address p0, address p1, address p2, uint p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,address,uint)", p0, p1, p2, p3));
    }

    function log(address p0, address p1, address p2, string memory p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,address,string)", p0, p1, p2, p3));
    }

    function log(address p0, address p1, address p2, bool p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,address,bool)", p0, p1, p2, p3));
    }

    function log(address p0, address p1, address p2, address p3) internal view {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,address,address)", p0, p1, p2, p3));
    }

}

File 15 of 22 : Base58.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;

bytes constant ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";

/**
 * @notice encode is used to encode the given bytes in base58 standard.
 * @param data_ raw data, passed in as bytes.
 * @return base58 encoded data_, returned as bytes.
 */
function encode(bytes memory data_) pure returns (bytes memory) {
    unchecked {
        uint256 size = data_.length;
        uint256 zeroCount;
        while (zeroCount < size && data_[zeroCount] == 0) {
            zeroCount++;
        }
        size = zeroCount + ((size - zeroCount) * 8351) / 6115 + 1;
        bytes memory slot = new bytes(size);
        uint32 carry;
        int256 m;
        int256 high = int256(size) - 1;
        for (uint256 i = 0; i < data_.length; i++) {
            m = int256(size - 1);
            for (carry = uint8(data_[i]); m > high || carry != 0; m--) {
                carry = carry + 256 * uint8(slot[uint256(m)]);
                slot[uint256(m)] = bytes1(uint8(carry % 58));
                carry /= 58;
            }
            high = m;
        }
        uint256 n;
        for (n = zeroCount; n < size && slot[n] == 0; n++) {}
        size = slot.length - (n - zeroCount);
        bytes memory out = new bytes(size);
        for (uint256 i = 0; i < size; i++) {
            uint256 j = i + n - zeroCount;
            out[i] = ALPHABET[uint8(slot[j])];
        }
        return out;
    }
}

/**
 * @notice decode is used to decode the given string in base58 standard.
 * @param data_ data encoded with base58, passed in as bytes.
 * @return raw data, returned as bytes.
 */
function decode(bytes memory data_) pure returns (bytes memory) {
    unchecked {
        uint256 zero = 49;
        uint256 b58sz = data_.length;
        uint256 zcount = 0;
        for (uint256 i = 0; i < b58sz && uint8(data_[i]) == zero; i++) {
            zcount++;
        }
        uint256 t;
        uint256 c;
        bool f;
        bytes memory binu = new bytes(2 * (((b58sz * 8351) / 6115) + 1));
        uint32[] memory outi = new uint32[]((b58sz + 3) / 4);
        for (uint256 i = 0; i < data_.length; i++) {
            bytes1 r = data_[i];
            (c, f) = indexOf(ALPHABET, r);
            require(f, "invalid base58 digit");
            for (int256 k = int256(outi.length) - 1; k >= 0; k--) {
                t = uint64(outi[uint256(k)]) * 58 + c;
                c = t >> 32;
                outi[uint256(k)] = uint32(t & 0xffffffff);
            }
        }
        uint64 mask = uint64(b58sz % 4) * 8;
        if (mask == 0) {
            mask = 32;
        }
        mask -= 8;
        uint256 outLen = 0;
        for (uint256 j = 0; j < outi.length; j++) {
            while (mask < 32) {
                binu[outLen] = bytes1(uint8(outi[j] >> mask));
                outLen++;
                if (mask < 8) {
                    break;
                }
                mask -= 8;
            }
            mask = 24;
        }
        for (uint256 msb = zcount; msb < binu.length; msb++) {
            if (binu[msb] > 0) {
                return slice(binu, msb - zcount, outLen);
            }
        }
        return slice(binu, 0, outLen);
    }
}

/**
 * @notice encodeToString is used to encode the given byte in base58 standard.
 * @param data_ raw data, passed in as bytes.
 * @return base58 encoded data_, returned as a string.
 */
function encodeToString(bytes memory data_) pure returns (string memory) {
    return string(encode(data_));
}

/**
 * @notice encodeFromString is used to encode the given string in base58 standard.
 * @param data_ raw data, passed in as a string.
 * @return base58 encoded data_, returned as bytes.
 */
function encodeFromString(string memory data_) pure returns (bytes memory) {
    return encode(bytes(data_));
}

/**
 * @notice decode is used to decode the given string in base58 standard.
 * @param data_ data encoded with base58, passed in as string.
 * @return raw data, returned as bytes.
 */
function decodeFromString(string memory data_) pure returns (bytes memory) {
    return decode(bytes(data_));
}

/**
 * @notice slice is used to slice the given byte, returns the bytes in the range of [start_, end_)
 * @param data_ raw data, passed in as bytes.
 * @param start_ start index.
 * @param end_ end index.
 * @return slice data
 */
function slice(bytes memory data_, uint256 start_, uint256 end_) pure returns (bytes memory) {
    unchecked {
        bytes memory ret = new bytes(end_ - start_);
        for (uint256 i = 0; i < end_ - start_; i++) {
            ret[i] = data_[i + start_];
        }
        return ret;
    }
}

/**
 * @notice indexOf is used to find where char_ appears in data_.
 * @param data_ raw data, passed in as bytes.
 * @param char_ target byte.
 * @return index, and whether the search was successful.
 */
function indexOf(bytes memory data_, bytes1 char_) pure returns (uint256, bool) {
    unchecked {
        for (uint256 i = 0; i < data_.length; i++) {
            if (data_[i] == char_) {
                return (i, true);
            }
        }
        return (0, false);
    }
}

File 16 of 22 : Bip340Ecrec.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.8.0;

import "./Secp256k1.sol";

import "./Bip340.sol";
import "./Bip340Util.sol";

contract Bip340Ecrec is Bip340Verifier {
    /// Uses the ecrecover hack to verify a schnorr signature more efficiently than it should.
    ///
    // Based on `https://hackmd.io/@nZ-twauPRISEa6G9zg3XRw/SyjJzSLt9`
    // ^ this line is un-doc-commented because solc is annoying
    function verify(uint256 px, uint256 rx, uint256 s, bytes32 m) public pure override returns (bool) {
        // Check pubkey, rx, and s are in-range.
        if (px >= Secp256k1.PP || rx >= Secp256k1.PP || s >= Secp256k1.NN) {
            return false;
        }

        (address exp, bool ok) = Bip340Util.convToFakeAddr(rx);
        if (!ok) {
            return false;
        }

        uint256 e = Bip340Util.computeChallenge(bytes32(rx), bytes32(px), m);
        bytes32 sp = bytes32(Secp256k1.NN - mulmod(s, px, Secp256k1.NN));
        bytes32 ep = bytes32(Secp256k1.NN - mulmod(e, px, Secp256k1.NN));

        // 27 apparently used to signal even parity (which it will always have).
        address rvh = ecrecover(sp, 27, bytes32(px), ep);
        return rvh == exp; // if recovery fails we fail anyways
    }
}

File 17 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 18 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 19 of 22 : Secp256k1.sol
// SPDX-License-Identifier: MIT
//
// Modified from https://raw.githubusercontent.com/witnet/elliptic-curve-solidity/master/examples/Secp256k1.sol
//
// See license terms from the above original source.

pragma solidity >=0.8.0;

import "./EllipticCurve.sol";

/**
 * @title Secp256k1 Elliptic Curve
 * @notice Example of particularization of Elliptic Curve for secp256k1 curve
 * @author Witnet Foundation
 */
library Secp256k1 {

  uint256 public constant GX = 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798;
  uint256 public constant GY = 0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8;
  uint256 public constant AA = 0;
  uint256 public constant BB = 7;
  uint256 public constant PP = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F;
  uint256 public constant NN = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141; // curve order

  /// @notice Public Key derivation from private key
  /// Warning: this is just an example. Do not expose your private key.
  /// @param privKey The private key
  /// @return (qx, qy) The Public Key
  function derivePubKey(uint256 privKey) external pure returns (uint256, uint256) {
    return EllipticCurve.ecMul(
      privKey,
      GX,
      GY,
      AA,
      PP
    );
  }
}

File 20 of 22 : Bip340.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.8.0;

interface Bip340Verifier {
    /// Verifies a BIP340 signature parsed as `(rx, s)` form against a message
    /// `m` and a pubkey's x coord `px`.
    ///
    /// px - public key x coordinate
    /// rx - signature r commitment
    /// s - signature s proof
    /// m - message hash
    function verify(uint256 px, uint256 rx, uint256 s, bytes32 m) external returns (bool);
}

File 21 of 22 : Bip340Util.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.8.0;

import "./EllipticCurve.sol";
import "./Secp256k1.sol";

library Bip340Util {
    /// BIP340 challenge function.
    ///
    /// Hopefully the first SHA256 call gets inlined.
    function computeChallenge(bytes32 rx, bytes32 px, bytes32 m) internal pure returns (uint256) {
        // Precomputed `sha256("BIP0340/challenge")`.
        //
        // Saves ~10k gas, mostly from byte shuffling to prepare the call.
        //bytes32 tag = sha256("BIP0340/challenge");
        bytes32 tag = 0x7bb52d7a9fef58323eb1bf7a407db382d2f3f2d81bb1224f49fe518f6d48d37c;

        // Let e = int(hashBIP0340/challenge(bytes(r) || bytes(P) || m)) mod n.
        return uint256(sha256(abi.encodePacked(tag, tag, rx, px, m))) % Secp256k1.NN;
    }

    /// Given an x coordinate, returns the y coordinate of an even point on
    /// the secp256k1 curve.  This can be used to precompute the pubkey Y as
    /// mentioned in a few places.
    ///
    /// The second return value specifies if the operation was successful.
    function liftX(uint256 _x) internal pure returns (uint256, bool) {
        uint256 _pp = Secp256k1.PP;
        uint256 _aa = Secp256k1.AA;
        uint256 _bb = Secp256k1.BB;

        if (_x >= _pp) {
            return (0, false);
        }
        
        // Taken from the EllipticCurve code.
        uint256 y2 = addmod(mulmod(_x, mulmod(_x, _x, _pp), _pp), addmod(mulmod(_x, _aa, _pp), _bb, _pp), _pp);
        y2 = EllipticCurve.expMod(y2, (_pp + 1) / 4, _pp);
        uint256 y = (y2 & 1) == 0 ? y2 : _pp - y2;

        //require(y % 2 == 0, "not even???");

        return (y, true);
    }

    /// Internal function for doing the affine conversion for only the x coordinate.
    function xToAffine(uint256 _x, uint256 _z, uint256 _pp) internal pure returns (uint256) {
        uint256 zInv = EllipticCurve.invMod(_z, _pp);
        uint256 zInv2 = mulmod(zInv, zInv, _pp);
        return mulmod(_x, zInv2, _pp);
    }

    /// Converts a BIP340 pubkey X coord to what it would look like as an
    /// Ethereum address.
    function convToFakeAddr(uint256 px) internal pure returns (address, bool) {
        (uint256 py, bool ok) = liftX(px);
        if (!ok) {
            return (address(0), false);
        }
        bytes32 h = keccak256(abi.encodePacked(bytes32(px), bytes32(py)));
        return (address(uint160(uint256(h))), true);
    }
}

File 22 of 22 : EllipticCurve.sol
// SPDX-License-Identifier: MIT
//
// Taken from https://github.com/witnet/elliptic-curve-solidity/blob/master/contracts/EllipticCurve.sol
//
// See license terms from the above original source.

pragma solidity >=0.8.0;

/**
 * @title Elliptic Curve Library
 * @dev Library providing arithmetic operations over elliptic curves.
 * This library does not check whether the inserted points belong to the curve
 * `isOnCurve` function should be used by the library user to check the aforementioned statement.
 * @author Witnet Foundation
 */
library EllipticCurve {

  // Pre-computed constant for 2 ** 255
  uint256 constant private U255_MAX_PLUS_1 = 57896044618658097711785492504343953926634992332820282019728792003956564819968;

  /// @dev Modular euclidean inverse of a number (mod p).
  /// @param _x The number
  /// @param _pp The modulus
  /// @return q such that x*q = 1 (mod _pp)
  function invMod(uint256 _x, uint256 _pp) internal pure returns (uint256) {
    require(_x != 0 && _x != _pp && _pp != 0, "Invalid number");
    uint256 q = 0;
    uint256 newT = 1;
    uint256 r = _pp;
    uint256 t;
    while (_x != 0) {
      t = r / _x;
      (q, newT) = (newT, addmod(q, (_pp - mulmod(t, newT, _pp)), _pp));
      (r, _x) = (_x, r - t * _x);
    }

    return q;
  }

  /// @dev Modular exponentiation, b^e % _pp.
  /// Source: https://github.com/androlo/standard-contracts/blob/master/contracts/src/crypto/ECCMath.sol
  /// @param _base base
  /// @param _exp exponent
  /// @param _pp modulus
  /// @return r such that r = b**e (mod _pp)
  function expMod(uint256 _base, uint256 _exp, uint256 _pp) internal pure returns (uint256) {
    require(_pp!=0, "Modulus is zero");

    if (_base == 0)
      return 0;
    if (_exp == 0)
      return 1;

    uint256 r = 1;
    uint256 bit = U255_MAX_PLUS_1;
    assembly {
      for { } gt(bit, 0) { }{
        r := mulmod(mulmod(r, r, _pp), exp(_base, iszero(iszero(and(_exp, bit)))), _pp)
        r := mulmod(mulmod(r, r, _pp), exp(_base, iszero(iszero(and(_exp, div(bit, 2))))), _pp)
        r := mulmod(mulmod(r, r, _pp), exp(_base, iszero(iszero(and(_exp, div(bit, 4))))), _pp)
        r := mulmod(mulmod(r, r, _pp), exp(_base, iszero(iszero(and(_exp, div(bit, 8))))), _pp)
        bit := div(bit, 16)
      }
    }

    return r;
  }

  /// @dev Converts a point (x, y, z) expressed in Jacobian coordinates to affine coordinates (x', y', 1).
  /// @param _x coordinate x
  /// @param _y coordinate y
  /// @param _z coordinate z
  /// @param _pp the modulus
  /// @return (x', y') affine coordinates
  function toAffine(
    uint256 _x,
    uint256 _y,
    uint256 _z,
    uint256 _pp)
  internal pure returns (uint256, uint256)
  {
    uint256 zInv = invMod(_z, _pp);
    uint256 zInv2 = mulmod(zInv, zInv, _pp);
    uint256 x2 = mulmod(_x, zInv2, _pp);
    uint256 y2 = mulmod(_y, mulmod(zInv, zInv2, _pp), _pp);

    return (x2, y2);
  }

  /// @dev Derives the y coordinate from a compressed-format point x [[SEC-1]](https://www.secg.org/SEC1-Ver-1.0.pdf).
  /// @param _prefix parity byte (0x02 even, 0x03 odd)
  /// @param _x coordinate x
  /// @param _aa constant of curve
  /// @param _bb constant of curve
  /// @param _pp the modulus
  /// @return y coordinate y
  function deriveY(
    uint8 _prefix,
    uint256 _x,
    uint256 _aa,
    uint256 _bb,
    uint256 _pp)
  internal pure returns (uint256)
  {
    require(_prefix == 0x02 || _prefix == 0x03, "Invalid compressed EC point prefix");

    // x^3 + ax + b
    uint256 y2 = addmod(mulmod(_x, mulmod(_x, _x, _pp), _pp), addmod(mulmod(_x, _aa, _pp), _bb, _pp), _pp);
    y2 = expMod(y2, (_pp + 1) / 4, _pp);
    // uint256 cmp = yBit ^ y_ & 1;
    uint256 y = (y2 + _prefix) % 2 == 0 ? y2 : _pp - y2;

    return y;
  }

  /// @dev Check whether point (x,y) is on curve defined by a, b, and _pp.
  /// @param _x coordinate x of P1
  /// @param _y coordinate y of P1
  /// @param _aa constant of curve
  /// @param _bb constant of curve
  /// @param _pp the modulus
  /// @return true if x,y in the curve, false else
  function isOnCurve(
    uint _x,
    uint _y,
    uint _aa,
    uint _bb,
    uint _pp)
  internal pure returns (bool)
  {
    if (0 == _x || _x >= _pp || 0 == _y || _y >= _pp) {
      return false;
    }
    // y^2
    uint lhs = mulmod(_y, _y, _pp);
    // x^3
    uint rhs = mulmod(mulmod(_x, _x, _pp), _x, _pp);
    if (_aa != 0) {
      // x^3 + a*x
      rhs = addmod(rhs, mulmod(_x, _aa, _pp), _pp);
    }
    if (_bb != 0) {
      // x^3 + a*x + b
      rhs = addmod(rhs, _bb, _pp);
    }

    return lhs == rhs;
  }

  /// @dev Calculate inverse (x, -y) of point (x, y).
  /// @param _x coordinate x of P1
  /// @param _y coordinate y of P1
  /// @param _pp the modulus
  /// @return (x, -y)
  function ecInv(
    uint256 _x,
    uint256 _y,
    uint256 _pp)
  internal pure returns (uint256, uint256)
  {
    return (_x, (_pp - _y) % _pp);
  }

  /// @dev Add two points (x1, y1) and (x2, y2) in affine coordinates.
  /// @param _x1 coordinate x of P1
  /// @param _y1 coordinate y of P1
  /// @param _x2 coordinate x of P2
  /// @param _y2 coordinate y of P2
  /// @param _aa constant of the curve
  /// @param _pp the modulus
  /// @return (qx, qy) = P1+P2 in affine coordinates
  function ecAdd(
    uint256 _x1,
    uint256 _y1,
    uint256 _x2,
    uint256 _y2,
    uint256 _aa,
    uint256 _pp)
    internal pure returns(uint256, uint256)
  {
    uint x = 0;
    uint y = 0;
    uint z = 0;

    // Double if x1==x2 else add
    if (_x1==_x2) {
      // y1 = -y2 mod p
      if (addmod(_y1, _y2, _pp) == 0) {
        return(0, 0);
      } else {
        // P1 = P2
        (x, y, z) = jacDouble(
          _x1,
          _y1,
          1,
          _aa,
          _pp);
      }
    } else {
      (x, y, z) = jacAdd(
        _x1,
        _y1,
        1,
        _x2,
        _y2,
        1,
        _pp);
    }
    // Get back to affine
    return toAffine(
      x,
      y,
      z,
      _pp);
  }

  /// @dev Substract two points (x1, y1) and (x2, y2) in affine coordinates.
  /// @param _x1 coordinate x of P1
  /// @param _y1 coordinate y of P1
  /// @param _x2 coordinate x of P2
  /// @param _y2 coordinate y of P2
  /// @param _aa constant of the curve
  /// @param _pp the modulus
  /// @return (qx, qy) = P1-P2 in affine coordinates
  function ecSub(
    uint256 _x1,
    uint256 _y1,
    uint256 _x2,
    uint256 _y2,
    uint256 _aa,
    uint256 _pp)
  internal pure returns(uint256, uint256)
  {
    // invert square
    (uint256 x, uint256 y) = ecInv(_x2, _y2, _pp);
    // P1-square
    return ecAdd(
      _x1,
      _y1,
      x,
      y,
      _aa,
      _pp);
  }

  /// @dev Multiply point (x1, y1, z1) times d in affine coordinates.
  /// @param _k scalar to multiply
  /// @param _x coordinate x of P1
  /// @param _y coordinate y of P1
  /// @param _aa constant of the curve
  /// @param _pp the modulus
  /// @return (qx, qy) = d*P in affine coordinates
  function ecMul(
    uint256 _k,
    uint256 _x,
    uint256 _y,
    uint256 _aa,
    uint256 _pp)
  internal pure returns(uint256, uint256)
  {
    // Jacobian multiplication
    (uint256 x1, uint256 y1, uint256 z1) = jacMul(
      _k,
      _x,
      _y,
      1,
      _aa,
      _pp);
    // Get back to affine
    return toAffine(
      x1,
      y1,
      z1,
      _pp);
  }

  /// @dev Adds two points (x1, y1, z1) and (x2 y2, z2).
  /// @param _x1 coordinate x of P1
  /// @param _y1 coordinate y of P1
  /// @param _z1 coordinate z of P1
  /// @param _x2 coordinate x of square
  /// @param _y2 coordinate y of square
  /// @param _z2 coordinate z of square
  /// @param _pp the modulus
  /// @return (qx, qy, qz) P1+square in Jacobian
  function jacAdd(
    uint256 _x1,
    uint256 _y1,
    uint256 _z1,
    uint256 _x2,
    uint256 _y2,
    uint256 _z2,
    uint256 _pp)
  internal pure returns (uint256, uint256, uint256)
  {
    if (_x1==0 && _y1==0)
      return (_x2, _y2, _z2);
    if (_x2==0 && _y2==0)
      return (_x1, _y1, _z1);

    // We follow the equations described in https://pdfs.semanticscholar.org/5c64/29952e08025a9649c2b0ba32518e9a7fb5c2.pdf Section 5
    uint[4] memory zs; // z1^2, z1^3, z2^2, z2^3
    zs[0] = mulmod(_z1, _z1, _pp);
    zs[1] = mulmod(_z1, zs[0], _pp);
    zs[2] = mulmod(_z2, _z2, _pp);
    zs[3] = mulmod(_z2, zs[2], _pp);

    // u1, s1, u2, s2
    zs = [
      mulmod(_x1, zs[2], _pp),
      mulmod(_y1, zs[3], _pp),
      mulmod(_x2, zs[0], _pp),
      mulmod(_y2, zs[1], _pp)
    ];

    // In case of zs[0] == zs[2] && zs[1] == zs[3], double function should be used
    require(zs[0] != zs[2] || zs[1] != zs[3], "Use jacDouble function instead");

    uint[4] memory hr;
    //h
    hr[0] = addmod(zs[2], _pp - zs[0], _pp);
    //r
    hr[1] = addmod(zs[3], _pp - zs[1], _pp);
    //h^2
    hr[2] = mulmod(hr[0], hr[0], _pp);
    // h^3
    hr[3] = mulmod(hr[2], hr[0], _pp);
    // qx = -h^3  -2u1h^2+r^2
    uint256 qx = addmod(mulmod(hr[1], hr[1], _pp), _pp - hr[3], _pp);
    qx = addmod(qx, _pp - mulmod(2, mulmod(zs[0], hr[2], _pp), _pp), _pp);
    // qy = -s1*z1*h^3+r(u1*h^2 -x^3)
    uint256 qy = mulmod(hr[1], addmod(mulmod(zs[0], hr[2], _pp), _pp - qx, _pp), _pp);
    qy = addmod(qy, _pp - mulmod(zs[1], hr[3], _pp), _pp);
    // qz = h*z1*z2
    uint256 qz = mulmod(hr[0], mulmod(_z1, _z2, _pp), _pp);
    return(qx, qy, qz);
  }

  /// @dev Doubles a points (x, y, z).
  /// @param _x coordinate x of P1
  /// @param _y coordinate y of P1
  /// @param _z coordinate z of P1
  /// @param _aa the a scalar in the curve equation
  /// @param _pp the modulus
  /// @return (qx, qy, qz) 2P in Jacobian
  function jacDouble(
    uint256 _x,
    uint256 _y,
    uint256 _z,
    uint256 _aa,
    uint256 _pp)
  internal pure returns (uint256, uint256, uint256)
  {
    if (_z == 0)
      return (_x, _y, _z);

    // We follow the equations described in https://pdfs.semanticscholar.org/5c64/29952e08025a9649c2b0ba32518e9a7fb5c2.pdf Section 5
    // Note: there is a bug in the paper regarding the m parameter, M=3*(x1^2)+a*(z1^4)
    // x, y, z at this point represent the squares of _x, _y, _z
    uint256 x = mulmod(_x, _x, _pp); //x1^2
    uint256 y = mulmod(_y, _y, _pp); //y1^2
    uint256 z = mulmod(_z, _z, _pp); //z1^2

    // s
    uint s = mulmod(4, mulmod(_x, y, _pp), _pp);
    // m
    uint m = addmod(mulmod(3, x, _pp), mulmod(_aa, mulmod(z, z, _pp), _pp), _pp);

    // x, y, z at this point will be reassigned and rather represent qx, qy, qz from the paper
    // This allows to reduce the gas cost and stack footprint of the algorithm
    // qx
    x = addmod(mulmod(m, m, _pp), _pp - addmod(s, s, _pp), _pp);
    // qy = -8*y1^4 + M(S-T)
    y = addmod(mulmod(m, addmod(s, _pp - x, _pp), _pp), _pp - mulmod(8, mulmod(y, y, _pp), _pp), _pp);
    // qz = 2*y1*z1
    z = mulmod(2, mulmod(_y, _z, _pp), _pp);

    return (x, y, z);
  }

  /// @dev Multiply point (x, y, z) times d.
  /// @param _d scalar to multiply
  /// @param _x coordinate x of P1
  /// @param _y coordinate y of P1
  /// @param _z coordinate z of P1
  /// @param _aa constant of curve
  /// @param _pp the modulus
  /// @return (qx, qy, qz) d*P1 in Jacobian
  function jacMul(
    uint256 _d,
    uint256 _x,
    uint256 _y,
    uint256 _z,
    uint256 _aa,
    uint256 _pp)
  internal pure returns (uint256, uint256, uint256)
  {
    // Early return in case that `_d == 0`
    if (_d == 0) {
      return (_x, _y, _z);
    }

    uint256 remaining = _d;
    uint256 qx = 0;
    uint256 qy = 0;
    uint256 qz = 1;

    // Double and add algorithm
    while (remaining != 0) {
      if ((remaining & 1) != 0) {
        (qx, qy, qz) = jacAdd(
          qx,
          qy,
          qz,
          _x,
          _y,
          _z,
          _pp);
      }
      remaining = remaining / 2;
      (_x, _y, _z) = jacDouble(
        _x,
        _y,
        _z,
        _aa,
        _pp);
    }
    return (qx, qy, qz);
  }
}

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

Contract ABI

[{"inputs":[{"internalType":"enum BitcoinUtils.Network","name":"_network","type":"uint8"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_to","type":"address"},{"indexed":false,"internalType":"uint256","name":"_value","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"_btcDepositId","type":"bytes32"}],"name":"MintBtcEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_from","type":"address"},{"indexed":false,"internalType":"string","name":"_BTCAddress","type":"string"},{"indexed":false,"internalType":"uint256","name":"_value","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_id","type":"uint256"}],"name":"RedeemBtcEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"inputs":[{"internalType":"bytes1","name":"char","type":"bytes1"}],"name":"BECH32_ALPHABET_MAP","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DUST_LIMIT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"BTCAddress","type":"bytes"}],"name":"alphabetCheck","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"btcDepositIds","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"one","type":"bytes"},{"internalType":"bytes","name":"two","type":"bytes"}],"name":"equalBytes","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"enum BitcoinUtils.Network","name":"network","type":"uint8"}],"name":"getBtcBech32Prefix","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"pure","type":"function"},{"inputs":[{"components":[{"internalType":"bytes32","name":"btcDepositId","type":"bytes32"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct MintInvoice","name":"invoice","type":"tuple"}],"name":"getMintInvoiceHash","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_validatorPublicKey","type":"bytes32"}],"name":"getUpdateValidatorPublicKeyMessage","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"minWithdrawAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"address","name":"_recipient","type":"address"},{"internalType":"bytes32","name":"_btcDepositId","type":"bytes32"}],"name":"mint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"bytes32","name":"btcDepositId","type":"bytes32"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct MintInvoice","name":"invoice","type":"tuple"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"mint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"minter","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"network","outputs":[{"internalType":"enum BitcoinUtils.Network","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"permit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"pre","type":"uint256"}],"name":"polymodStep","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes","name":"prefix","type":"bytes"}],"name":"prefixChk","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"string","name":"BTCAddress","type":"string"}],"name":"redeem","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"redeemCounter","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_minWithdrawAmount","type":"uint256"}],"name":"setMinWithdrawAmount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_minter","type":"address"}],"name":"setMinter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_validatorPublicKey","type":"bytes32"}],"name":"setValidatorPublicKey","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_validatorPublicKey","type":"bytes32"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"setValidatorPublicKeySigned","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"btcAddress","type":"string"}],"name":"validateBase58Checksum","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"btcAddress","type":"string"}],"name":"validateBech32Checksum","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"enum BitcoinUtils.Network","name":"network","type":"uint8"},{"internalType":"string","name":"BTCAddress","type":"string"}],"name":"validateBitcoinAddress","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"hash","type":"bytes32"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"validateMessage","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"validatorPublicKey","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"px","type":"uint256"},{"internalType":"uint256","name":"rx","type":"uint256"},{"internalType":"uint256","name":"s","type":"uint256"},{"internalType":"bytes32","name":"m","type":"bytes32"}],"name":"verify","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"}]

60e0604052620186a06009553480156200001857600080fd5b506040516200440b3803806200440b8339810160408190526200003b9162000207565b6040518060400160405280600e81526020016d29ba3937b7b6902134ba31b7b4b760911b81525060405180604001604052806005815260200164737442544360d81b81525060088260009081620000939190620002d6565b506001620000a28382620002d6565b5060ff81166080524660a052620000b862000119565b60c05250620000cb9150339050620001b5565b6008805460ff19811682556000600a5582919061ffff1916610100836002811115620000fb57620000fb620003a2565b021790555050600b80546001600160a01b0319163317905562000436565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60006040516200014d9190620003b8565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b600680546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b6000602082840312156200021a57600080fd5b8151600381106200022a57600080fd5b9392505050565b634e487b7160e01b600052604160045260246000fd5b600181811c908216806200025c57607f821691505b6020821081036200027d57634e487b7160e01b600052602260045260246000fd5b50919050565b601f821115620002d157600081815260208120601f850160051c81016020861015620002ac5750805b601f850160051c820191505b81811015620002cd57828155600101620002b8565b5050505b505050565b81516001600160401b03811115620002f257620002f262000231565b6200030a8162000303845462000247565b8462000283565b602080601f831160018114620003425760008415620003295750858301515b600019600386901b1c1916600185901b178555620002cd565b600085815260208120601f198616915b82811015620003735788860151825594840194600190910190840162000352565b5085821015620003925787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b634e487b7160e01b600052602160045260246000fd5b6000808354620003c88162000247565b60018281168015620003e35760018114620003f9576200042a565b60ff19841687528215158302870194506200042a565b8760005260208060002060005b85811015620004215781548a82015290840190820162000406565b50505082870194505b50929695505050505050565b60805160a05160c051613fa5620004666000396000610daf01526000610d7a0152600061038c0152613fa56000f3fe608060405234801561001057600080fd5b506004361061027f5760003560e01c8063780f8df31161015c578063a9059cbb116100ce578063d9ab380111610087578063d9ab380114610591578063dd62ed3e146105a4578063e07fbd00146105cf578063f2fde38b146105e2578063f9a8bc48146105f5578063fca3b5aa1461060857600080fd5b8063a9059cbb1461051f578063af7b817014610532578063b35fc44e14610545578063d354c65d14610558578063d3b6d7f51461056b578063d505accf1461057e57600080fd5b80638da5cb5b116101205780638da5cb5b146104ce57806395d89b41146104df5780639647ea37146104e757806399f4a506146104fa5780639fd9568714610503578063a7ce45651461051657600080fd5b8063780f8df31461046d5780637b8d3cb4146104805780637ecebe001461049357806380cf79c8146104b35780638456cb59146104c657600080fd5b80633644e515116101f55780634cac70ff116101b95780634cac70ff146103f55780635abdb0dc146104085780635c975abb1461041b5780636739afca1461042657806370a0823114610445578063715018a61461046557600080fd5b80633644e515146103c05780633cc85e79146103c85780633f4ba83a146103db57806342193473146103e3578063457e1a49146103ec57600080fd5b80630b2aeb6c116102475780630b2aeb6c146103245780630fde6e551461033757806318160ddd1461034a57806323b872dd1461036157806324b76fd514610374578063313ce5671461038757600080fd5b8063026034f0146102845780630356b1a5146102bc57806306fdde03146102d157806307546172146102e6578063095ea7b314610311575b600080fd5b6102a7610292366004613663565b600c6020526000908152604090205460ff1681565b60405190151581526020015b60405180910390f35b6102cf6102ca366004613663565b61061b565b005b6102d9610628565b6040516102b391906136cc565b600b546102f9906001600160a01b031681565b6040516001600160a01b0390911681526020016102b3565b6102a761031f3660046136f6565b6106b6565b6102a7610332366004613770565b610723565b6102a76103453660046137c2565b6109d5565b61035360025481565b6040519081526020016102b3565b6102a761036f3660046137f4565b610b3d565b6102cf610382366004613830565b610c1d565b6103ae7f000000000000000000000000000000000000000000000000000000000000000081565b60405160ff90911681526020016102b3565b610353610d76565b6102cf6103d6366004613830565b610dd1565b6102cf610e10565b61035361022281565b61035360095481565b6102a761040336600461390d565b610e22565b6102cf610416366004613663565b610eb4565b60085460ff166102a7565b60085461043890610100900460ff1681565b6040516102b39190613986565b6103536104533660046139ae565b60036020526000908152604090205481565b6102cf610f39565b6102cf61047b3660046139c9565b610f4b565b6103ae61048e3660046139ee565b610fc8565b6103536104a13660046139ae565b60056020526000908152604090205481565b6103536104c1366004613663565b6113ad565b6102cf61146d565b6006546001600160a01b03166102f9565b6102d961147d565b6102a76104f5366004613a18565b61148a565b61035360075481565b6102cf610511366004613a71565b6119d4565b610353600a5481565b6102a761052d3660046136f6565b611a34565b6102a7610540366004613aab565b611a9a565b610353610553366004613663565b611b00565b6102a7610566366004613adf565b611b67565b610353610579366004613aab565b6121c0565b6102cf61058c366004613b27565b6122bc565b6102d961059f366004613b9a565b612500565b6103536105b2366004613bb5565b600460209081526000928352604080842090915290825290205481565b6103536105dd366004613be8565b6125f0565b6102cf6105f03660046139ae565b6125fb565b6102a7610603366004613830565b612674565b6102cf6106163660046139ae565b612731565b61062361275b565b600755565b6000805461063590613c04565b80601f016020809104026020016040519081016040528092919081815260200182805461066190613c04565b80156106ae5780601f10610683576101008083540402835291602001916106ae565b820191906000526020600020905b81548152906001019060200180831161069157829003601f168201915b505050505081565b3360008181526004602090815260408083206001600160a01b038716808552925280832085905551919290917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925906107119086815260200190565b60405180910390a35060015b92915050565b6000606061076884848080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250859250610e22915050565b156107775760009150506109ce565b6107db610788600160008688613c38565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250506040805180820190915260018152603160f81b60208201529150610e229050565b8061084557506108456107f2600160008688613c38565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250506040805180820190915260018152603360f81b60208201529150610e229050565b156108c157601a8310806108595750602383115b806108a0575061089e84848080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250611a9a92505050565b155b156108af5760009150506109ce565b6108b9848461148a565b9150506109ce565b60006108cc86612500565b805190915061091e906108e29060008789613c38565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250859250610e22915050565b156109c757600286600281111561093757610937613970565b0361096057602b84108061094b5750603f84115b1561095b576000925050506109ce565b61097f565b602a84108061096f5750603e84115b1561097f576000925050506109ce565b6109be85858080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250611b6792505050565b925050506109ce565b6000925050505b9392505050565b60006401000003d019851015806109f257506401000003d0198410155b80610a0f575070014551231950b75fc4402da1732fc9bebe198310155b15610a1c57506000610b35565b600080610a28866127b5565b9150915080610a3c57600092505050610b35565b6000610a49878987612810565b9050600070014551231950b75fc4402da1732fc9bebe19898809610a7f9070014551231950b75fc4402da1732fc9bebe19613c8e565b9050600070014551231950b75fc4402da1732fc9bebe198a8409610ab59070014551231950b75fc4402da1732fc9bebe19613c8e565b60408051600080825260208201808452869052601b92820192909252606081018d9052608081018390529192509060019060a0016020604051602081039080840390855afa158015610b0b573d6000803e3d6000fd5b505050602060405103519050856001600160a01b0316816001600160a01b03161496505050505050505b949350505050565b6001600160a01b03831660009081526004602090815260408083203384529091528120546000198114610b9957610b748382613c8e565b6001600160a01b03861660009081526004602090815260408083203384529091529020555b6001600160a01b03851660009081526003602052604081208054859290610bc1908490613c8e565b90915550506001600160a01b0380851660008181526003602052604090819020805487019055519091871690600080516020613f1683398151915290610c0a9087815260200190565b60405180910390a3506001949350505050565b610c256128e2565b600954831015610ca25760405162461bcd60e51b815260206004820152603e60248201527f5468652073656e742076616c7565206d7573742062652067726561746572206f60448201527f7220657175616c20746f206d696e20776974686472617720616d6f756e74000060648201526084015b60405180910390fd5b600854610cb890610100900460ff168383610723565b610d0e5760405162461bcd60e51b815260206004820152602160248201527f5468652073656e74204254432061646472657373206973206e6f742076616c696044820152601960fa1b6064820152608401610c99565b610d183384612928565b6001600a6000828254610d2b9190613ca1565b9091555050600a5460405133917f83c16822c691a011b471d2653b84faff158a050c4e117390a6c008ecdefcc14e91610d6991869186918991613cb4565b60405180910390a2505050565b60007f00000000000000000000000000000000000000000000000000000000000000004614610dac57610da7612992565b905090565b507f000000000000000000000000000000000000000000000000000000000000000090565b610dda83611b00565b8282610de7838383612674565b610e035760405162461bcd60e51b8152600401610c9990613cf4565b5050506007929092555050565b610e1861275b565b610e20612a2c565b565b60008151835114610e355750600061071d565b60005b8351811015610eaa57828181518110610e5357610e53613d3f565b602001015160f81c60f81b6001600160f81b031916848281518110610e7a57610e7a613d3f565b01602001516001600160f81b03191614610e9857600091505061071d565b80610ea281613d55565b915050610e38565b5060019392505050565b610ebc61275b565b610222811015610f345760405162461bcd60e51b815260206004820152603c60248201527f4d696e20776974686472617720616d6f756e742073686f756c6420626520677260448201527f6561746572206f7220657175616c20746f2064757374206c696d6974000000006064820152608401610c99565b600955565b610f4161275b565b610e206000612a7e565b610f536128e2565b600b546001600160a01b03163314610fb85760405162461bcd60e51b815260206004820152602260248201527f6c6e4254433a206f6e6c79206d696e74657220616c6c6f77656420746f206d696044820152611b9d60f21b6064820152608401610c99565b610fc3838383612ad0565b505050565b6000600d60fc1b6001600160f81b0319831601610fe75750600f919050565b606760f91b6001600160f81b03198316016110045750600a919050565b60cd60f81b6001600160f81b031983160161102157506011919050565b603360fa1b6001600160f81b031983160161103e57506015919050565b60cb60f81b6001600160f81b031983160161105b57506014919050565b606560f91b6001600160f81b03198316016110785750601a919050565b601960fb1b6001600160f81b031983160161109557506007919050565b60c960f81b6001600160f81b03198316016110b25750601e919050565b60c760f81b6001600160f81b03198316016110cf57506005919050565b608f60f81b6001600160f81b03198316016110ec57506000919050565b600960fc1b6001600160f81b031983160161110957506001919050565b604360f91b6001600160f81b031983160161112657506002919050565b604760f91b6001600160f81b031983160161114357506003919050565b608760f81b6001600160f81b031983160161116057506004919050565b601160fb1b6001600160f81b031983160161117d57506006919050565b609960f81b6001600160f81b031983160161119a57506008919050565b604d60f91b6001600160f81b03198316016111b757506009919050565b602360fa1b6001600160f81b03198316016111d45750600b919050565b604560f91b6001600160f81b03198316016111f15750600c919050565b602760fa1b6001600160f81b031983160161120e5750600d919050565b608960f81b6001600160f81b031983160161122b5750600e919050565b608d60f81b6001600160f81b031983160161124857506010919050565b604b60f91b6001600160f81b031983160161126557506012919050565b604960f91b6001600160f81b031983160161128257506013919050565b609560f81b6001600160f81b031983160161129f57506016919050565b601360fb1b6001600160f81b03198316016112bc57506017919050565b609d60f81b6001600160f81b03198316016112d957506018919050565b609b60f81b6001600160f81b03198316016112f657506019919050565b609360f81b6001600160f81b03198316016113135750601b919050565b608b60f81b6001600160f81b03198316016113305750601c919050565b609f60f81b6001600160f81b031983160161134d5750601d919050565b602560fa1b6001600160f81b031983160161136a5750601f919050565b61139c6040518060400160405280601181526020017024b73b30b634b21031b430b930b1ba32b960791b815250612c7d565b6113a582612cc0565b5060ff919050565b6000601982901c6001601d84901c8116146113c95760006113cf565b632a1462b35b63ffffffff16600382901c6001166001146113eb5760006113f1565b633d4233dd5b63ffffffff16600283901c60011660011461140d576000611413565b631ea119fa5b63ffffffff16600184811c81161461142c576000611432565b6326508e6d5b63ffffffff1660018086161461144957600061144f565b633b6a57b25b63ffffffff166005886301ffffff16901b1818181818915050919050565b61147561275b565b610e20612d09565b6001805461063590613c04565b6000806114cc84848080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250612d4692505050565b90506115056040518060400160405280601681526020017576616c6964617465426173653538436865636b73756d60501b815250612c7d565b61152d604051806040016040528060078152602001661c185e5b1bd85960ca1b815250612c7d565b61153681612d51565b805160191461154957600091505061071d565b6040805160018082528183019092526000916020820181803683370190505090508160008151811061157d5761157d613d3f565b602001015160f81c60f81b8160008151811061159b5761159b613d3f565b60200101906001600160f81b031916908160001a9053506115da604051806040016040528060078152602001663b32b939b4b7b760c91b815250612c7d565b6115e381612d51565b60006004600184516115f59190613c8e565b6115ff9190613c8e565b6001600160401b0381111561161657611616613862565b6040519080825280601f01601f191660200182016040528015611640576020820181803683370190505b50905060005b6004600185516116569190613c8e565b6116609190613c8e565b8110156116c95783611673826001613ca1565b8151811061168357611683613d3f565b602001015160f81c60f81b8282815181106116a0576116a0613d3f565b60200101906001600160f81b031916908160001a905350806116c181613d55565b915050611646565b506116f2604051806040016040528060078152602001661c185e5b1bd85960ca1b815250612c7d565b6116fb81612d51565b8051601414611710576000935050505061071d565b60408051600480825281830190925260009160208201818036833701905050905060005b60048110156117ad5784816004875161174d9190613c8e565b6117579190613ca1565b8151811061176757611767613d3f565b602001015160f81c60f81b82828151811061178457611784613d3f565b60200101906001600160f81b031916908160001a905350806117a581613d55565b915050611734565b506117d760405180604001604052806008815260200167636865636b73756d60c01b815250612c7d565b6117e081612d51565b600060028085856040516020016117f8929190613d6e565b60408051601f198184030181529082905261181291613d9d565b602060405180830381855afa15801561182f573d6000803e3d6000fd5b5050506040513d601f19601f820116820180604052508101906118529190613db9565b60405160200161186491815260200190565b60408051601f198184030181529082905261187e91613d9d565b602060405180830381855afa15801561189b573d6000803e3d6000fd5b5050506040513d601f19601f820116820180604052508101906118be9190613db9565b90506118f46040518060400160405280601381526020017263616c63756c6174656420636865636b73756d60681b815250612c7d565b6118fd81612d94565b8060031a60f81b8260038151811061191757611917613d3f565b01602001516001600160f81b031916188160021a60f81b8360028151811061194157611941613d3f565b01602001516001600160f81b031916188260011a60f81b8460018151811061196b5761196b613d3f565b01602001516001600160f81b031916188360001a60f81b8560008151811061199557611995613d3f565b602001015160f81c60f81b181717176001600160f81b031916600060f81b146119c65760009550505050505061071d565b506001979650505050505050565b6119dc6128e2565b6119e5836125f0565b82826119f2838383612674565b611a0e5760405162461bcd60e51b8152600401610c9990613cf4565b611a2c60408701803590611a259060208a016139ae565b8835612ad0565b505050505050565b33600090815260036020526040812080548391908390611a55908490613c8e565b90915550506001600160a01b03831660008181526003602052604090819020805485019055513390600080516020613f16833981519152906107119086815260200190565b6000805b8251811015611af7576000838281518110611abb57611abb613d3f565b016020015160f81c90506000611ad082612dd9565b905080611ae257506000949350505050565b50508080611aef90613d55565b915050611a9e565b50600192915050565b600081604051602001611b4a91907f5354524f4f4d5f5550444154455f56414c494441544f525f5055424c49435f4b815261455960f01b6020820152602281019190915260420190565b604051602081830303815290604052805190602001209050919050565b6000611ba76040518060400160405280601981526020017f0a76616c69646174652062656368333220636865636b73756d00000000000000815250612c7d565b611bcf604051806040016040528060078152602001666164647265737360c81b815250612c7d565b611bd882612c7d565b8151829060081115611c1757611c0e604051806040016040528060098152602001681d1bdbc81cda1bdc9d60ba1b815250612c7d565b50600092915050565b605a81511115611c4a57611c0e60405180604001604052806008815260200167746f6f206c6f6e6760c01b815250612c7d565b50816000805b8251811015611c9e57828181518110611c6b57611c6b613d3f565b01602001516001600160f81b031916603160f81b03611c8c57809150611c9e565b80611c9681613d55565b915050611c50565b5080600003611cde57611cd46040518060400160405280600c81526020016b37379039b2b830b930ba37b960a11b815250612c7d565b5060009392505050565b80600103611d1557611cd46040518060400160405280600e81526020016d0dad2e6e6d2dcce40e0e4caccd2f60931b815250612c7d565b6000816001600160401b03811115611d2f57611d2f613862565b6040519080825280601f01601f191660200182016040528015611d59576020820181803683370190505b50905060006001838551611d6d9190613c8e565b611d779190613c8e565b6001600160401b03811115611d8e57611d8e613862565b6040519080825280601f01601f191660200182016040528015611db8576020820181803683370190505b50905060005b83811015611e1e57848181518110611dd857611dd8613d3f565b602001015160f81c60f81b838281518110611df557611df5613d3f565b60200101906001600160f81b031916908160001a90535080611e1681613d55565b915050611dbe565b5060005b8151811015611e975784611e368583613ca1565b611e41906001613ca1565b81518110611e5157611e51613d3f565b602001015160f81c60f81b828281518110611e6e57611e6e613d3f565b60200101906001600160f81b031916908160001a90535080611e8f81613d55565b915050611e22565b50611ebf604051806040016040528060068152602001650e0e4caccd2f60d31b815250612c7d565b611ec882612d51565b600681511015611f0d57611f016040518060400160405280600e81526020016d19185d18481d1bdbc81cda1bdc9d60921b815250612c7d565b50600095945050505050565b6000611f18836121c0565b905080600003611f5e57611f516040518060400160405280600e81526020016d0d2dcecc2d8d2c840e0e4caccd2f60931b815250612c7d565b5060009695505050505050565b600082516001600160401b03811115611f7957611f79613862565b6040519080825280601f01601f191660200182016040528015611fa3576020820181803683370190505b50905060005b8351811015612106576000848281518110611fc657611fc6613d3f565b01602001516001600160f81b03191690506000611fe282610fc8565b905060fe1960ff82160161209a57612022604051806040016040528060118152602001703ab735b737bbb71031b430b930b1ba32b960791b815250612c7d565b61202b83612e82565b61203482612cc0565b6120896040518060400160405280600481526020016331b430b960e11b8152508360405160200161207591906001600160f81b031991909116815260010190565b604051602081830303815290604052612ec7565b5060009a9950505050505050505050565b8060ff166120a7866113ad565b8751911895506120b8846006613ca1565b106120c45750506120f4565b8060f81b8484815181106120da576120da613d3f565b60200101906001600160f81b031916908160001a90535050505b806120fe81613d55565b915050611fa9565b5061212d60405180604001604052806005815260200164776f72647360d81b815250612c7d565b61213681612d51565b632bc830a3821415801561214b575081600114155b15612190576121826040518060400160405280601081526020016f696e76616c696420636865636b73756d60801b81525083612f10565b506000979650505050505050565b6119c66040518060400160405280600e81526020016d76616c696420636865636b73756d60901b81525083612f10565b60006001815b835181101561225d5760008482815181106121e3576121e3613d3f565b016020015160f81c905060218110806121fc5750607e81115b1561223a5760405162461bcd60e51b815260206004820152600e60248201526d092dcecc2d8d2c840e0e4caccd2f60931b6044820152606401610c99565b600581901c612248846113ad565b189250508061225690613d55565b90506121c6565b50612267816113ad565b905060005b83518110156122b557600084828151811061228957612289613d3f565b016020015160f81c9050601f81166122a0846113ad565b18925050806122ae90613d55565b905061226c565b5092915050565b4284101561230c5760405162461bcd60e51b815260206004820152601760248201527f5045524d49545f444541444c494e455f455850495245440000000000000000006044820152606401610c99565b60006001612318610d76565b6001600160a01b038a811660008181526005602090815260409182902080546001810190915582517f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98184015280840194909452938d166060840152608083018c905260a083019390935260c08083018b90528151808403909101815260e08301909152805192019190912061190160f01b6101008301526101028201929092526101228101919091526101420160408051601f198184030181528282528051602091820120600084529083018083525260ff871690820152606081018590526080810184905260a0016020604051602081039080840390855afa158015612424573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b0381161580159061245a5750876001600160a01b0316816001600160a01b0316145b6124975760405162461bcd60e51b815260206004820152600e60248201526d24a72b20a624a22fa9a4a3a722a960911b6044820152606401610c99565b6001600160a01b0390811660009081526004602090815260408083208a8516808552908352928190208990555188815291928a16917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a350505050505050565b6060600082600281111561251657612516613970565b0361253a57505060408051808201909152600381526262633160e81b602082015290565b600282600281111561254e5761254e613970565b03612574575050604080518082019091526005815264626372743160d81b602082015290565b600182600281111561258857612588613970565b036125ac57505060408051808201909152600381526274623160e81b602082015290565b60405162461bcd60e51b8152602060048201526014602482015273556e6b6e6f776e206e6574776f726b207479706560601b6044820152606401610c99565b919050565b600061071d82612f55565b61260361275b565b6001600160a01b0381166126685760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401610c99565b61267181612a7e565b50565b60075460009081036126e25760405162461bcd60e51b815260206004820152603160248201527f56616c696461746f724d65737361676552656365697665723a204e4f5f56414c604482015270494441544f525f5055424c49435f4b455960781b6064820152608401610c99565b60006126f16020828587613c38565b6126fa91613dd2565b9050600061270c604060208688613c38565b61271591613dd2565b600754909150612727908383896109d5565b9695505050505050565b61273961275b565b600b80546001600160a01b0319166001600160a01b0392909216919091179055565b6006546001600160a01b03163314610e205760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610c99565b6000806000806127c485612fc4565b91509150806127da575060009485945092505050565b50604080516020808201969096528082019290925280518083038201815260609092019052805193019290922092600192509050565b604080517f7bb52d7a9fef58323eb1bf7a407db382d2f3f2d81bb1224f49fe518f6d48d37c60208201819052918101829052606081018590526080810184905260a081018390526000919070014551231950b75fc4402da1732fc9bebe199060029060c00160408051601f198184030181529082905261288f91613d9d565b602060405180830381855afa1580156128ac573d6000803e3d6000fd5b5050506040513d601f19601f820116820180604052508101906128cf9190613db9565b6128d99190613df0565b95945050505050565b60085460ff1615610e205760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b6044820152606401610c99565b6001600160a01b03821660009081526003602052604081208054839290612950908490613c8e565b90915550506002805482900390556040518181526000906001600160a01b03841690600080516020613f16833981519152906020015b60405180910390a35050565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60006040516129c49190613e04565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b612a34613089565b6008805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a1565b600680546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b60008311612b135760405162461bcd60e51b815260206004820152601060248201526f4d494e545f414d4f554e545f5a45524f60801b6044820152606401610c99565b612b256305f5e1006301406f40613ea3565b8310612b695760405162461bcd60e51b81526020600482015260136024820152724d494e545f414d4f554e545f544f4f5f42494760681b6044820152606401610c99565b306001600160a01b03831603612bc15760405162461bcd60e51b815260206004820152601c60248201527f4d494e545f544f5f5448455f434f4e54524143545f41444452455353000000006044820152606401610c99565b6000818152600c602052604090205460ff1615612c195760405162461bcd60e51b81526020600482015260166024820152751352539517d053149150511657d41493d0d154d4d15160521b6044820152606401610c99565b6000818152600c60205260409020805460ff19166001179055612c3c82846130d2565b60408051848152602081018390526001600160a01b038416917fb73f3e96d1e157f064cb3a8d0abed06bcec05e5515bf7486364c027dab6aa4699101610d69565b61267181604051602401612c9191906136cc565b60408051601f198184030181529190526020810180516001600160e01b031663104c13eb60e21b179052613124565b6040516001600160f81b0319821660248201526126719060440160408051601f198184030181529190526020810180516001600160e01b0316630dc3142560e31b179052613124565b612d116128e2565b6008805460ff191660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258612a613390565b606061071d82613145565b61267181604051602401612d6591906136cc565b60408051601f198184030181529190526020810180516001600160e01b03166305f3bfab60e11b179052613124565b61267181604051602401612daa91815260200190565b60408051601f198184030181529190526020810180516001600160e01b03166327b7cf8560e01b179052613124565b60008160ff1660491480612df057508160ff16604f145b80612dfe57508160ff16606c145b15612e0b57506000919050565b60318260ff1610158015612e23575060398260ff1611155b15612e3057506001919050565b60418260ff1610158015612e485750605a8260ff1611155b15612e5557506001919050565b60618260ff1610158015612e6d5750607a8260ff1611155b15612e7a57506001919050565b506000919050565b61267181604051602401612e9891815260200190565b60408051601f198184030181529190526020810180516001600160e01b031663f5b1bba960e01b179052613124565b612f0c8282604051602401612edd929190613eba565b60408051601f198184030181529190526020810180516001600160e01b0316634b5c427760e01b179052613124565b5050565b612f0c8282604051602401612f26929190613edf565b60408051601f198184030181529190526020810180516001600160e01b03166309710a9d60e41b179052613124565b60007f7d21eae75cdfb1cf6d27ca9a73c6341b49bc05072228b51a7cd4df444c1c8710612f8860408401602085016139ae565b60408051602081019390935260609190911b6bffffffffffffffffffffffff191682820152830135605482015282356074820152609401611b4a565b6000806401000003d019816007828610612fe657506000958695509350505050565b60008380612ff657612ff6613c62565b848061300457613004613c62565b83868061301357613013613c62565b868b0908858061302557613025613c62565b868061303357613033613c62565b8a8b098a0908905061305c81600461304c876001613ca1565b6130569190613f01565b8661348f565b905060006001821615613078576130738286613c8e565b61307a565b815b98600198509650505050505050565b60085460ff16610e205760405162461bcd60e51b815260206004820152601460248201527314185d5cd8589b194e881b9bdd081c185d5cd95960621b6044820152606401610c99565b80600260008282546130e49190613ca1565b90915550506001600160a01b038216600081815260036020908152604080832080548601905551848152600080516020613f168339815191529101612986565b80516a636f6e736f6c652e6c6f67602083016000808483855afa5050505050565b80516060906031906000805b828110801561317857508386828151811061316e5761316e613d3f565b016020015160f81c145b156131895760019182019101613151565b5060008080806117e361209f8702046001016002026001600160401b038111156131b5576131b5613862565b6040519080825280601f01601f1916602001820160405280156131df576020820181803683370190505b5090506000600460038801046001600160401b0381111561320257613202613862565b60405190808252806020026020018201604052801561322b578160200160208202803683370190505b50905060005b8a5181101561334d5760008b828151811061324e5761324e613d3f565b602001015160f81c60f81b905061327d6040518060600160405280603a8152602001613f36603a913982613558565b9096509450846132c65760405162461bcd60e51b81526020600482015260146024820152731a5b9d985b1a590818985cd94d4e08191a59da5d60621b6044820152606401610c99565b8251600019015b6000811261334357868482815181106132e8576132e8613d3f565b602002602001015163ffffffff16603a026001600160401b0316019750602088901c96508763ffffffff1684828151811061332557613325613d3f565b63ffffffff90921660209283029190910190910152600019016132cd565b5050600101613231565b50600860038816026001600160401b03811660000361336a575060205b600719016000805b8351811015613414575b6020836001600160401b0316101561340857826001600160401b03168482815181106133aa576133aa613d3f565b602002602001015163ffffffff16901c60f81b8583815181106133cf576133cf613d3f565b60200101906001600160f81b031916908160001a90535060019091019060086001600160401b038416106134085760088303925061337c565b60189250600101613372565b50875b845181101561347257600060f81b85828151811061343757613437613d3f565b01602001516001600160f81b031916111561346a57613459858a8303846135bb565b9d9c50505050505050505050505050565b600101613417565b5061347f846000836135bb565b9c9b505050505050505050505050565b6000816000036134d35760405162461bcd60e51b815260206004820152600f60248201526e4d6f64756c7573206973207a65726f60881b6044820152606401610c99565b836000036134e3575060006109ce565b826000036134f3575060016109ce565b6001600160ff1b5b801561354f57838186161515870a85848509099150836002820486161515870a85848509099150836004820486161515870a85848509099150836008820486161515870a85848509099150601090046134fb565b50949350505050565b60008060005b84518110156135ab57836001600160f81b03191685828151811061358457613584613d3f565b01602001516001600160f81b031916036135a3579150600190506135b4565b60010161355e565b50600080915091505b9250929050565b606060008383036001600160401b038111156135d9576135d9613862565b6040519080825280601f01601f191660200182016040528015613603576020820181803683370190505b50905060005b84840381101561354f57858582018151811061362757613627613d3f565b602001015160f81c60f81b82828151811061364457613644613d3f565b60200101906001600160f81b031916908160001a905350600101613609565b60006020828403121561367557600080fd5b5035919050565b60005b8381101561369757818101518382015260200161367f565b50506000910152565b600081518084526136b881602086016020860161367c565b601f01601f19169290920160200192915050565b6020815260006109ce60208301846136a0565b80356001600160a01b03811681146125eb57600080fd5b6000806040838503121561370957600080fd5b613712836136df565b946020939093013593505050565b8035600381106125eb57600080fd5b60008083601f84011261374157600080fd5b5081356001600160401b0381111561375857600080fd5b6020830191508360208285010111156135b457600080fd5b60008060006040848603121561378557600080fd5b61378e84613720565b925060208401356001600160401b038111156137a957600080fd5b6137b58682870161372f565b9497909650939450505050565b600080600080608085870312156137d857600080fd5b5050823594602084013594506040840135936060013592509050565b60008060006060848603121561380957600080fd5b613812846136df565b9250613820602085016136df565b9150604084013590509250925092565b60008060006040848603121561384557600080fd5b8335925060208401356001600160401b038111156137a957600080fd5b634e487b7160e01b600052604160045260246000fd5b60006001600160401b038084111561389257613892613862565b604051601f8501601f19908116603f011681019082821181831017156138ba576138ba613862565b816040528093508581528686860111156138d357600080fd5b858560208301376000602087830101525050509392505050565b600082601f8301126138fe57600080fd5b6109ce83833560208501613878565b6000806040838503121561392057600080fd5b82356001600160401b038082111561393757600080fd5b613943868387016138ed565b9350602085013591508082111561395957600080fd5b50613966858286016138ed565b9150509250929050565b634e487b7160e01b600052602160045260246000fd5b60208101600383106139a857634e487b7160e01b600052602160045260246000fd5b91905290565b6000602082840312156139c057600080fd5b6109ce826136df565b6000806000606084860312156139de57600080fd5b83359250613820602085016136df565b600060208284031215613a0057600080fd5b81356001600160f81b0319811681146109ce57600080fd5b60008060208385031215613a2b57600080fd5b82356001600160401b03811115613a4157600080fd5b613a4d8582860161372f565b90969095509350505050565b600060608284031215613a6b57600080fd5b50919050565b600080600060808486031215613a8657600080fd5b613a908585613a59565b925060608401356001600160401b038111156137a957600080fd5b600060208284031215613abd57600080fd5b81356001600160401b03811115613ad357600080fd5b610b35848285016138ed565b600060208284031215613af157600080fd5b81356001600160401b03811115613b0757600080fd5b8201601f81018413613b1857600080fd5b610b3584823560208401613878565b600080600080600080600060e0888a031215613b4257600080fd5b613b4b886136df565b9650613b59602089016136df565b95506040880135945060608801359350608088013560ff81168114613b7d57600080fd5b9699959850939692959460a0840135945060c09093013592915050565b600060208284031215613bac57600080fd5b6109ce82613720565b60008060408385031215613bc857600080fd5b613bd1836136df565b9150613bdf602084016136df565b90509250929050565b600060608284031215613bfa57600080fd5b6109ce8383613a59565b600181811c90821680613c1857607f821691505b602082108103613a6b57634e487b7160e01b600052602260045260246000fd5b60008085851115613c4857600080fd5b83861115613c5557600080fd5b5050820193919092039150565b634e487b7160e01b600052601260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b8181038181111561071d5761071d613c78565b8082018082111561071d5761071d613c78565b606081528360608201528385608083013760006080858301015260006080601f19601f870116830101905083602083015282604083015295945050505050565b6020808252602b908201527f56616c696461746f724d65737361676552656365697665723a20494e56414c4960408201526a445f5349474e415455524560a81b606082015260800190565b634e487b7160e01b600052603260045260246000fd5b600060018201613d6757613d67613c78565b5060010190565b60008351613d8081846020880161367c565b835190830190613d9481836020880161367c565b01949350505050565b60008251613daf81846020870161367c565b9190910192915050565b600060208284031215613dcb57600080fd5b5051919050565b8035602083101561071d57600019602084900360031b1b1692915050565b600082613dff57613dff613c62565b500690565b600080835481600182811c915080831680613e2057607f831692505b60208084108203613e3f57634e487b7160e01b86526022600452602486fd5b818015613e535760018114613e6857613e95565b60ff1986168952841515850289019650613e95565b60008a81526020902060005b86811015613e8d5781548b820152908501908301613e74565b505084890196505b509498975050505050505050565b808202811582820484141761071d5761071d613c78565b604081526000613ecd60408301856136a0565b82810360208401526128d981856136a0565b604081526000613ef260408301856136a0565b90508260208301529392505050565b600082613f1057613f10613c62565b50049056feddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef31323334353637383941424344454647484a4b4c4d4e505152535455565758595a6162636465666768696a6b6d6e6f707172737475767778797aa264697066735822122020caec933d9a13d787330ff7014afd1cebca57a4e7f475fd8eef0dc5d9b4847a64736f6c634300081300330000000000000000000000000000000000000000000000000000000000000001

Deployed Bytecode

0x608060405234801561001057600080fd5b506004361061027f5760003560e01c8063780f8df31161015c578063a9059cbb116100ce578063d9ab380111610087578063d9ab380114610591578063dd62ed3e146105a4578063e07fbd00146105cf578063f2fde38b146105e2578063f9a8bc48146105f5578063fca3b5aa1461060857600080fd5b8063a9059cbb1461051f578063af7b817014610532578063b35fc44e14610545578063d354c65d14610558578063d3b6d7f51461056b578063d505accf1461057e57600080fd5b80638da5cb5b116101205780638da5cb5b146104ce57806395d89b41146104df5780639647ea37146104e757806399f4a506146104fa5780639fd9568714610503578063a7ce45651461051657600080fd5b8063780f8df31461046d5780637b8d3cb4146104805780637ecebe001461049357806380cf79c8146104b35780638456cb59146104c657600080fd5b80633644e515116101f55780634cac70ff116101b95780634cac70ff146103f55780635abdb0dc146104085780635c975abb1461041b5780636739afca1461042657806370a0823114610445578063715018a61461046557600080fd5b80633644e515146103c05780633cc85e79146103c85780633f4ba83a146103db57806342193473146103e3578063457e1a49146103ec57600080fd5b80630b2aeb6c116102475780630b2aeb6c146103245780630fde6e551461033757806318160ddd1461034a57806323b872dd1461036157806324b76fd514610374578063313ce5671461038757600080fd5b8063026034f0146102845780630356b1a5146102bc57806306fdde03146102d157806307546172146102e6578063095ea7b314610311575b600080fd5b6102a7610292366004613663565b600c6020526000908152604090205460ff1681565b60405190151581526020015b60405180910390f35b6102cf6102ca366004613663565b61061b565b005b6102d9610628565b6040516102b391906136cc565b600b546102f9906001600160a01b031681565b6040516001600160a01b0390911681526020016102b3565b6102a761031f3660046136f6565b6106b6565b6102a7610332366004613770565b610723565b6102a76103453660046137c2565b6109d5565b61035360025481565b6040519081526020016102b3565b6102a761036f3660046137f4565b610b3d565b6102cf610382366004613830565b610c1d565b6103ae7f000000000000000000000000000000000000000000000000000000000000000881565b60405160ff90911681526020016102b3565b610353610d76565b6102cf6103d6366004613830565b610dd1565b6102cf610e10565b61035361022281565b61035360095481565b6102a761040336600461390d565b610e22565b6102cf610416366004613663565b610eb4565b60085460ff166102a7565b60085461043890610100900460ff1681565b6040516102b39190613986565b6103536104533660046139ae565b60036020526000908152604090205481565b6102cf610f39565b6102cf61047b3660046139c9565b610f4b565b6103ae61048e3660046139ee565b610fc8565b6103536104a13660046139ae565b60056020526000908152604090205481565b6103536104c1366004613663565b6113ad565b6102cf61146d565b6006546001600160a01b03166102f9565b6102d961147d565b6102a76104f5366004613a18565b61148a565b61035360075481565b6102cf610511366004613a71565b6119d4565b610353600a5481565b6102a761052d3660046136f6565b611a34565b6102a7610540366004613aab565b611a9a565b610353610553366004613663565b611b00565b6102a7610566366004613adf565b611b67565b610353610579366004613aab565b6121c0565b6102cf61058c366004613b27565b6122bc565b6102d961059f366004613b9a565b612500565b6103536105b2366004613bb5565b600460209081526000928352604080842090915290825290205481565b6103536105dd366004613be8565b6125f0565b6102cf6105f03660046139ae565b6125fb565b6102a7610603366004613830565b612674565b6102cf6106163660046139ae565b612731565b61062361275b565b600755565b6000805461063590613c04565b80601f016020809104026020016040519081016040528092919081815260200182805461066190613c04565b80156106ae5780601f10610683576101008083540402835291602001916106ae565b820191906000526020600020905b81548152906001019060200180831161069157829003601f168201915b505050505081565b3360008181526004602090815260408083206001600160a01b038716808552925280832085905551919290917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925906107119086815260200190565b60405180910390a35060015b92915050565b6000606061076884848080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250859250610e22915050565b156107775760009150506109ce565b6107db610788600160008688613c38565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250506040805180820190915260018152603160f81b60208201529150610e229050565b8061084557506108456107f2600160008688613c38565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250506040805180820190915260018152603360f81b60208201529150610e229050565b156108c157601a8310806108595750602383115b806108a0575061089e84848080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250611a9a92505050565b155b156108af5760009150506109ce565b6108b9848461148a565b9150506109ce565b60006108cc86612500565b805190915061091e906108e29060008789613c38565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250859250610e22915050565b156109c757600286600281111561093757610937613970565b0361096057602b84108061094b5750603f84115b1561095b576000925050506109ce565b61097f565b602a84108061096f5750603e84115b1561097f576000925050506109ce565b6109be85858080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250611b6792505050565b925050506109ce565b6000925050505b9392505050565b60006401000003d019851015806109f257506401000003d0198410155b80610a0f575070014551231950b75fc4402da1732fc9bebe198310155b15610a1c57506000610b35565b600080610a28866127b5565b9150915080610a3c57600092505050610b35565b6000610a49878987612810565b9050600070014551231950b75fc4402da1732fc9bebe19898809610a7f9070014551231950b75fc4402da1732fc9bebe19613c8e565b9050600070014551231950b75fc4402da1732fc9bebe198a8409610ab59070014551231950b75fc4402da1732fc9bebe19613c8e565b60408051600080825260208201808452869052601b92820192909252606081018d9052608081018390529192509060019060a0016020604051602081039080840390855afa158015610b0b573d6000803e3d6000fd5b505050602060405103519050856001600160a01b0316816001600160a01b03161496505050505050505b949350505050565b6001600160a01b03831660009081526004602090815260408083203384529091528120546000198114610b9957610b748382613c8e565b6001600160a01b03861660009081526004602090815260408083203384529091529020555b6001600160a01b03851660009081526003602052604081208054859290610bc1908490613c8e565b90915550506001600160a01b0380851660008181526003602052604090819020805487019055519091871690600080516020613f1683398151915290610c0a9087815260200190565b60405180910390a3506001949350505050565b610c256128e2565b600954831015610ca25760405162461bcd60e51b815260206004820152603e60248201527f5468652073656e742076616c7565206d7573742062652067726561746572206f60448201527f7220657175616c20746f206d696e20776974686472617720616d6f756e74000060648201526084015b60405180910390fd5b600854610cb890610100900460ff168383610723565b610d0e5760405162461bcd60e51b815260206004820152602160248201527f5468652073656e74204254432061646472657373206973206e6f742076616c696044820152601960fa1b6064820152608401610c99565b610d183384612928565b6001600a6000828254610d2b9190613ca1565b9091555050600a5460405133917f83c16822c691a011b471d2653b84faff158a050c4e117390a6c008ecdefcc14e91610d6991869186918991613cb4565b60405180910390a2505050565b60007f0000000000000000000000000000000000000000000000000000000000aa36a74614610dac57610da7612992565b905090565b507f2dbb407e29884e426f895ff7692f430b775109a98839b8d7483c39515b4428f090565b610dda83611b00565b8282610de7838383612674565b610e035760405162461bcd60e51b8152600401610c9990613cf4565b5050506007929092555050565b610e1861275b565b610e20612a2c565b565b60008151835114610e355750600061071d565b60005b8351811015610eaa57828181518110610e5357610e53613d3f565b602001015160f81c60f81b6001600160f81b031916848281518110610e7a57610e7a613d3f565b01602001516001600160f81b03191614610e9857600091505061071d565b80610ea281613d55565b915050610e38565b5060019392505050565b610ebc61275b565b610222811015610f345760405162461bcd60e51b815260206004820152603c60248201527f4d696e20776974686472617720616d6f756e742073686f756c6420626520677260448201527f6561746572206f7220657175616c20746f2064757374206c696d6974000000006064820152608401610c99565b600955565b610f4161275b565b610e206000612a7e565b610f536128e2565b600b546001600160a01b03163314610fb85760405162461bcd60e51b815260206004820152602260248201527f6c6e4254433a206f6e6c79206d696e74657220616c6c6f77656420746f206d696044820152611b9d60f21b6064820152608401610c99565b610fc3838383612ad0565b505050565b6000600d60fc1b6001600160f81b0319831601610fe75750600f919050565b606760f91b6001600160f81b03198316016110045750600a919050565b60cd60f81b6001600160f81b031983160161102157506011919050565b603360fa1b6001600160f81b031983160161103e57506015919050565b60cb60f81b6001600160f81b031983160161105b57506014919050565b606560f91b6001600160f81b03198316016110785750601a919050565b601960fb1b6001600160f81b031983160161109557506007919050565b60c960f81b6001600160f81b03198316016110b25750601e919050565b60c760f81b6001600160f81b03198316016110cf57506005919050565b608f60f81b6001600160f81b03198316016110ec57506000919050565b600960fc1b6001600160f81b031983160161110957506001919050565b604360f91b6001600160f81b031983160161112657506002919050565b604760f91b6001600160f81b031983160161114357506003919050565b608760f81b6001600160f81b031983160161116057506004919050565b601160fb1b6001600160f81b031983160161117d57506006919050565b609960f81b6001600160f81b031983160161119a57506008919050565b604d60f91b6001600160f81b03198316016111b757506009919050565b602360fa1b6001600160f81b03198316016111d45750600b919050565b604560f91b6001600160f81b03198316016111f15750600c919050565b602760fa1b6001600160f81b031983160161120e5750600d919050565b608960f81b6001600160f81b031983160161122b5750600e919050565b608d60f81b6001600160f81b031983160161124857506010919050565b604b60f91b6001600160f81b031983160161126557506012919050565b604960f91b6001600160f81b031983160161128257506013919050565b609560f81b6001600160f81b031983160161129f57506016919050565b601360fb1b6001600160f81b03198316016112bc57506017919050565b609d60f81b6001600160f81b03198316016112d957506018919050565b609b60f81b6001600160f81b03198316016112f657506019919050565b609360f81b6001600160f81b03198316016113135750601b919050565b608b60f81b6001600160f81b03198316016113305750601c919050565b609f60f81b6001600160f81b031983160161134d5750601d919050565b602560fa1b6001600160f81b031983160161136a5750601f919050565b61139c6040518060400160405280601181526020017024b73b30b634b21031b430b930b1ba32b960791b815250612c7d565b6113a582612cc0565b5060ff919050565b6000601982901c6001601d84901c8116146113c95760006113cf565b632a1462b35b63ffffffff16600382901c6001166001146113eb5760006113f1565b633d4233dd5b63ffffffff16600283901c60011660011461140d576000611413565b631ea119fa5b63ffffffff16600184811c81161461142c576000611432565b6326508e6d5b63ffffffff1660018086161461144957600061144f565b633b6a57b25b63ffffffff166005886301ffffff16901b1818181818915050919050565b61147561275b565b610e20612d09565b6001805461063590613c04565b6000806114cc84848080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250612d4692505050565b90506115056040518060400160405280601681526020017576616c6964617465426173653538436865636b73756d60501b815250612c7d565b61152d604051806040016040528060078152602001661c185e5b1bd85960ca1b815250612c7d565b61153681612d51565b805160191461154957600091505061071d565b6040805160018082528183019092526000916020820181803683370190505090508160008151811061157d5761157d613d3f565b602001015160f81c60f81b8160008151811061159b5761159b613d3f565b60200101906001600160f81b031916908160001a9053506115da604051806040016040528060078152602001663b32b939b4b7b760c91b815250612c7d565b6115e381612d51565b60006004600184516115f59190613c8e565b6115ff9190613c8e565b6001600160401b0381111561161657611616613862565b6040519080825280601f01601f191660200182016040528015611640576020820181803683370190505b50905060005b6004600185516116569190613c8e565b6116609190613c8e565b8110156116c95783611673826001613ca1565b8151811061168357611683613d3f565b602001015160f81c60f81b8282815181106116a0576116a0613d3f565b60200101906001600160f81b031916908160001a905350806116c181613d55565b915050611646565b506116f2604051806040016040528060078152602001661c185e5b1bd85960ca1b815250612c7d565b6116fb81612d51565b8051601414611710576000935050505061071d565b60408051600480825281830190925260009160208201818036833701905050905060005b60048110156117ad5784816004875161174d9190613c8e565b6117579190613ca1565b8151811061176757611767613d3f565b602001015160f81c60f81b82828151811061178457611784613d3f565b60200101906001600160f81b031916908160001a905350806117a581613d55565b915050611734565b506117d760405180604001604052806008815260200167636865636b73756d60c01b815250612c7d565b6117e081612d51565b600060028085856040516020016117f8929190613d6e565b60408051601f198184030181529082905261181291613d9d565b602060405180830381855afa15801561182f573d6000803e3d6000fd5b5050506040513d601f19601f820116820180604052508101906118529190613db9565b60405160200161186491815260200190565b60408051601f198184030181529082905261187e91613d9d565b602060405180830381855afa15801561189b573d6000803e3d6000fd5b5050506040513d601f19601f820116820180604052508101906118be9190613db9565b90506118f46040518060400160405280601381526020017263616c63756c6174656420636865636b73756d60681b815250612c7d565b6118fd81612d94565b8060031a60f81b8260038151811061191757611917613d3f565b01602001516001600160f81b031916188160021a60f81b8360028151811061194157611941613d3f565b01602001516001600160f81b031916188260011a60f81b8460018151811061196b5761196b613d3f565b01602001516001600160f81b031916188360001a60f81b8560008151811061199557611995613d3f565b602001015160f81c60f81b181717176001600160f81b031916600060f81b146119c65760009550505050505061071d565b506001979650505050505050565b6119dc6128e2565b6119e5836125f0565b82826119f2838383612674565b611a0e5760405162461bcd60e51b8152600401610c9990613cf4565b611a2c60408701803590611a259060208a016139ae565b8835612ad0565b505050505050565b33600090815260036020526040812080548391908390611a55908490613c8e565b90915550506001600160a01b03831660008181526003602052604090819020805485019055513390600080516020613f16833981519152906107119086815260200190565b6000805b8251811015611af7576000838281518110611abb57611abb613d3f565b016020015160f81c90506000611ad082612dd9565b905080611ae257506000949350505050565b50508080611aef90613d55565b915050611a9e565b50600192915050565b600081604051602001611b4a91907f5354524f4f4d5f5550444154455f56414c494441544f525f5055424c49435f4b815261455960f01b6020820152602281019190915260420190565b604051602081830303815290604052805190602001209050919050565b6000611ba76040518060400160405280601981526020017f0a76616c69646174652062656368333220636865636b73756d00000000000000815250612c7d565b611bcf604051806040016040528060078152602001666164647265737360c81b815250612c7d565b611bd882612c7d565b8151829060081115611c1757611c0e604051806040016040528060098152602001681d1bdbc81cda1bdc9d60ba1b815250612c7d565b50600092915050565b605a81511115611c4a57611c0e60405180604001604052806008815260200167746f6f206c6f6e6760c01b815250612c7d565b50816000805b8251811015611c9e57828181518110611c6b57611c6b613d3f565b01602001516001600160f81b031916603160f81b03611c8c57809150611c9e565b80611c9681613d55565b915050611c50565b5080600003611cde57611cd46040518060400160405280600c81526020016b37379039b2b830b930ba37b960a11b815250612c7d565b5060009392505050565b80600103611d1557611cd46040518060400160405280600e81526020016d0dad2e6e6d2dcce40e0e4caccd2f60931b815250612c7d565b6000816001600160401b03811115611d2f57611d2f613862565b6040519080825280601f01601f191660200182016040528015611d59576020820181803683370190505b50905060006001838551611d6d9190613c8e565b611d779190613c8e565b6001600160401b03811115611d8e57611d8e613862565b6040519080825280601f01601f191660200182016040528015611db8576020820181803683370190505b50905060005b83811015611e1e57848181518110611dd857611dd8613d3f565b602001015160f81c60f81b838281518110611df557611df5613d3f565b60200101906001600160f81b031916908160001a90535080611e1681613d55565b915050611dbe565b5060005b8151811015611e975784611e368583613ca1565b611e41906001613ca1565b81518110611e5157611e51613d3f565b602001015160f81c60f81b828281518110611e6e57611e6e613d3f565b60200101906001600160f81b031916908160001a90535080611e8f81613d55565b915050611e22565b50611ebf604051806040016040528060068152602001650e0e4caccd2f60d31b815250612c7d565b611ec882612d51565b600681511015611f0d57611f016040518060400160405280600e81526020016d19185d18481d1bdbc81cda1bdc9d60921b815250612c7d565b50600095945050505050565b6000611f18836121c0565b905080600003611f5e57611f516040518060400160405280600e81526020016d0d2dcecc2d8d2c840e0e4caccd2f60931b815250612c7d565b5060009695505050505050565b600082516001600160401b03811115611f7957611f79613862565b6040519080825280601f01601f191660200182016040528015611fa3576020820181803683370190505b50905060005b8351811015612106576000848281518110611fc657611fc6613d3f565b01602001516001600160f81b03191690506000611fe282610fc8565b905060fe1960ff82160161209a57612022604051806040016040528060118152602001703ab735b737bbb71031b430b930b1ba32b960791b815250612c7d565b61202b83612e82565b61203482612cc0565b6120896040518060400160405280600481526020016331b430b960e11b8152508360405160200161207591906001600160f81b031991909116815260010190565b604051602081830303815290604052612ec7565b5060009a9950505050505050505050565b8060ff166120a7866113ad565b8751911895506120b8846006613ca1565b106120c45750506120f4565b8060f81b8484815181106120da576120da613d3f565b60200101906001600160f81b031916908160001a90535050505b806120fe81613d55565b915050611fa9565b5061212d60405180604001604052806005815260200164776f72647360d81b815250612c7d565b61213681612d51565b632bc830a3821415801561214b575081600114155b15612190576121826040518060400160405280601081526020016f696e76616c696420636865636b73756d60801b81525083612f10565b506000979650505050505050565b6119c66040518060400160405280600e81526020016d76616c696420636865636b73756d60901b81525083612f10565b60006001815b835181101561225d5760008482815181106121e3576121e3613d3f565b016020015160f81c905060218110806121fc5750607e81115b1561223a5760405162461bcd60e51b815260206004820152600e60248201526d092dcecc2d8d2c840e0e4caccd2f60931b6044820152606401610c99565b600581901c612248846113ad565b189250508061225690613d55565b90506121c6565b50612267816113ad565b905060005b83518110156122b557600084828151811061228957612289613d3f565b016020015160f81c9050601f81166122a0846113ad565b18925050806122ae90613d55565b905061226c565b5092915050565b4284101561230c5760405162461bcd60e51b815260206004820152601760248201527f5045524d49545f444541444c494e455f455850495245440000000000000000006044820152606401610c99565b60006001612318610d76565b6001600160a01b038a811660008181526005602090815260409182902080546001810190915582517f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98184015280840194909452938d166060840152608083018c905260a083019390935260c08083018b90528151808403909101815260e08301909152805192019190912061190160f01b6101008301526101028201929092526101228101919091526101420160408051601f198184030181528282528051602091820120600084529083018083525260ff871690820152606081018590526080810184905260a0016020604051602081039080840390855afa158015612424573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b0381161580159061245a5750876001600160a01b0316816001600160a01b0316145b6124975760405162461bcd60e51b815260206004820152600e60248201526d24a72b20a624a22fa9a4a3a722a960911b6044820152606401610c99565b6001600160a01b0390811660009081526004602090815260408083208a8516808552908352928190208990555188815291928a16917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a350505050505050565b6060600082600281111561251657612516613970565b0361253a57505060408051808201909152600381526262633160e81b602082015290565b600282600281111561254e5761254e613970565b03612574575050604080518082019091526005815264626372743160d81b602082015290565b600182600281111561258857612588613970565b036125ac57505060408051808201909152600381526274623160e81b602082015290565b60405162461bcd60e51b8152602060048201526014602482015273556e6b6e6f776e206e6574776f726b207479706560601b6044820152606401610c99565b919050565b600061071d82612f55565b61260361275b565b6001600160a01b0381166126685760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401610c99565b61267181612a7e565b50565b60075460009081036126e25760405162461bcd60e51b815260206004820152603160248201527f56616c696461746f724d65737361676552656365697665723a204e4f5f56414c604482015270494441544f525f5055424c49435f4b455960781b6064820152608401610c99565b60006126f16020828587613c38565b6126fa91613dd2565b9050600061270c604060208688613c38565b61271591613dd2565b600754909150612727908383896109d5565b9695505050505050565b61273961275b565b600b80546001600160a01b0319166001600160a01b0392909216919091179055565b6006546001600160a01b03163314610e205760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610c99565b6000806000806127c485612fc4565b91509150806127da575060009485945092505050565b50604080516020808201969096528082019290925280518083038201815260609092019052805193019290922092600192509050565b604080517f7bb52d7a9fef58323eb1bf7a407db382d2f3f2d81bb1224f49fe518f6d48d37c60208201819052918101829052606081018590526080810184905260a081018390526000919070014551231950b75fc4402da1732fc9bebe199060029060c00160408051601f198184030181529082905261288f91613d9d565b602060405180830381855afa1580156128ac573d6000803e3d6000fd5b5050506040513d601f19601f820116820180604052508101906128cf9190613db9565b6128d99190613df0565b95945050505050565b60085460ff1615610e205760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b6044820152606401610c99565b6001600160a01b03821660009081526003602052604081208054839290612950908490613c8e565b90915550506002805482900390556040518181526000906001600160a01b03841690600080516020613f16833981519152906020015b60405180910390a35050565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60006040516129c49190613e04565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b612a34613089565b6008805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a1565b600680546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b60008311612b135760405162461bcd60e51b815260206004820152601060248201526f4d494e545f414d4f554e545f5a45524f60801b6044820152606401610c99565b612b256305f5e1006301406f40613ea3565b8310612b695760405162461bcd60e51b81526020600482015260136024820152724d494e545f414d4f554e545f544f4f5f42494760681b6044820152606401610c99565b306001600160a01b03831603612bc15760405162461bcd60e51b815260206004820152601c60248201527f4d494e545f544f5f5448455f434f4e54524143545f41444452455353000000006044820152606401610c99565b6000818152600c602052604090205460ff1615612c195760405162461bcd60e51b81526020600482015260166024820152751352539517d053149150511657d41493d0d154d4d15160521b6044820152606401610c99565b6000818152600c60205260409020805460ff19166001179055612c3c82846130d2565b60408051848152602081018390526001600160a01b038416917fb73f3e96d1e157f064cb3a8d0abed06bcec05e5515bf7486364c027dab6aa4699101610d69565b61267181604051602401612c9191906136cc565b60408051601f198184030181529190526020810180516001600160e01b031663104c13eb60e21b179052613124565b6040516001600160f81b0319821660248201526126719060440160408051601f198184030181529190526020810180516001600160e01b0316630dc3142560e31b179052613124565b612d116128e2565b6008805460ff191660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258612a613390565b606061071d82613145565b61267181604051602401612d6591906136cc565b60408051601f198184030181529190526020810180516001600160e01b03166305f3bfab60e11b179052613124565b61267181604051602401612daa91815260200190565b60408051601f198184030181529190526020810180516001600160e01b03166327b7cf8560e01b179052613124565b60008160ff1660491480612df057508160ff16604f145b80612dfe57508160ff16606c145b15612e0b57506000919050565b60318260ff1610158015612e23575060398260ff1611155b15612e3057506001919050565b60418260ff1610158015612e485750605a8260ff1611155b15612e5557506001919050565b60618260ff1610158015612e6d5750607a8260ff1611155b15612e7a57506001919050565b506000919050565b61267181604051602401612e9891815260200190565b60408051601f198184030181529190526020810180516001600160e01b031663f5b1bba960e01b179052613124565b612f0c8282604051602401612edd929190613eba565b60408051601f198184030181529190526020810180516001600160e01b0316634b5c427760e01b179052613124565b5050565b612f0c8282604051602401612f26929190613edf565b60408051601f198184030181529190526020810180516001600160e01b03166309710a9d60e41b179052613124565b60007f7d21eae75cdfb1cf6d27ca9a73c6341b49bc05072228b51a7cd4df444c1c8710612f8860408401602085016139ae565b60408051602081019390935260609190911b6bffffffffffffffffffffffff191682820152830135605482015282356074820152609401611b4a565b6000806401000003d019816007828610612fe657506000958695509350505050565b60008380612ff657612ff6613c62565b848061300457613004613c62565b83868061301357613013613c62565b868b0908858061302557613025613c62565b868061303357613033613c62565b8a8b098a0908905061305c81600461304c876001613ca1565b6130569190613f01565b8661348f565b905060006001821615613078576130738286613c8e565b61307a565b815b98600198509650505050505050565b60085460ff16610e205760405162461bcd60e51b815260206004820152601460248201527314185d5cd8589b194e881b9bdd081c185d5cd95960621b6044820152606401610c99565b80600260008282546130e49190613ca1565b90915550506001600160a01b038216600081815260036020908152604080832080548601905551848152600080516020613f168339815191529101612986565b80516a636f6e736f6c652e6c6f67602083016000808483855afa5050505050565b80516060906031906000805b828110801561317857508386828151811061316e5761316e613d3f565b016020015160f81c145b156131895760019182019101613151565b5060008080806117e361209f8702046001016002026001600160401b038111156131b5576131b5613862565b6040519080825280601f01601f1916602001820160405280156131df576020820181803683370190505b5090506000600460038801046001600160401b0381111561320257613202613862565b60405190808252806020026020018201604052801561322b578160200160208202803683370190505b50905060005b8a5181101561334d5760008b828151811061324e5761324e613d3f565b602001015160f81c60f81b905061327d6040518060600160405280603a8152602001613f36603a913982613558565b9096509450846132c65760405162461bcd60e51b81526020600482015260146024820152731a5b9d985b1a590818985cd94d4e08191a59da5d60621b6044820152606401610c99565b8251600019015b6000811261334357868482815181106132e8576132e8613d3f565b602002602001015163ffffffff16603a026001600160401b0316019750602088901c96508763ffffffff1684828151811061332557613325613d3f565b63ffffffff90921660209283029190910190910152600019016132cd565b5050600101613231565b50600860038816026001600160401b03811660000361336a575060205b600719016000805b8351811015613414575b6020836001600160401b0316101561340857826001600160401b03168482815181106133aa576133aa613d3f565b602002602001015163ffffffff16901c60f81b8583815181106133cf576133cf613d3f565b60200101906001600160f81b031916908160001a90535060019091019060086001600160401b038416106134085760088303925061337c565b60189250600101613372565b50875b845181101561347257600060f81b85828151811061343757613437613d3f565b01602001516001600160f81b031916111561346a57613459858a8303846135bb565b9d9c50505050505050505050505050565b600101613417565b5061347f846000836135bb565b9c9b505050505050505050505050565b6000816000036134d35760405162461bcd60e51b815260206004820152600f60248201526e4d6f64756c7573206973207a65726f60881b6044820152606401610c99565b836000036134e3575060006109ce565b826000036134f3575060016109ce565b6001600160ff1b5b801561354f57838186161515870a85848509099150836002820486161515870a85848509099150836004820486161515870a85848509099150836008820486161515870a85848509099150601090046134fb565b50949350505050565b60008060005b84518110156135ab57836001600160f81b03191685828151811061358457613584613d3f565b01602001516001600160f81b031916036135a3579150600190506135b4565b60010161355e565b50600080915091505b9250929050565b606060008383036001600160401b038111156135d9576135d9613862565b6040519080825280601f01601f191660200182016040528015613603576020820181803683370190505b50905060005b84840381101561354f57858582018151811061362757613627613d3f565b602001015160f81c60f81b82828151811061364457613644613d3f565b60200101906001600160f81b031916908160001a905350600101613609565b60006020828403121561367557600080fd5b5035919050565b60005b8381101561369757818101518382015260200161367f565b50506000910152565b600081518084526136b881602086016020860161367c565b601f01601f19169290920160200192915050565b6020815260006109ce60208301846136a0565b80356001600160a01b03811681146125eb57600080fd5b6000806040838503121561370957600080fd5b613712836136df565b946020939093013593505050565b8035600381106125eb57600080fd5b60008083601f84011261374157600080fd5b5081356001600160401b0381111561375857600080fd5b6020830191508360208285010111156135b457600080fd5b60008060006040848603121561378557600080fd5b61378e84613720565b925060208401356001600160401b038111156137a957600080fd5b6137b58682870161372f565b9497909650939450505050565b600080600080608085870312156137d857600080fd5b5050823594602084013594506040840135936060013592509050565b60008060006060848603121561380957600080fd5b613812846136df565b9250613820602085016136df565b9150604084013590509250925092565b60008060006040848603121561384557600080fd5b8335925060208401356001600160401b038111156137a957600080fd5b634e487b7160e01b600052604160045260246000fd5b60006001600160401b038084111561389257613892613862565b604051601f8501601f19908116603f011681019082821181831017156138ba576138ba613862565b816040528093508581528686860111156138d357600080fd5b858560208301376000602087830101525050509392505050565b600082601f8301126138fe57600080fd5b6109ce83833560208501613878565b6000806040838503121561392057600080fd5b82356001600160401b038082111561393757600080fd5b613943868387016138ed565b9350602085013591508082111561395957600080fd5b50613966858286016138ed565b9150509250929050565b634e487b7160e01b600052602160045260246000fd5b60208101600383106139a857634e487b7160e01b600052602160045260246000fd5b91905290565b6000602082840312156139c057600080fd5b6109ce826136df565b6000806000606084860312156139de57600080fd5b83359250613820602085016136df565b600060208284031215613a0057600080fd5b81356001600160f81b0319811681146109ce57600080fd5b60008060208385031215613a2b57600080fd5b82356001600160401b03811115613a4157600080fd5b613a4d8582860161372f565b90969095509350505050565b600060608284031215613a6b57600080fd5b50919050565b600080600060808486031215613a8657600080fd5b613a908585613a59565b925060608401356001600160401b038111156137a957600080fd5b600060208284031215613abd57600080fd5b81356001600160401b03811115613ad357600080fd5b610b35848285016138ed565b600060208284031215613af157600080fd5b81356001600160401b03811115613b0757600080fd5b8201601f81018413613b1857600080fd5b610b3584823560208401613878565b600080600080600080600060e0888a031215613b4257600080fd5b613b4b886136df565b9650613b59602089016136df565b95506040880135945060608801359350608088013560ff81168114613b7d57600080fd5b9699959850939692959460a0840135945060c09093013592915050565b600060208284031215613bac57600080fd5b6109ce82613720565b60008060408385031215613bc857600080fd5b613bd1836136df565b9150613bdf602084016136df565b90509250929050565b600060608284031215613bfa57600080fd5b6109ce8383613a59565b600181811c90821680613c1857607f821691505b602082108103613a6b57634e487b7160e01b600052602260045260246000fd5b60008085851115613c4857600080fd5b83861115613c5557600080fd5b5050820193919092039150565b634e487b7160e01b600052601260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b8181038181111561071d5761071d613c78565b8082018082111561071d5761071d613c78565b606081528360608201528385608083013760006080858301015260006080601f19601f870116830101905083602083015282604083015295945050505050565b6020808252602b908201527f56616c696461746f724d65737361676552656365697665723a20494e56414c4960408201526a445f5349474e415455524560a81b606082015260800190565b634e487b7160e01b600052603260045260246000fd5b600060018201613d6757613d67613c78565b5060010190565b60008351613d8081846020880161367c565b835190830190613d9481836020880161367c565b01949350505050565b60008251613daf81846020870161367c565b9190910192915050565b600060208284031215613dcb57600080fd5b5051919050565b8035602083101561071d57600019602084900360031b1b1692915050565b600082613dff57613dff613c62565b500690565b600080835481600182811c915080831680613e2057607f831692505b60208084108203613e3f57634e487b7160e01b86526022600452602486fd5b818015613e535760018114613e6857613e95565b60ff1986168952841515850289019650613e95565b60008a81526020902060005b86811015613e8d5781548b820152908501908301613e74565b505084890196505b509498975050505050505050565b808202811582820484141761071d5761071d613c78565b604081526000613ecd60408301856136a0565b82810360208401526128d981856136a0565b604081526000613ef260408301856136a0565b90508260208301529392505050565b600082613f1057613f10613c62565b50049056feddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef31323334353637383941424344454647484a4b4c4d4e505152535455565758595a6162636465666768696a6b6d6e6f707172737475767778797aa264697066735822122020caec933d9a13d787330ff7014afd1cebca57a4e7f475fd8eef0dc5d9b4847a64736f6c63430008130033

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

0000000000000000000000000000000000000000000000000000000000000001

-----Decoded View---------------
Arg [0] : _network (uint8): 1

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


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

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