ETH Price: $1,975.78 (-5.19%)

Contract

0x3D282Cc0d69e27fBd4aa59DfD08D6a72B45Ce889
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To

There are no matching entries

Please try again later

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Validator Index Block Amount
View All Withdrawals

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

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

Contract Name:
MezoBridge

Compiler Version
v0.8.24+commit.e11b9ed9

Optimization Enabled:
Yes with 10000 runs

Other Settings:
paris EvmVersion

Contract Source Code (Solidity Standard Json-Input format)

// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.24;

import {BitcoinBridge} from "./BitcoinBridge.sol";
import {ERC20Bridge} from "./ERC20Bridge.sol";

/// @notice MezoBridge contract allows bridging Bitcoin (using tBTC) and other
///         ERC20 tokens from Ethereum to the Mezo chain.
/// @dev The contract inherits specific bridging capabilities from BitcoinBridge
///      and ERC20Bridge abstract contracts.
/// @dev The contract is supposed to be deployed behind a transparent
///      upgradeable proxy.
contract MezoBridge is BitcoinBridge, ERC20Bridge {
    /// @notice Holds the count of all bridging requests made so far. Includes
    ///         both Bitcoin and ERC20 bridging requests. It is incremented every
    ///         time a new bridging request is made. Its value is used to assign
    ///         sequence numbers to these requests which help to keep track of them.
    uint256 public sequence;

    event AssetsLocked(
        uint256 indexed sequenceNumber,
        address indexed recipient,
        address indexed token,
        uint256 amount
    );

    /// @custom:oz-upgrades-unsafe-allow constructor
    constructor() {
        _disableInitializers();
    }

    /// @notice Initializes the contract.
    /// @dev All addresses passed to the contract must not be 0x0.
    /// @param _tbtcBridge Address to the tBTC Bridge contract.
    /// @param _tbtcVault Address to the tBTC TBTCVault contract.
    /// @param _tbtcToken Address to the tBTC ERC20 token contract.
    /// @param _initialSequence Initial sequence number. Normally, it should be zero.
    ///        A non-zero value can be set when migrating from an old bridge contract,
    ///        to keep continuity in the sequence numbers on Mezo.
    function initialize(
        address _tbtcBridge,
        address _tbtcVault,
        address _tbtcToken,
        uint256 _initialSequence
    ) external initializer {
        __BitcoinBridge_initialize(_tbtcBridge, _tbtcVault, _tbtcToken);
        __ERC20Bridge_initialize();

        //slither-disable-next-line events-maths
        sequence = _initialSequence;
    }

    /// @notice Bridges the `amount` of the `token` to the `recipient` address on Mezo.
    /// @param recipient Recipient of the bridged token.
    /// @param token Address of the bridged token.
    /// @param amount Amount of the bridged token.
    /// @dev Increases the sequence number and emits the AssetsLocked event.
    ///      The AssetsLocked event is a sign for Mezo validators to process
    ///      the bridging request.
    function _bridge(
        address recipient,
        address token,
        uint256 amount
    ) internal override(BitcoinBridge, ERC20Bridge) {
        emit AssetsLocked(++sequence, recipient, token, amount);
    }
}

File 2 of 19 : BTCUtils.sol
pragma solidity ^0.8.4;

/** @title BitcoinSPV */
/** @author Summa (https://summa.one) */

import {BytesLib} from "./BytesLib.sol";
import {SafeMath} from "./SafeMath.sol";

library BTCUtils {
    using BytesLib for bytes;
    using SafeMath for uint256;

    // The target at minimum Difficulty. Also the target of the genesis block
    uint256 public constant DIFF1_TARGET = 0xffff0000000000000000000000000000000000000000000000000000;

    uint256 public constant RETARGET_PERIOD = 2 * 7 * 24 * 60 * 60;  // 2 weeks in seconds
    uint256 public constant RETARGET_PERIOD_BLOCKS = 2016;  // 2 weeks in blocks

    uint256 public constant ERR_BAD_ARG = 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff;

    /* ***** */
    /* UTILS */
    /* ***** */

    /// @notice         Determines the length of a VarInt in bytes
    /// @dev            A VarInt of >1 byte is prefixed with a flag indicating its length
    /// @param _flag    The first byte of a VarInt
    /// @return         The number of non-flag bytes in the VarInt
    function determineVarIntDataLength(bytes memory _flag) internal pure returns (uint8) {
        return determineVarIntDataLengthAt(_flag, 0);
    }

    /// @notice         Determines the length of a VarInt in bytes
    /// @dev            A VarInt of >1 byte is prefixed with a flag indicating its length
    /// @param _b       The byte array containing a VarInt
    /// @param _at      The position of the VarInt in the array
    /// @return         The number of non-flag bytes in the VarInt
    function determineVarIntDataLengthAt(bytes memory _b, uint256 _at) internal pure returns (uint8) {
        if (uint8(_b[_at]) == 0xff) {
            return 8;  // one-byte flag, 8 bytes data
        }
        if (uint8(_b[_at]) == 0xfe) {
            return 4;  // one-byte flag, 4 bytes data
        }
        if (uint8(_b[_at]) == 0xfd) {
            return 2;  // one-byte flag, 2 bytes data
        }

        return 0;  // flag is data
    }

    /// @notice     Parse a VarInt into its data length and the number it represents
    /// @dev        Useful for Parsing Vins and Vouts. Returns ERR_BAD_ARG if insufficient bytes.
    ///             Caller SHOULD explicitly handle this case (or bubble it up)
    /// @param _b   A byte-string starting with a VarInt
    /// @return     number of bytes in the encoding (not counting the tag), the encoded int
    function parseVarInt(bytes memory _b) internal pure returns (uint256, uint256) {
        return parseVarIntAt(_b, 0);
    }

    /// @notice     Parse a VarInt into its data length and the number it represents
    /// @dev        Useful for Parsing Vins and Vouts. Returns ERR_BAD_ARG if insufficient bytes.
    ///             Caller SHOULD explicitly handle this case (or bubble it up)
    /// @param _b   A byte-string containing a VarInt
    /// @param _at  The position of the VarInt
    /// @return     number of bytes in the encoding (not counting the tag), the encoded int
    function parseVarIntAt(bytes memory _b, uint256 _at) internal pure returns (uint256, uint256) {
        uint8 _dataLen = determineVarIntDataLengthAt(_b, _at);

        if (_dataLen == 0) {
            return (0, uint8(_b[_at]));
        }
        if (_b.length < 1 + _dataLen + _at) {
            return (ERR_BAD_ARG, 0);
        }
        uint256 _number;
        if (_dataLen == 2) {
            _number = reverseUint16(uint16(_b.slice2(1 + _at)));
        } else if (_dataLen == 4) {
            _number = reverseUint32(uint32(_b.slice4(1 + _at)));
        } else if (_dataLen == 8) {
            _number = reverseUint64(uint64(_b.slice8(1 + _at)));
        }
        return (_dataLen, _number);
    }

    /// @notice          Changes the endianness of a byte array
    /// @dev             Returns a new, backwards, bytes
    /// @param _b        The bytes to reverse
    /// @return          The reversed bytes
    function reverseEndianness(bytes memory _b) internal pure returns (bytes memory) {
        bytes memory _newValue = new bytes(_b.length);

        for (uint i = 0; i < _b.length; i++) {
            _newValue[_b.length - i - 1] = _b[i];
        }

        return _newValue;
    }

    /// @notice          Changes the endianness of a uint256
    /// @dev             https://graphics.stanford.edu/~seander/bithacks.html#ReverseParallel
    /// @param _b        The unsigned integer to reverse
    /// @return v        The reversed value
    function reverseUint256(uint256 _b) internal pure returns (uint256 v) {
        v = _b;

        // swap bytes
        v = ((v >> 8) & 0x00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF) |
            ((v & 0x00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF) << 8);
        // swap 2-byte long pairs
        v = ((v >> 16) & 0x0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF) |
            ((v & 0x0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF) << 16);
        // swap 4-byte long pairs
        v = ((v >> 32) & 0x00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF) |
            ((v & 0x00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF) << 32);
        // swap 8-byte long pairs
        v = ((v >> 64) & 0x0000000000000000FFFFFFFFFFFFFFFF0000000000000000FFFFFFFFFFFFFFFF) |
            ((v & 0x0000000000000000FFFFFFFFFFFFFFFF0000000000000000FFFFFFFFFFFFFFFF) << 64);
        // swap 16-byte long pairs
        v = (v >> 128) | (v << 128);
    }

    /// @notice          Changes the endianness of a uint64
    /// @param _b        The unsigned integer to reverse
    /// @return v        The reversed value
    function reverseUint64(uint64 _b) internal pure returns (uint64 v) {
        v = _b;

        // swap bytes
        v = ((v >> 8) & 0x00FF00FF00FF00FF) |
            ((v & 0x00FF00FF00FF00FF) << 8);
        // swap 2-byte long pairs
        v = ((v >> 16) & 0x0000FFFF0000FFFF) |
            ((v & 0x0000FFFF0000FFFF) << 16);
        // swap 4-byte long pairs
        v = (v >> 32) | (v << 32);
    }

    /// @notice          Changes the endianness of a uint32
    /// @param _b        The unsigned integer to reverse
    /// @return v        The reversed value
    function reverseUint32(uint32 _b) internal pure returns (uint32 v) {
        v = _b;

        // swap bytes
        v = ((v >> 8) & 0x00FF00FF) |
            ((v & 0x00FF00FF) << 8);
        // swap 2-byte long pairs
        v = (v >> 16) | (v << 16);
    }

    /// @notice          Changes the endianness of a uint24
    /// @param _b        The unsigned integer to reverse
    /// @return v        The reversed value
    function reverseUint24(uint24 _b) internal pure returns (uint24 v) {
        v =  (_b << 16) | (_b & 0x00FF00) | (_b >> 16);
    }

    /// @notice          Changes the endianness of a uint16
    /// @param _b        The unsigned integer to reverse
    /// @return v        The reversed value
    function reverseUint16(uint16 _b) internal pure returns (uint16 v) {
        v =  (_b << 8) | (_b >> 8);
    }


    /// @notice          Converts big-endian bytes to a uint
    /// @dev             Traverses the byte array and sums the bytes
    /// @param _b        The big-endian bytes-encoded integer
    /// @return          The integer representation
    function bytesToUint(bytes memory _b) internal pure returns (uint256) {
        uint256 _number;

        for (uint i = 0; i < _b.length; i++) {
            _number = _number + uint8(_b[i]) * (2 ** (8 * (_b.length - (i + 1))));
        }

        return _number;
    }

    /// @notice          Get the last _num bytes from a byte array
    /// @param _b        The byte array to slice
    /// @param _num      The number of bytes to extract from the end
    /// @return          The last _num bytes of _b
    function lastBytes(bytes memory _b, uint256 _num) internal pure returns (bytes memory) {
        uint256 _start = _b.length.sub(_num);

        return _b.slice(_start, _num);
    }

    /// @notice          Implements bitcoin's hash160 (rmd160(sha2()))
    /// @dev             abi.encodePacked changes the return to bytes instead of bytes32
    /// @param _b        The pre-image
    /// @return          The digest
    function hash160(bytes memory _b) internal pure returns (bytes memory) {
        return abi.encodePacked(ripemd160(abi.encodePacked(sha256(_b))));
    }

    /// @notice          Implements bitcoin's hash160 (sha2 + ripemd160)
    /// @dev             sha2 precompile at address(2), ripemd160 at address(3)
    /// @param _b        The pre-image
    /// @return res      The digest
    function hash160View(bytes memory _b) internal view returns (bytes20 res) {
        // solium-disable-next-line security/no-inline-assembly
        assembly {
            pop(staticcall(gas(), 2, add(_b, 32), mload(_b), 0x00, 32))
            pop(staticcall(gas(), 3, 0x00, 32, 0x00, 32))
            // read from position 12 = 0c
            res := mload(0x0c)
        }
    }

    /// @notice          Implements bitcoin's hash256 (double sha2)
    /// @dev             abi.encodePacked changes the return to bytes instead of bytes32
    /// @param _b        The pre-image
    /// @return          The digest
    function hash256(bytes memory _b) internal pure returns (bytes32) {
        return sha256(abi.encodePacked(sha256(_b)));
    }

    /// @notice          Implements bitcoin's hash256 (double sha2)
    /// @dev             sha2 is precompiled smart contract located at address(2)
    /// @param _b        The pre-image
    /// @return res      The digest
    function hash256View(bytes memory _b) internal view returns (bytes32 res) {
        // solium-disable-next-line security/no-inline-assembly
        assembly {
            pop(staticcall(gas(), 2, add(_b, 32), mload(_b), 0x00, 32))
            pop(staticcall(gas(), 2, 0x00, 32, 0x00, 32))
            res := mload(0x00)
        }
    }

    /// @notice          Implements bitcoin's hash256 on a pair of bytes32
    /// @dev             sha2 is precompiled smart contract located at address(2)
    /// @param _a        The first bytes32 of the pre-image
    /// @param _b        The second bytes32 of the pre-image
    /// @return res      The digest
    function hash256Pair(bytes32 _a, bytes32 _b) internal view returns (bytes32 res) {
        // solium-disable-next-line security/no-inline-assembly
        assembly {
            mstore(0x00, _a)
            mstore(0x20, _b)
            pop(staticcall(gas(), 2, 0x00, 64, 0x00, 32))
            pop(staticcall(gas(), 2, 0x00, 32, 0x00, 32))
            res := mload(0x00)
        }
    }

    /// @notice          Implements bitcoin's hash256 (double sha2)
    /// @dev             sha2 is precompiled smart contract located at address(2)
    /// @param _b        The array containing the pre-image
    /// @param at        The start of the pre-image
    /// @param len       The length of the pre-image
    /// @return res      The digest
    function hash256Slice(
        bytes memory _b,
        uint256 at,
        uint256 len
    ) internal view returns (bytes32 res) {
        // solium-disable-next-line security/no-inline-assembly
        assembly {
            pop(staticcall(gas(), 2, add(_b, add(32, at)), len, 0x00, 32))
            pop(staticcall(gas(), 2, 0x00, 32, 0x00, 32))
            res := mload(0x00)
        }
    }

    /* ************ */
    /* Legacy Input */
    /* ************ */

    /// @notice          Extracts the nth input from the vin (0-indexed)
    /// @dev             Iterates over the vin. If you need to extract several, write a custom function
    /// @param _vin      The vin as a tightly-packed byte array
    /// @param _index    The 0-indexed location of the input to extract
    /// @return          The input as a byte array
    function extractInputAtIndex(bytes memory _vin, uint256 _index) internal pure returns (bytes memory) {
        uint256 _varIntDataLen;
        uint256 _nIns;

        (_varIntDataLen, _nIns) = parseVarInt(_vin);
        require(_varIntDataLen != ERR_BAD_ARG, "Read overrun during VarInt parsing");
        require(_index < _nIns, "Vin read overrun");

        uint256 _len = 0;
        uint256 _offset = 1 + _varIntDataLen;

        for (uint256 _i = 0; _i < _index; _i ++) {
            _len = determineInputLengthAt(_vin, _offset);
            require(_len != ERR_BAD_ARG, "Bad VarInt in scriptSig");
            _offset = _offset + _len;
        }

        _len = determineInputLengthAt(_vin, _offset);
        require(_len != ERR_BAD_ARG, "Bad VarInt in scriptSig");
        return _vin.slice(_offset, _len);
    }

    /// @notice          Determines whether an input is legacy
    /// @dev             False if no scriptSig, otherwise True
    /// @param _input    The input
    /// @return          True for legacy, False for witness
    function isLegacyInput(bytes memory _input) internal pure returns (bool) {
        return _input[36] != hex"00";
    }

    /// @notice          Determines the length of a scriptSig in an input
    /// @dev             Will return 0 if passed a witness input.
    /// @param _input    The LEGACY input
    /// @return          The length of the script sig
    function extractScriptSigLen(bytes memory _input) internal pure returns (uint256, uint256) {
        return extractScriptSigLenAt(_input, 0);
    }

    /// @notice          Determines the length of a scriptSig in an input
    ///                  starting at the specified position
    /// @dev             Will return 0 if passed a witness input.
    /// @param _input    The byte array containing the LEGACY input
    /// @param _at       The position of the input in the array
    /// @return          The length of the script sig
    function extractScriptSigLenAt(bytes memory _input, uint256 _at) internal pure returns (uint256, uint256) {
        if (_input.length < 37 + _at) {
            return (ERR_BAD_ARG, 0);
        }

        uint256 _varIntDataLen;
        uint256 _scriptSigLen;
        (_varIntDataLen, _scriptSigLen) = parseVarIntAt(_input, _at + 36);

        return (_varIntDataLen, _scriptSigLen);
    }

    /// @notice          Determines the length of an input from its scriptSig
    /// @dev             36 for outpoint, 1 for scriptSig length, 4 for sequence
    /// @param _input    The input
    /// @return          The length of the input in bytes
    function determineInputLength(bytes memory _input) internal pure returns (uint256) {
        return determineInputLengthAt(_input, 0);
    }

    /// @notice          Determines the length of an input from its scriptSig,
    ///                  starting at the specified position
    /// @dev             36 for outpoint, 1 for scriptSig length, 4 for sequence
    /// @param _input    The byte array containing the input
    /// @param _at       The position of the input in the array
    /// @return          The length of the input in bytes
    function determineInputLengthAt(bytes memory _input, uint256 _at) internal pure returns (uint256) {
        uint256 _varIntDataLen;
        uint256 _scriptSigLen;
        (_varIntDataLen, _scriptSigLen) = extractScriptSigLenAt(_input, _at);
        if (_varIntDataLen == ERR_BAD_ARG) {
            return ERR_BAD_ARG;
        }

        return 36 + 1 + _varIntDataLen + _scriptSigLen + 4;
    }

    /// @notice          Extracts the LE sequence bytes from an input
    /// @dev             Sequence is used for relative time locks
    /// @param _input    The LEGACY input
    /// @return          The sequence bytes (LE uint)
    function extractSequenceLELegacy(bytes memory _input) internal pure returns (bytes4) {
        uint256 _varIntDataLen;
        uint256 _scriptSigLen;
        (_varIntDataLen, _scriptSigLen) = extractScriptSigLen(_input);
        require(_varIntDataLen != ERR_BAD_ARG, "Bad VarInt in scriptSig");
        return _input.slice4(36 + 1 + _varIntDataLen + _scriptSigLen);
    }

    /// @notice          Extracts the sequence from the input
    /// @dev             Sequence is a 4-byte little-endian number
    /// @param _input    The LEGACY input
    /// @return          The sequence number (big-endian uint)
    function extractSequenceLegacy(bytes memory _input) internal pure returns (uint32) {
        uint32 _leSeqence = uint32(extractSequenceLELegacy(_input));
        uint32 _beSequence = reverseUint32(_leSeqence);
        return _beSequence;
    }
    /// @notice          Extracts the VarInt-prepended scriptSig from the input in a tx
    /// @dev             Will return hex"00" if passed a witness input
    /// @param _input    The LEGACY input
    /// @return          The length-prepended scriptSig
    function extractScriptSig(bytes memory _input) internal pure returns (bytes memory) {
        uint256 _varIntDataLen;
        uint256 _scriptSigLen;
        (_varIntDataLen, _scriptSigLen) = extractScriptSigLen(_input);
        require(_varIntDataLen != ERR_BAD_ARG, "Bad VarInt in scriptSig");
        return _input.slice(36, 1 + _varIntDataLen + _scriptSigLen);
    }


    /* ************* */
    /* Witness Input */
    /* ************* */

    /// @notice          Extracts the LE sequence bytes from an input
    /// @dev             Sequence is used for relative time locks
    /// @param _input    The WITNESS input
    /// @return          The sequence bytes (LE uint)
    function extractSequenceLEWitness(bytes memory _input) internal pure returns (bytes4) {
        return _input.slice4(37);
    }

    /// @notice          Extracts the sequence from the input in a tx
    /// @dev             Sequence is a 4-byte little-endian number
    /// @param _input    The WITNESS input
    /// @return          The sequence number (big-endian uint)
    function extractSequenceWitness(bytes memory _input) internal pure returns (uint32) {
        uint32 _leSeqence = uint32(extractSequenceLEWitness(_input));
        uint32 _inputeSequence = reverseUint32(_leSeqence);
        return _inputeSequence;
    }

    /// @notice          Extracts the outpoint from the input in a tx
    /// @dev             32-byte tx id with 4-byte index
    /// @param _input    The input
    /// @return          The outpoint (LE bytes of prev tx hash + LE bytes of prev tx index)
    function extractOutpoint(bytes memory _input) internal pure returns (bytes memory) {
        return _input.slice(0, 36);
    }

    /// @notice          Extracts the outpoint tx id from an input
    /// @dev             32-byte tx id
    /// @param _input    The input
    /// @return          The tx id (little-endian bytes)
    function extractInputTxIdLE(bytes memory _input) internal pure returns (bytes32) {
        return _input.slice32(0);
    }

    /// @notice          Extracts the outpoint tx id from an input
    ///                  starting at the specified position
    /// @dev             32-byte tx id
    /// @param _input    The byte array containing the input
    /// @param _at       The position of the input
    /// @return          The tx id (little-endian bytes)
    function extractInputTxIdLeAt(bytes memory _input, uint256 _at) internal pure returns (bytes32) {
        return _input.slice32(_at);
    }

    /// @notice          Extracts the LE tx input index from the input in a tx
    /// @dev             4-byte tx index
    /// @param _input    The input
    /// @return          The tx index (little-endian bytes)
    function extractTxIndexLE(bytes memory _input) internal pure returns (bytes4) {
        return _input.slice4(32);
    }

    /// @notice          Extracts the LE tx input index from the input in a tx
    ///                  starting at the specified position
    /// @dev             4-byte tx index
    /// @param _input    The byte array containing the input
    /// @param _at       The position of the input
    /// @return          The tx index (little-endian bytes)
    function extractTxIndexLeAt(bytes memory _input, uint256 _at) internal pure returns (bytes4) {
        return _input.slice4(32 + _at);
    }

    /* ****** */
    /* Output */
    /* ****** */

    /// @notice          Determines the length of an output
    /// @dev             Works with any properly formatted output
    /// @param _output   The output
    /// @return          The length indicated by the prefix, error if invalid length
    function determineOutputLength(bytes memory _output) internal pure returns (uint256) {
        return determineOutputLengthAt(_output, 0);
    }

    /// @notice          Determines the length of an output
    ///                  starting at the specified position
    /// @dev             Works with any properly formatted output
    /// @param _output   The byte array containing the output
    /// @param _at       The position of the output
    /// @return          The length indicated by the prefix, error if invalid length
    function determineOutputLengthAt(bytes memory _output, uint256 _at) internal pure returns (uint256) {
        if (_output.length < 9 + _at) {
            return ERR_BAD_ARG;
        }
        uint256 _varIntDataLen;
        uint256 _scriptPubkeyLength;
        (_varIntDataLen, _scriptPubkeyLength) = parseVarIntAt(_output, 8 + _at);

        if (_varIntDataLen == ERR_BAD_ARG) {
            return ERR_BAD_ARG;
        }

        // 8-byte value, 1-byte for tag itself
        return 8 + 1 + _varIntDataLen + _scriptPubkeyLength;
    }

    /// @notice          Extracts the output at a given index in the TxOuts vector
    /// @dev             Iterates over the vout. If you need to extract multiple, write a custom function
    /// @param _vout     The _vout to extract from
    /// @param _index    The 0-indexed location of the output to extract
    /// @return          The specified output
    function extractOutputAtIndex(bytes memory _vout, uint256 _index) internal pure returns (bytes memory) {
        uint256 _varIntDataLen;
        uint256 _nOuts;

        (_varIntDataLen, _nOuts) = parseVarInt(_vout);
        require(_varIntDataLen != ERR_BAD_ARG, "Read overrun during VarInt parsing");
        require(_index < _nOuts, "Vout read overrun");

        uint256 _len = 0;
        uint256 _offset = 1 + _varIntDataLen;

        for (uint256 _i = 0; _i < _index; _i ++) {
            _len = determineOutputLengthAt(_vout, _offset);
            require(_len != ERR_BAD_ARG, "Bad VarInt in scriptPubkey");
            _offset += _len;
        }

        _len = determineOutputLengthAt(_vout, _offset);
        require(_len != ERR_BAD_ARG, "Bad VarInt in scriptPubkey");
        return _vout.slice(_offset, _len);
    }

    /// @notice          Extracts the value bytes from the output in a tx
    /// @dev             Value is an 8-byte little-endian number
    /// @param _output   The output
    /// @return          The output value as LE bytes
    function extractValueLE(bytes memory _output) internal pure returns (bytes8) {
        return _output.slice8(0);
    }

    /// @notice          Extracts the value from the output in a tx
    /// @dev             Value is an 8-byte little-endian number
    /// @param _output   The output
    /// @return          The output value
    function extractValue(bytes memory _output) internal pure returns (uint64) {
        uint64 _leValue = uint64(extractValueLE(_output));
        uint64 _beValue = reverseUint64(_leValue);
        return _beValue;
    }

    /// @notice          Extracts the value from the output in a tx
    /// @dev             Value is an 8-byte little-endian number
    /// @param _output   The byte array containing the output
    /// @param _at       The starting index of the output in the array
    /// @return          The output value
    function extractValueAt(bytes memory _output, uint256 _at) internal pure returns (uint64) {
        uint64 _leValue = uint64(_output.slice8(_at));
        uint64 _beValue = reverseUint64(_leValue);
        return _beValue;
    }

    /// @notice          Extracts the data from an op return output
    /// @dev             Returns hex"" if no data or not an op return
    /// @param _output   The output
    /// @return          Any data contained in the opreturn output, null if not an op return
    function extractOpReturnData(bytes memory _output) internal pure returns (bytes memory) {
        if (_output[9] != hex"6a") {
            return hex"";
        }
        bytes1 _dataLen = _output[10];
        return _output.slice(11, uint256(uint8(_dataLen)));
    }

    /// @notice          Extracts the hash from the output script
    /// @dev             Determines type by the length prefix and validates format
    /// @param _output   The output
    /// @return          The hash committed to by the pk_script, or null for errors
    function extractHash(bytes memory _output) internal pure returns (bytes memory) {
        return extractHashAt(_output, 8, _output.length - 8);
    }

    /// @notice          Extracts the hash from the output script
    /// @dev             Determines type by the length prefix and validates format
    /// @param _output   The byte array containing the output
    /// @param _at       The starting index of the output script in the array
    ///                  (output start + 8)
    /// @param _len      The length of the output script
    ///                  (output length - 8)
    /// @return          The hash committed to by the pk_script, or null for errors
    function extractHashAt(
        bytes memory _output,
        uint256 _at,
        uint256 _len
    ) internal pure returns (bytes memory) {
        uint8 _scriptLen = uint8(_output[_at]);

        // don't have to worry about overflow here.
        // if _scriptLen + 1 overflows, then output length would have to be < 1
        // for this check to pass. if it's < 1, then we errored when assigning
        // _scriptLen
        if (_scriptLen + 1 != _len) {
            return hex"";
        }

        if (uint8(_output[_at + 1]) == 0) {
            if (_scriptLen < 2) {
                return hex"";
            }
            uint256 _payloadLen = uint8(_output[_at + 2]);
            // Check for maliciously formatted witness outputs.
            // No need to worry about underflow as long b/c of the `< 2` check
            if (_payloadLen != _scriptLen - 2 || (_payloadLen != 0x20 && _payloadLen != 0x14)) {
                return hex"";
            }
            return _output.slice(_at + 3, _payloadLen);
        } else {
            bytes3 _tag = _output.slice3(_at);
            // p2pkh
            if (_tag == hex"1976a9") {
                // Check for maliciously formatted p2pkh
                // No need to worry about underflow, b/c of _scriptLen check
                if (uint8(_output[_at + 3]) != 0x14 ||
                    _output.slice2(_at + _len - 2) != hex"88ac") {
                    return hex"";
                }
                return _output.slice(_at + 4, 20);
            //p2sh
            } else if (_tag == hex"17a914") {
                // Check for maliciously formatted p2sh
                // No need to worry about underflow, b/c of _scriptLen check
                if (uint8(_output[_at + _len - 1]) != 0x87) {
                    return hex"";
                }
                return _output.slice(_at + 3, 20);
            }
        }
        return hex"";  /* NB: will trigger on OPRETURN and any non-standard that doesn't overrun */
    }

    /* ********** */
    /* Witness TX */
    /* ********** */


    /// @notice      Checks that the vin passed up is properly formatted
    /// @dev         Consider a vin with a valid vout in its scriptsig
    /// @param _vin  Raw bytes length-prefixed input vector
    /// @return      True if it represents a validly formatted vin
    function validateVin(bytes memory _vin) internal pure returns (bool) {
        uint256 _varIntDataLen;
        uint256 _nIns;

        (_varIntDataLen, _nIns) = parseVarInt(_vin);

        // Not valid if it says there are too many or no inputs
        if (_nIns == 0 || _varIntDataLen == ERR_BAD_ARG) {
            return false;
        }

        uint256 _offset = 1 + _varIntDataLen;

        for (uint256 i = 0; i < _nIns; i++) {
            // If we're at the end, but still expect more
            if (_offset >= _vin.length) {
                return false;
            }

            // Grab the next input and determine its length.
            uint256 _nextLen = determineInputLengthAt(_vin, _offset);
            if (_nextLen == ERR_BAD_ARG) {
                return false;
            }

            // Increase the offset by that much
            _offset += _nextLen;
        }

        // Returns false if we're not exactly at the end
        return _offset == _vin.length;
    }

    /// @notice      Checks that the vout passed up is properly formatted
    /// @dev         Consider a vout with a valid scriptpubkey
    /// @param _vout Raw bytes length-prefixed output vector
    /// @return      True if it represents a validly formatted vout
    function validateVout(bytes memory _vout) internal pure returns (bool) {
        uint256 _varIntDataLen;
        uint256 _nOuts;

        (_varIntDataLen, _nOuts) = parseVarInt(_vout);

        // Not valid if it says there are too many or no outputs
        if (_nOuts == 0 || _varIntDataLen == ERR_BAD_ARG) {
            return false;
        }

        uint256 _offset = 1 + _varIntDataLen;

        for (uint256 i = 0; i < _nOuts; i++) {
            // If we're at the end, but still expect more
            if (_offset >= _vout.length) {
                return false;
            }

            // Grab the next output and determine its length.
            // Increase the offset by that much
            uint256 _nextLen = determineOutputLengthAt(_vout, _offset);
            if (_nextLen == ERR_BAD_ARG) {
                return false;
            }

            _offset += _nextLen;
        }

        // Returns false if we're not exactly at the end
        return _offset == _vout.length;
    }



    /* ************ */
    /* Block Header */
    /* ************ */

    /// @notice          Extracts the transaction merkle root from a block header
    /// @dev             Use verifyHash256Merkle to verify proofs with this root
    /// @param _header   The header
    /// @return          The merkle root (little-endian)
    function extractMerkleRootLE(bytes memory _header) internal pure returns (bytes32) {
        return _header.slice32(36);
    }

    /// @notice          Extracts the target from a block header
    /// @dev             Target is a 256-bit number encoded as a 3-byte mantissa and 1-byte exponent
    /// @param _header   The header
    /// @return          The target threshold
    function extractTarget(bytes memory _header) internal pure returns (uint256) {
        return extractTargetAt(_header, 0);
    }

    /// @notice          Extracts the target from a block header
    /// @dev             Target is a 256-bit number encoded as a 3-byte mantissa and 1-byte exponent
    /// @param _header   The array containing the header
    /// @param at        The start of the header
    /// @return          The target threshold
    function extractTargetAt(bytes memory _header, uint256 at) internal pure returns (uint256) {
        uint24 _m = uint24(_header.slice3(72 + at));
        uint8 _e = uint8(_header[75 + at]);
        uint256 _mantissa = uint256(reverseUint24(_m));
        uint _exponent = _e - 3;

        return _mantissa * (256 ** _exponent);
    }

    /// @notice          Calculate difficulty from the difficulty 1 target and current target
    /// @dev             Difficulty 1 is 0x1d00ffff on mainnet and testnet
    /// @dev             Difficulty 1 is a 256-bit number encoded as a 3-byte mantissa and 1-byte exponent
    /// @param _target   The current target
    /// @return          The block difficulty (bdiff)
    function calculateDifficulty(uint256 _target) internal pure returns (uint256) {
        // Difficulty 1 calculated from 0x1d00ffff
        return DIFF1_TARGET.div(_target);
    }

    /// @notice          Extracts the previous block's hash from a block header
    /// @dev             Block headers do NOT include block number :(
    /// @param _header   The header
    /// @return          The previous block's hash (little-endian)
    function extractPrevBlockLE(bytes memory _header) internal pure returns (bytes32) {
        return _header.slice32(4);
    }

    /// @notice          Extracts the previous block's hash from a block header
    /// @dev             Block headers do NOT include block number :(
    /// @param _header   The array containing the header
    /// @param at        The start of the header
    /// @return          The previous block's hash (little-endian)
    function extractPrevBlockLEAt(
        bytes memory _header,
        uint256 at
    ) internal pure returns (bytes32) {
        return _header.slice32(4 + at);
    }

    /// @notice          Extracts the timestamp from a block header
    /// @dev             Time is not 100% reliable
    /// @param _header   The header
    /// @return          The timestamp (little-endian bytes)
    function extractTimestampLE(bytes memory _header) internal pure returns (bytes4) {
        return _header.slice4(68);
    }

    /// @notice          Extracts the timestamp from a block header
    /// @dev             Time is not 100% reliable
    /// @param _header   The header
    /// @return          The timestamp (uint)
    function extractTimestamp(bytes memory _header) internal pure returns (uint32) {
        return reverseUint32(uint32(extractTimestampLE(_header)));
    }

    /// @notice          Extracts the expected difficulty from a block header
    /// @dev             Does NOT verify the work
    /// @param _header   The header
    /// @return          The difficulty as an integer
    function extractDifficulty(bytes memory _header) internal pure returns (uint256) {
        return calculateDifficulty(extractTarget(_header));
    }

    /// @notice          Concatenates and hashes two inputs for merkle proving
    /// @param _a        The first hash
    /// @param _b        The second hash
    /// @return          The double-sha256 of the concatenated hashes
    function _hash256MerkleStep(bytes memory _a, bytes memory _b) internal view returns (bytes32) {
        return hash256View(abi.encodePacked(_a, _b));
    }

    /// @notice          Concatenates and hashes two inputs for merkle proving
    /// @param _a        The first hash
    /// @param _b        The second hash
    /// @return          The double-sha256 of the concatenated hashes
    function _hash256MerkleStep(bytes32 _a, bytes32 _b) internal view returns (bytes32) {
        return hash256Pair(_a, _b);
    }


    /// @notice          Verifies a Bitcoin-style merkle tree
    /// @dev             Leaves are 0-indexed. Inefficient version.
    /// @param _proof    The proof. Tightly packed LE sha256 hashes. The last hash is the root
    /// @param _index    The index of the leaf
    /// @return          true if the proof is valid, else false
    function verifyHash256Merkle(bytes memory _proof, uint _index) internal view returns (bool) {
        // Not an even number of hashes
        if (_proof.length % 32 != 0) {
            return false;
        }

        // Special case for coinbase-only blocks
        if (_proof.length == 32) {
            return true;
        }

        // Should never occur
        if (_proof.length == 64) {
            return false;
        }

        bytes32 _root = _proof.slice32(_proof.length - 32);
        bytes32 _current = _proof.slice32(0);
        bytes memory _tree = _proof.slice(32, _proof.length - 64);

        return verifyHash256Merkle(_current, _tree, _root, _index);
    }

    /// @notice          Verifies a Bitcoin-style merkle tree
    /// @dev             Leaves are 0-indexed. Efficient version.
    /// @param _leaf     The leaf of the proof. LE sha256 hash.
    /// @param _tree     The intermediate nodes in the proof.
    ///                  Tightly packed LE sha256 hashes.
    /// @param _root     The root of the proof. LE sha256 hash.
    /// @param _index    The index of the leaf
    /// @return          true if the proof is valid, else false
    function verifyHash256Merkle(
        bytes32 _leaf,
        bytes memory _tree,
        bytes32 _root,
        uint _index
    ) internal view returns (bool) {
        // Not an even number of hashes
        if (_tree.length % 32 != 0) {
            return false;
        }

        // Should never occur
        if (_tree.length == 0) {
            return false;
        }

        uint _idx = _index;
        bytes32 _current = _leaf;

        // i moves in increments of 32
        for (uint i = 0; i < _tree.length; i += 32) {
            if (_idx % 2 == 1) {
                _current = _hash256MerkleStep(_tree.slice32(i), _current);
            } else {
                _current = _hash256MerkleStep(_current, _tree.slice32(i));
            }
            _idx = _idx >> 1;
        }
        return _current == _root;
    }

    /*
    NB: https://github.com/bitcoin/bitcoin/blob/78dae8caccd82cfbfd76557f1fb7d7557c7b5edb/src/pow.cpp#L49-L72
    NB: We get a full-bitlength target from this. For comparison with
        header-encoded targets we need to mask it with the header target
        e.g. (full & truncated) == truncated
    */
    /// @notice                 performs the bitcoin difficulty retarget
    /// @dev                    implements the Bitcoin algorithm precisely
    /// @param _previousTarget  the target of the previous period
    /// @param _firstTimestamp  the timestamp of the first block in the difficulty period
    /// @param _secondTimestamp the timestamp of the last block in the difficulty period
    /// @return                 the new period's target threshold
    function retargetAlgorithm(
        uint256 _previousTarget,
        uint256 _firstTimestamp,
        uint256 _secondTimestamp
    ) internal pure returns (uint256) {
        uint256 _elapsedTime = _secondTimestamp.sub(_firstTimestamp);

        // Normalize ratio to factor of 4 if very long or very short
        if (_elapsedTime < RETARGET_PERIOD.div(4)) {
            _elapsedTime = RETARGET_PERIOD.div(4);
        }
        if (_elapsedTime > RETARGET_PERIOD.mul(4)) {
            _elapsedTime = RETARGET_PERIOD.mul(4);
        }

        /*
          NB: high targets e.g. ffff0020 can cause overflows here
              so we divide it by 256**2, then multiply by 256**2 later
              we know the target is evenly divisible by 256**2, so this isn't an issue
        */

        uint256 _adjusted = _previousTarget.div(65536).mul(_elapsedTime);
        return _adjusted.div(RETARGET_PERIOD).mul(65536);
    }
}

pragma solidity ^0.8.4;

/*

https://github.com/GNSPS/solidity-bytes-utils/

This is free and unencumbered software released into the public domain.

Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.

In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

For more information, please refer to <https://unlicense.org>
*/


/** @title BytesLib **/
/** @author https://github.com/GNSPS **/

library BytesLib {
    function concat(bytes memory _preBytes, bytes memory _postBytes) internal pure returns (bytes memory) {
        bytes memory tempBytes;

        assembly {
            // Get a location of some free memory and store it in tempBytes as
            // Solidity does for memory variables.
            tempBytes := mload(0x40)

            // Store the length of the first bytes array at the beginning of
            // the memory for tempBytes.
            let length := mload(_preBytes)
            mstore(tempBytes, length)

            // Maintain a memory counter for the current write location in the
            // temp bytes array by adding the 32 bytes for the array length to
            // the starting location.
            let mc := add(tempBytes, 0x20)
            // Stop copying when the memory counter reaches the length of the
            // first bytes array.
            let end := add(mc, length)

            for {
                // Initialize a copy counter to the start of the _preBytes data,
                // 32 bytes into its memory.
                let cc := add(_preBytes, 0x20)
            } lt(mc, end) {
                // Increase both counters by 32 bytes each iteration.
                mc := add(mc, 0x20)
                cc := add(cc, 0x20)
            } {
                // Write the _preBytes data into the tempBytes memory 32 bytes
                // at a time.
                mstore(mc, mload(cc))
            }

            // Add the length of _postBytes to the current length of tempBytes
            // and store it as the new length in the first 32 bytes of the
            // tempBytes memory.
            length := mload(_postBytes)
            mstore(tempBytes, add(length, mload(tempBytes)))

            // Move the memory counter back from a multiple of 0x20 to the
            // actual end of the _preBytes data.
            mc := end
            // Stop copying when the memory counter reaches the new combined
            // length of the arrays.
            end := add(mc, length)

            for {
                let cc := add(_postBytes, 0x20)
            } lt(mc, end) {
                mc := add(mc, 0x20)
                cc := add(cc, 0x20)
            } {
                mstore(mc, mload(cc))
            }

            // Update the free-memory pointer by padding our last write location
            // to 32 bytes: add 31 bytes to the end of tempBytes to move to the
            // next 32 byte block, then round down to the nearest multiple of
            // 32. If the sum of the length of the two arrays is zero then add
            // one before rounding down to leave a blank 32 bytes (the length block with 0).
            mstore(0x40, and(
                add(add(end, iszero(add(length, mload(_preBytes)))), 31),
                not(31) // Round down to the nearest 32 bytes.
            ))
        }

        return tempBytes;
    }

    function concatStorage(bytes storage _preBytes, bytes memory _postBytes) internal {
        assembly {
            // Read the first 32 bytes of _preBytes storage, which is the length
            // of the array. (We don't need to use the offset into the slot
            // because arrays use the entire slot.)
            let fslot := sload(_preBytes.slot)
            // Arrays of 31 bytes or less have an even value in their slot,
            // while longer arrays have an odd value. The actual length is
            // the slot divided by two for odd values, and the lowest order
            // byte divided by two for even values.
            // If the slot is even, bitwise and the slot with 255 and divide by
            // two to get the length. If the slot is odd, bitwise and the slot
            // with -1 and divide by two.
            let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2)
            let mlength := mload(_postBytes)
            let newlength := add(slength, mlength)
            // slength can contain both the length and contents of the array
            // if length < 32 bytes so let's prepare for that
            // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage
            switch add(lt(slength, 32), lt(newlength, 32))
            case 2 {
                // Since the new array still fits in the slot, we just need to
                // update the contents of the slot.
                // uint256(bytes_storage) = uint256(bytes_storage) + uint256(bytes_memory) + new_length
                sstore(
                    _preBytes.slot,
                    // all the modifications to the slot are inside this
                    // next block
                    add(
                        // we can just add to the slot contents because the
                        // bytes we want to change are the LSBs
                        fslot,
                        add(
                            mul(
                                div(
                                    // load the bytes from memory
                                    mload(add(_postBytes, 0x20)),
                                    // zero all bytes to the right
                                    exp(0x100, sub(32, mlength))
                        ),
                        // and now shift left the number of bytes to
                        // leave space for the length in the slot
                        exp(0x100, sub(32, newlength))
                        ),
                        // increase length by the double of the memory
                        // bytes length
                        mul(mlength, 2)
                        )
                    )
                )
            }
            case 1 {
                // The stored value fits in the slot, but the combined value
                // will exceed it.
                // get the keccak hash to get the contents of the array
                mstore(0x0, _preBytes.slot)
                let sc := add(keccak256(0x0, 0x20), div(slength, 32))

                // save new length
                sstore(_preBytes.slot, add(mul(newlength, 2), 1))

                // The contents of the _postBytes array start 32 bytes into
                // the structure. Our first read should obtain the `submod`
                // bytes that can fit into the unused space in the last word
                // of the stored array. To get this, we read 32 bytes starting
                // from `submod`, so the data we read overlaps with the array
                // contents by `submod` bytes. Masking the lowest-order
                // `submod` bytes allows us to add that value directly to the
                // stored value.

                let submod := sub(32, slength)
                let mc := add(_postBytes, submod)
                let end := add(_postBytes, mlength)
                let mask := sub(exp(0x100, submod), 1)

                sstore(
                    sc,
                    add(
                        and(
                            fslot,
                            0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00
                    ),
                    and(mload(mc), mask)
                    )
                )

                for {
                    mc := add(mc, 0x20)
                    sc := add(sc, 1)
                } lt(mc, end) {
                    sc := add(sc, 1)
                    mc := add(mc, 0x20)
                } {
                    sstore(sc, mload(mc))
                }

                mask := exp(0x100, sub(mc, end))

                sstore(sc, mul(div(mload(mc), mask), mask))
            }
            default {
                // get the keccak hash to get the contents of the array
                mstore(0x0, _preBytes.slot)
                // Start copying to the last used word of the stored array.
                let sc := add(keccak256(0x0, 0x20), div(slength, 32))

                // save new length
                sstore(_preBytes.slot, add(mul(newlength, 2), 1))

                // Copy over the first `submod` bytes of the new data as in
                // case 1 above.
                let slengthmod := mod(slength, 32)
                let mlengthmod := mod(mlength, 32)
                let submod := sub(32, slengthmod)
                let mc := add(_postBytes, submod)
                let end := add(_postBytes, mlength)
                let mask := sub(exp(0x100, submod), 1)

                sstore(sc, add(sload(sc), and(mload(mc), mask)))

                for {
                    sc := add(sc, 1)
                    mc := add(mc, 0x20)
                } lt(mc, end) {
                    sc := add(sc, 1)
                    mc := add(mc, 0x20)
                } {
                    sstore(sc, mload(mc))
                }

                mask := exp(0x100, sub(mc, end))

                sstore(sc, mul(div(mload(mc), mask), mask))
            }
        }
    }

    function slice(bytes memory _bytes, uint _start, uint _length) internal  pure returns (bytes memory res) {
        if (_length == 0) {
            return hex"";
        }
        uint _end = _start + _length;
        require(_end > _start && _bytes.length >= _end, "Slice out of bounds");

        assembly {
            // Alloc bytes array with additional 32 bytes afterspace and assign it's size
            res := mload(0x40)
            mstore(0x40, add(add(res, 64), _length))
            mstore(res, _length)

            // Compute distance between source and destination pointers
            let diff := sub(res, add(_bytes, _start))

            for {
                let src := add(add(_bytes, 32), _start)
                let end := add(src, _length)
            } lt(src, end) {
                src := add(src, 32)
            } {
                mstore(add(src, diff), mload(src))
            }
        }
    }

    /// @notice Take a slice of the byte array, overwriting the destination.
    /// The length of the slice will equal the length of the destination array.
    /// @dev Make sure the destination array has afterspace if required.
    /// @param _bytes The source array
    /// @param _dest The destination array.
    /// @param _start The location to start in the source array.
    function sliceInPlace(
        bytes memory _bytes,
        bytes memory _dest,
        uint _start
    ) internal pure {
        uint _length = _dest.length;
        uint _end = _start + _length;
        require(_end > _start && _bytes.length >= _end, "Slice out of bounds");

        assembly {
            for {
                let src := add(add(_bytes, 32), _start)
                let res := add(_dest, 32)
                let end := add(src, _length)
            } lt(src, end) {
                src := add(src, 32)
                res := add(res, 32)
            } {
                mstore(res, mload(src))
            }
        }
    }

    // Static slice functions, no bounds checking
    /// @notice take a 32-byte slice from the specified position
    function slice32(bytes memory _bytes, uint _start) internal pure returns (bytes32 res) {
        assembly {
            res := mload(add(add(_bytes, 32), _start))
        }
    }

    /// @notice take a 20-byte slice from the specified position
    function slice20(bytes memory _bytes, uint _start) internal pure returns (bytes20) {
        return bytes20(slice32(_bytes, _start));
    }

    /// @notice take a 8-byte slice from the specified position
    function slice8(bytes memory _bytes, uint _start) internal pure returns (bytes8) {
        return bytes8(slice32(_bytes, _start));
    }

    /// @notice take a 4-byte slice from the specified position
    function slice4(bytes memory _bytes, uint _start) internal pure returns (bytes4) {
        return bytes4(slice32(_bytes, _start));
    }

    /// @notice take a 3-byte slice from the specified position
    function slice3(bytes memory _bytes, uint _start) internal pure returns (bytes3) {
        return bytes3(slice32(_bytes, _start));
    }

    /// @notice take a 2-byte slice from the specified position
    function slice2(bytes memory _bytes, uint _start) internal pure returns (bytes2) {
        return bytes2(slice32(_bytes, _start));
    }

    function toAddress(bytes memory _bytes, uint _start) internal  pure returns (address) {
        uint _totalLen = _start + 20;
        require(_totalLen > _start && _bytes.length >= _totalLen, "Address conversion out of bounds.");
        address tempAddress;

        assembly {
            tempAddress := div(mload(add(add(_bytes, 0x20), _start)), 0x1000000000000000000000000)
        }

        return tempAddress;
    }

    function toUint(bytes memory _bytes, uint _start) internal  pure returns (uint256) {
        uint _totalLen = _start + 32;
        require(_totalLen > _start && _bytes.length >= _totalLen, "Uint conversion out of bounds.");
        uint256 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0x20), _start))
        }

        return tempUint;
    }

    function equal(bytes memory _preBytes, bytes memory _postBytes) internal pure returns (bool) {
        bool success = true;

        assembly {
            let length := mload(_preBytes)

            // if lengths don't match the arrays are not equal
            switch eq(length, mload(_postBytes))
            case 1 {
                // cb is a circuit breaker in the for loop since there's
                //  no said feature for inline assembly loops
                // cb = 1 - don't breaker
                // cb = 0 - break
                let cb := 1

                let mc := add(_preBytes, 0x20)
                let end := add(mc, length)

                for {
                    let cc := add(_postBytes, 0x20)
                    // the next line is the loop condition:
                    // while(uint(mc < end) + cb == 2)
                } eq(add(lt(mc, end), cb), 2) {
                    mc := add(mc, 0x20)
                    cc := add(cc, 0x20)
                } {
                    // if any of these checks fails then arrays are not equal
                    if iszero(eq(mload(mc), mload(cc))) {
                        // unsuccess:
                        success := 0
                        cb := 0
                    }
                }
            }
            default {
                // unsuccess:
                success := 0
            }
        }

        return success;
    }

    function equalStorage(bytes storage _preBytes, bytes memory _postBytes) internal view returns (bool) {
        bool success = true;

        assembly {
            // we know _preBytes_offset is 0
            let fslot := sload(_preBytes.slot)
            // Decode the length of the stored array like in concatStorage().
            let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2)
            let mlength := mload(_postBytes)

            // if lengths don't match the arrays are not equal
            switch eq(slength, mlength)
            case 1 {
                // slength can contain both the length and contents of the array
                // if length < 32 bytes so let's prepare for that
                // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage
                if iszero(iszero(slength)) {
                    switch lt(slength, 32)
                    case 1 {
                        // blank the last byte which is the length
                        fslot := mul(div(fslot, 0x100), 0x100)

                        if iszero(eq(fslot, mload(add(_postBytes, 0x20)))) {
                            // unsuccess:
                            success := 0
                        }
                    }
                    default {
                        // cb is a circuit breaker in the for loop since there's
                        //  no said feature for inline assembly loops
                        // cb = 1 - don't breaker
                        // cb = 0 - break
                        let cb := 1

                        // get the keccak hash to get the contents of the array
                        mstore(0x0, _preBytes.slot)
                        let sc := keccak256(0x0, 0x20)

                        let mc := add(_postBytes, 0x20)
                        let end := add(mc, mlength)

                        // the next line is the loop condition:
                        // while(uint(mc < end) + cb == 2)
                        for {} eq(add(lt(mc, end), cb), 2) {
                            sc := add(sc, 1)
                            mc := add(mc, 0x20)
                        } {
                            if iszero(eq(sload(sc), mload(mc))) {
                                // unsuccess:
                                success := 0
                                cb := 0
                            }
                        }
                    }
                }
            }
            default {
                // unsuccess:
                success := 0
            }
        }

        return success;
    }

    function toBytes32(bytes memory _source) pure internal returns (bytes32 result) {
        if (_source.length == 0) {
            return 0x0;
        }

        assembly {
            result := mload(add(_source, 32))
        }
    }

    function keccak256Slice(bytes memory _bytes, uint _start, uint _length) pure internal returns (bytes32 result) {
        uint _end = _start + _length;
        require(_end > _start && _bytes.length >= _end, "Slice out of bounds");

        assembly {
            result := keccak256(add(add(_bytes, 32), _start), _length)
        }
    }
}

pragma solidity ^0.8.4;

/*
The MIT License (MIT)

Copyright (c) 2016 Smart Contract Solutions, Inc.

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/


/**
 * @title SafeMath
 * @dev Math operations with safety checks that throw on error
 */
library SafeMath {

    /**
     * @dev Multiplies two numbers, throws on overflow.
     */
    function mul(uint256 _a, uint256 _b) internal pure returns (uint256 c) {
        // Gas optimization: this is cheaper than asserting 'a' not being zero, but the
        // benefit is lost if 'b' is also tested.
        // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522
        if (_a == 0) {
            return 0;
        }

        c = _a * _b;
        require(c / _a == _b, "Overflow during multiplication.");
        return c;
    }

    /**
     * @dev Integer division of two numbers, truncating the quotient.
     */
    function div(uint256 _a, uint256 _b) internal pure returns (uint256) {
        // assert(_b > 0); // Solidity automatically throws when dividing by 0
        // uint256 c = _a / _b;
        // assert(_a == _b * c + _a % _b); // There is no case in which this doesn't hold
        return _a / _b;
    }

    /**
     * @dev Subtracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend).
     */
    function sub(uint256 _a, uint256 _b) internal pure returns (uint256) {
        require(_b <= _a, "Underflow during subtraction.");
        return _a - _b;
    }

    /**
     * @dev Adds two numbers, throws on overflow.
     */
    function add(uint256 _a, uint256 _b) internal pure returns (uint256 c) {
        c = _a + _b;
        require(c >= _a, "Overflow during addition.");
        return c;
    }
}

// SPDX-License-Identifier: GPL-3.0-only

// ██████████████     ▐████▌     ██████████████
// ██████████████     ▐████▌     ██████████████
//               ▐████▌    ▐████▌
//               ▐████▌    ▐████▌
// ██████████████     ▐████▌     ██████████████
// ██████████████     ▐████▌     ██████████████
//               ▐████▌    ▐████▌
//               ▐████▌    ▐████▌
//               ▐████▌    ▐████▌
//               ▐████▌    ▐████▌
//               ▐████▌    ▐████▌
//               ▐████▌    ▐████▌

pragma solidity ^0.8.0;

import {BTCUtils} from "@keep-network/bitcoin-spv-sol/contracts/BTCUtils.sol";

import "./IBridge.sol";
import "./ITBTCVault.sol";

/// @title Abstract AbstractTBTCDepositor contract.
/// @notice This abstract contract is meant to facilitate integration of protocols
///         aiming to use tBTC as an underlying Bitcoin bridge.
///
///         Such an integrator is supposed to:
///         - Create a child contract inheriting from this abstract contract
///         - Call the `__AbstractTBTCDepositor_initialize` initializer function
///         - Use the `_initializeDeposit` and `_finalizeDeposit` as part of their
///           business logic in order to initialize and finalize deposits.
///
/// @dev Example usage:
///      ```
///      // Example upgradeable integrator contract.
///      contract ExampleTBTCIntegrator is AbstractTBTCDepositor, Initializable {
///          /// @custom:oz-upgrades-unsafe-allow constructor
///          constructor() {
///              // Prevents the contract from being initialized again.
///              _disableInitializers();
///          }
///
///          function initialize(
///              address _bridge,
///              address _tbtcVault
///          ) external initializer {
///              __AbstractTBTCDepositor_initialize(_bridge, _tbtcVault);
///          }
///
///          function startProcess(
///              IBridgeTypes.BitcoinTxInfo calldata fundingTx,
///              IBridgeTypes.DepositRevealInfo calldata reveal
///          ) external {
///              // Embed necessary context as extra data.
///              bytes32 extraData = ...;
///
///              (uint256 depositKey, uint256 initialDepositAmount) = _initializeDeposit(
///                  fundingTx,
///                  reveal,
///                  extraData
///              );
///
///              // Use the depositKey to track the process.
///          }
///
///          function finalizeProcess(uint256 depositKey) external {
///              // Ensure the function cannot be called for the same deposit
///              // twice.
///
///              (
///                  uint256 initialDepositAmount,
///                  uint256 tbtcAmount,
///                  bytes32 extraData
///              ) = _finalizeDeposit(depositKey);
///
///              // Do something with the minted TBTC using context
///              // embedded in the extraData.
///          }
///      }
abstract contract AbstractTBTCDepositor {
    using BTCUtils for bytes;

    /// @notice Multiplier to convert satoshi to TBTC token units.
    uint256 public constant SATOSHI_MULTIPLIER = 10**10;

    /// @notice Bridge contract address.
    IBridge public bridge;
    /// @notice TBTCVault contract address.
    ITBTCVault public tbtcVault;

    // Reserved storage space that allows adding more variables without affecting
    // the storage layout of the child contracts. The convention from OpenZeppelin
    // suggests the storage space should add up to 50 slots. If more variables are
    // added in the upcoming versions one need to reduce the array size accordingly.
    // See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
    // slither-disable-next-line unused-state
    uint256[47] private __gap;

    /// @notice Initializes the contract. MUST BE CALLED from the child
    ///         contract initializer.
    // slither-disable-next-line dead-code
    function __AbstractTBTCDepositor_initialize(
        address _bridge,
        address _tbtcVault
    ) internal {
        require(
            address(bridge) == address(0) && address(tbtcVault) == address(0),
            "AbstractTBTCDepositor already initialized"
        );

        require(_bridge != address(0), "Bridge address cannot be zero");
        require(_tbtcVault != address(0), "TBTCVault address cannot be zero");

        bridge = IBridge(_bridge);
        tbtcVault = ITBTCVault(_tbtcVault);
    }

    /// @notice Initializes a deposit by revealing it to the Bridge.
    /// @param fundingTx Bitcoin funding transaction data, see `IBridgeTypes.BitcoinTxInfo`.
    /// @param reveal Deposit reveal data, see `IBridgeTypes.DepositRevealInfo` struct.
    /// @param extraData 32-byte deposit extra data.
    /// @return depositKey Deposit key computed as
    ///         `keccak256(fundingTxHash | reveal.fundingOutputIndex)`. This
    ///         key can be used to refer to the deposit in the Bridge and
    ///         TBTCVault contracts.
    /// @return initialDepositAmount Amount of funding transaction deposit. In
    ///         TBTC token decimals precision.
    /// @dev Requirements:
    ///      - The revealed vault address must match the TBTCVault address,
    ///      - All requirements from {Bridge#revealDepositWithExtraData}
    ///        function must be met.
    /// @dev This function doesn't validate if a deposit has been initialized before,
    ///      as the Bridge won't allow the same deposit to be revealed twice.
    // slither-disable-next-line dead-code
    function _initializeDeposit(
        IBridgeTypes.BitcoinTxInfo calldata fundingTx,
        IBridgeTypes.DepositRevealInfo calldata reveal,
        bytes32 extraData
    ) internal returns (uint256 depositKey, uint256 initialDepositAmount) {
        require(reveal.vault == address(tbtcVault), "Vault address mismatch");

        depositKey = _calculateDepositKey(
            _calculateBitcoinTxHash(fundingTx),
            reveal.fundingOutputIndex
        );

        // The Bridge does not allow to reveal the same deposit twice and
        // revealed deposits stay there forever. The transaction will revert
        // if the deposit has already been revealed so, there is no need to do
        // an explicit check here.
        bridge.revealDepositWithExtraData(fundingTx, reveal, extraData);

        initialDepositAmount =
            bridge.deposits(depositKey).amount *
            SATOSHI_MULTIPLIER;
    }

    /// @notice Finalizes a deposit by calculating the amount of TBTC minted
    ///         for the deposit.
    /// @param depositKey Deposit key identifying the deposit.
    /// @return initialDepositAmount Amount of funding transaction deposit. In
    ///         TBTC token decimals precision.
    /// @return tbtcAmount Approximate amount of TBTC minted for the deposit. In
    ///         TBTC token decimals precision.
    /// @return extraData 32-byte deposit extra data.
    /// @dev Requirements:
    ///      - The deposit must be initialized but not finalized
    ///        (in the context of this contract) yet.
    ///      - The deposit must be finalized on the Bridge side. That means the
    ///        deposit must be either swept or optimistically minted.
    /// @dev THIS FUNCTION DOESN'T VALIDATE IF A DEPOSIT HAS BEEN FINALIZED BEFORE,
    ///      IT IS A RESPONSIBILITY OF THE IMPLEMENTING CONTRACT TO ENSURE THIS
    ///      FUNCTION WON'T BE CALLED TWICE FOR THE SAME DEPOSIT.
    /// @dev IMPORTANT NOTE: The tbtcAmount returned by this function is an
    ///      approximation. See documentation of the `calculateTbtcAmount`
    ///      responsible for calculating this value for more details.
    // slither-disable-next-line dead-code
    function _finalizeDeposit(uint256 depositKey)
        internal
        returns (
            uint256 initialDepositAmount,
            uint256 tbtcAmount,
            bytes32 extraData
        )
    {
        IBridgeTypes.DepositRequest memory deposit = bridge.deposits(
            depositKey
        );
        require(deposit.revealedAt != 0, "Deposit not initialized");

        (, uint64 finalizedAt) = tbtcVault.optimisticMintingRequests(
            depositKey
        );

        require(
            deposit.sweptAt != 0 || finalizedAt != 0,
            "Deposit not finalized by the bridge"
        );

        initialDepositAmount = deposit.amount * SATOSHI_MULTIPLIER;

        tbtcAmount = _calculateTbtcAmount(deposit.amount, deposit.treasuryFee);

        extraData = deposit.extraData;
    }

    /// @notice Calculates the amount of TBTC minted for the deposit.
    /// @param depositAmountSat Deposit amount in satoshi (1e8 precision).
    ///        This is the actual amount deposited by the deposit creator, i.e.
    ///        the gross amount the Bridge's fees are cut from.
    /// @param depositTreasuryFeeSat Deposit treasury fee in satoshi (1e8 precision).
    ///        This is an accurate value of the treasury fee that was actually
    ///        cut upon minting.
    /// @return tbtcAmount Approximate amount of TBTC minted for the deposit.
    /// @dev IMPORTANT NOTE: The tbtcAmount returned by this function may
    ///      not correspond to the actual amount of TBTC minted for the deposit.
    ///      Although the treasury fee cut upon minting is known precisely,
    ///      this is not the case for the optimistic minting fee and the Bitcoin
    ///      transaction fee. To overcome that problem, this function just takes
    ///      the current maximum allowed values of both fees, at the moment of deposit
    ///      finalization. For the great majority of the deposits, such an
    ///      algorithm will return a tbtcAmount slightly lesser than the
    ///      actual amount of TBTC minted for the deposit. This will cause
    ///      some TBTC to be left in the contract and ensure there is enough
    ///      liquidity to finalize the deposit. However, in some rare cases,
    ///      where the actual values of those fees change between the deposit
    ///      minting and finalization, the tbtcAmount returned by this function
    ///      may be greater than the actual amount of TBTC minted for the deposit.
    ///      If this happens and the reserve coming from previous deposits
    ///      leftovers does not provide enough liquidity, the deposit will have
    ///      to wait for finalization until the reserve is refilled by subsequent
    ///      deposits or a manual top-up. The integrator is responsible for
    ///      handling such cases.
    // slither-disable-next-line dead-code
    function _calculateTbtcAmount(
        uint64 depositAmountSat,
        uint64 depositTreasuryFeeSat
    ) internal view virtual returns (uint256) {
        // Both deposit amount and treasury fee are in the 1e8 satoshi precision.
        // We need to convert them to the 1e18 TBTC precision.
        uint256 amountSubTreasury = (depositAmountSat - depositTreasuryFeeSat) *
            SATOSHI_MULTIPLIER;

        uint256 omFeeDivisor = tbtcVault.optimisticMintingFeeDivisor();
        uint256 omFee = omFeeDivisor > 0
            ? (amountSubTreasury / omFeeDivisor)
            : 0;

        // The deposit transaction max fee is in the 1e8 satoshi precision.
        // We need to convert them to the 1e18 TBTC precision.
        (, , uint64 depositTxMaxFee, ) = bridge.depositParameters();
        uint256 txMaxFee = depositTxMaxFee * SATOSHI_MULTIPLIER;

        return amountSubTreasury - omFee - txMaxFee;
    }

    /// @notice Calculates the deposit key for the given funding transaction
    ///         hash and funding output index.
    /// @param fundingTxHash Funding transaction hash.
    /// @param fundingOutputIndex Funding output index.
    /// @return depositKey Deposit key computed as
    ///         `keccak256(fundingTxHash | reveal.fundingOutputIndex)`. This
    ///         key can be used to refer to the deposit in the Bridge and
    ///         TBTCVault contracts.
    // slither-disable-next-line dead-code
    function _calculateDepositKey(
        bytes32 fundingTxHash,
        uint32 fundingOutputIndex
    ) internal pure returns (uint256) {
        return
            uint256(
                keccak256(abi.encodePacked(fundingTxHash, fundingOutputIndex))
            );
    }

    /// @notice Calculates the Bitcoin transaction hash for the given Bitcoin
    ///         transaction data.
    /// @param txInfo Bitcoin transaction data, see `IBridgeTypes.BitcoinTxInfo` struct.
    /// @return txHash Bitcoin transaction hash.
    // slither-disable-next-line dead-code
    function _calculateBitcoinTxHash(IBridgeTypes.BitcoinTxInfo calldata txInfo)
        internal
        view
        returns (bytes32)
    {
        return
            abi
                .encodePacked(
                    txInfo.version,
                    txInfo.inputVector,
                    txInfo.outputVector,
                    txInfo.locktime
                )
                .hash256View();
    }

    /// @notice Returns minimum deposit amount.
    /// @return Minimum deposit amount. In TBTC token decimals precision.
    // slither-disable-next-line dead-code
    function _minDepositAmount() internal view returns (uint256) {
        // Read tBTC Bridge Deposit Dust Threshold in satoshi precision.
        (uint64 bridgeDepositDustThresholdSat, , , ) = bridge
            .depositParameters();

        // Convert tBTC Bridge Deposit Dust Threshold to TBTC token precision.
        return bridgeDepositDustThresholdSat * SATOSHI_MULTIPLIER;
    }
}

// SPDX-License-Identifier: GPL-3.0-only

// ██████████████     ▐████▌     ██████████████
// ██████████████     ▐████▌     ██████████████
//               ▐████▌    ▐████▌
//               ▐████▌    ▐████▌
// ██████████████     ▐████▌     ██████████████
// ██████████████     ▐████▌     ██████████████
//               ▐████▌    ▐████▌
//               ▐████▌    ▐████▌
//               ▐████▌    ▐████▌
//               ▐████▌    ▐████▌
//               ▐████▌    ▐████▌
//               ▐████▌    ▐████▌

pragma solidity ^0.8.0;

/// @notice Namespace which groups all types relevant to the IBridge interface.
/// @dev This is a mirror of the real types used in the Bridge contract.
///      This way, the `integrator` subpackage does not need to import
///      anything from the `bridge` subpackage and explicitly depend on it.
///      This simplifies the dependency graph for integrators.
library IBridgeTypes {
    /// @dev See bridge/BitcoinTx.sol#Info
    struct BitcoinTxInfo {
        bytes4 version;
        bytes inputVector;
        bytes outputVector;
        bytes4 locktime;
    }

    /// @dev See bridge/Deposit.sol#DepositRevealInfo
    struct DepositRevealInfo {
        uint32 fundingOutputIndex;
        bytes8 blindingFactor;
        bytes20 walletPubKeyHash;
        bytes20 refundPubKeyHash;
        bytes4 refundLocktime;
        address vault;
    }

    /// @dev See bridge/Deposit.sol#DepositRequest
    struct DepositRequest {
        address depositor;
        uint64 amount;
        uint32 revealedAt;
        address vault;
        uint64 treasuryFee;
        uint32 sweptAt;
        bytes32 extraData;
    }
}

/// @notice Interface of the Bridge contract.
/// @dev See bridge/Bridge.sol
interface IBridge {
    /// @dev See {Bridge#revealDepositWithExtraData}
    function revealDepositWithExtraData(
        IBridgeTypes.BitcoinTxInfo calldata fundingTx,
        IBridgeTypes.DepositRevealInfo calldata reveal,
        bytes32 extraData
    ) external;

    /// @dev See {Bridge#deposits}
    function deposits(uint256 depositKey)
        external
        view
        returns (IBridgeTypes.DepositRequest memory);

    /// @dev See {Bridge#depositParameters}
    function depositParameters()
        external
        view
        returns (
            uint64 depositDustThreshold,
            uint64 depositTreasuryFeeDivisor,
            uint64 depositTxMaxFee,
            uint32 depositRevealAheadPeriod
        );
}

// SPDX-License-Identifier: GPL-3.0-only

// ██████████████     ▐████▌     ██████████████
// ██████████████     ▐████▌     ██████████████
//               ▐████▌    ▐████▌
//               ▐████▌    ▐████▌
// ██████████████     ▐████▌     ██████████████
// ██████████████     ▐████▌     ██████████████
//               ▐████▌    ▐████▌
//               ▐████▌    ▐████▌
//               ▐████▌    ▐████▌
//               ▐████▌    ▐████▌
//               ▐████▌    ▐████▌
//               ▐████▌    ▐████▌

pragma solidity ^0.8.0;

/// @notice Interface of the TBTCVault contract.
/// @dev See vault/TBTCVault.sol
interface ITBTCVault {
    /// @dev See {TBTCVault#optimisticMintingRequests}
    function optimisticMintingRequests(uint256 depositKey)
        external
        returns (uint64 requestedAt, uint64 finalizedAt);

    /// @dev See {TBTCVault#optimisticMintingFeeDivisor}
    function optimisticMintingFeeDivisor() external view returns (uint32);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable2Step.sol)

pragma solidity ^0.8.20;

import {OwnableUpgradeable} from "./OwnableUpgradeable.sol";
import {Initializable} from "../proxy/utils/Initializable.sol";

/**
 * @dev Contract module which provides access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * The initial owner is specified at deployment time in the constructor for `Ownable`. This
 * can later be changed with {transferOwnership} and {acceptOwnership}.
 *
 * This module is used through inheritance. It will make available all functions
 * from parent (Ownable).
 */
abstract contract Ownable2StepUpgradeable is Initializable, OwnableUpgradeable {
    /// @custom:storage-location erc7201:openzeppelin.storage.Ownable2Step
    struct Ownable2StepStorage {
        address _pendingOwner;
    }

    // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Ownable2Step")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant Ownable2StepStorageLocation = 0x237e158222e3e6968b72b9db0d8043aacf074ad9f650f0d1606b4d82ee432c00;

    function _getOwnable2StepStorage() private pure returns (Ownable2StepStorage storage $) {
        assembly {
            $.slot := Ownable2StepStorageLocation
        }
    }

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

    function __Ownable2Step_init() internal onlyInitializing {
    }

    function __Ownable2Step_init_unchained() internal onlyInitializing {
    }
    /**
     * @dev Returns the address of the pending owner.
     */
    function pendingOwner() public view virtual returns (address) {
        Ownable2StepStorage storage $ = _getOwnable2StepStorage();
        return $._pendingOwner;
    }

    /**
     * @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual override onlyOwner {
        Ownable2StepStorage storage $ = _getOwnable2StepStorage();
        $._pendingOwner = newOwner;
        emit OwnershipTransferStarted(owner(), newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual override {
        Ownable2StepStorage storage $ = _getOwnable2StepStorage();
        delete $._pendingOwner;
        super._transferOwnership(newOwner);
    }

    /**
     * @dev The new owner accepts the ownership transfer.
     */
    function acceptOwnership() public virtual {
        address sender = _msgSender();
        if (pendingOwner() != sender) {
            revert OwnableUnauthorizedAccount(sender);
        }
        _transferOwnership(sender);
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)

pragma solidity ^0.8.20;

import {ContextUpgradeable} from "../utils/ContextUpgradeable.sol";
import {Initializable} from "../proxy/utils/Initializable.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.
 *
 * The initial owner is set to the address provided by the deployer. 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 OwnableUpgradeable is Initializable, ContextUpgradeable {
    /// @custom:storage-location erc7201:openzeppelin.storage.Ownable
    struct OwnableStorage {
        address _owner;
    }

    // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Ownable")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant OwnableStorageLocation = 0x9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300;

    function _getOwnableStorage() private pure returns (OwnableStorage storage $) {
        assembly {
            $.slot := OwnableStorageLocation
        }
    }

    /**
     * @dev The caller account is not authorized to perform an operation.
     */
    error OwnableUnauthorizedAccount(address account);

    /**
     * @dev The owner is not a valid owner account. (eg. `address(0)`)
     */
    error OwnableInvalidOwner(address owner);

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

    /**
     * @dev Initializes the contract setting the address provided by the deployer as the initial owner.
     */
    function __Ownable_init(address initialOwner) internal onlyInitializing {
        __Ownable_init_unchained(initialOwner);
    }

    function __Ownable_init_unchained(address initialOwner) internal onlyInitializing {
        if (initialOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(initialOwner);
    }

    /**
     * @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) {
        OwnableStorage storage $ = _getOwnableStorage();
        return $._owner;
    }

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        if (owner() != _msgSender()) {
            revert OwnableUnauthorizedAccount(_msgSender());
        }
    }

    /**
     * @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 {
        if (newOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(newOwner);
    }

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

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/Initializable.sol)

pragma solidity ^0.8.20;

/**
 * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
 * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
 * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
 * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
 *
 * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
 * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
 * case an upgrade adds a module that needs to be initialized.
 *
 * For example:
 *
 * [.hljs-theme-light.nopadding]
 * ```solidity
 * contract MyToken is ERC20Upgradeable {
 *     function initialize() initializer public {
 *         __ERC20_init("MyToken", "MTK");
 *     }
 * }
 *
 * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
 *     function initializeV2() reinitializer(2) public {
 *         __ERC20Permit_init("MyToken");
 *     }
 * }
 * ```
 *
 * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
 * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
 *
 * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
 * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
 *
 * [CAUTION]
 * ====
 * Avoid leaving a contract uninitialized.
 *
 * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
 * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
 * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
 *
 * [.hljs-theme-light.nopadding]
 * ```
 * /// @custom:oz-upgrades-unsafe-allow constructor
 * constructor() {
 *     _disableInitializers();
 * }
 * ```
 * ====
 */
abstract contract Initializable {
    /**
     * @dev Storage of the initializable contract.
     *
     * It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions
     * when using with upgradeable contracts.
     *
     * @custom:storage-location erc7201:openzeppelin.storage.Initializable
     */
    struct InitializableStorage {
        /**
         * @dev Indicates that the contract has been initialized.
         */
        uint64 _initialized;
        /**
         * @dev Indicates that the contract is in the process of being initialized.
         */
        bool _initializing;
    }

    // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00;

    /**
     * @dev The contract is already initialized.
     */
    error InvalidInitialization();

    /**
     * @dev The contract is not initializing.
     */
    error NotInitializing();

    /**
     * @dev Triggered when the contract has been initialized or reinitialized.
     */
    event Initialized(uint64 version);

    /**
     * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
     * `onlyInitializing` functions can be used to initialize parent contracts.
     *
     * Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any
     * number of times. This behavior in the constructor can be useful during testing and is not expected to be used in
     * production.
     *
     * Emits an {Initialized} event.
     */
    modifier initializer() {
        // solhint-disable-next-line var-name-mixedcase
        InitializableStorage storage $ = _getInitializableStorage();

        // Cache values to avoid duplicated sloads
        bool isTopLevelCall = !$._initializing;
        uint64 initialized = $._initialized;

        // Allowed calls:
        // - initialSetup: the contract is not in the initializing state and no previous version was
        //                 initialized
        // - construction: the contract is initialized at version 1 (no reininitialization) and the
        //                 current contract is just being deployed
        bool initialSetup = initialized == 0 && isTopLevelCall;
        bool construction = initialized == 1 && address(this).code.length == 0;

        if (!initialSetup && !construction) {
            revert InvalidInitialization();
        }
        $._initialized = 1;
        if (isTopLevelCall) {
            $._initializing = true;
        }
        _;
        if (isTopLevelCall) {
            $._initializing = false;
            emit Initialized(1);
        }
    }

    /**
     * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
     * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
     * used to initialize parent contracts.
     *
     * A reinitializer may be used after the original initialization step. This is essential to configure modules that
     * are added through upgrades and that require initialization.
     *
     * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
     * cannot be nested. If one is invoked in the context of another, execution will revert.
     *
     * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
     * a contract, executing them in the right order is up to the developer or operator.
     *
     * WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization.
     *
     * Emits an {Initialized} event.
     */
    modifier reinitializer(uint64 version) {
        // solhint-disable-next-line var-name-mixedcase
        InitializableStorage storage $ = _getInitializableStorage();

        if ($._initializing || $._initialized >= version) {
            revert InvalidInitialization();
        }
        $._initialized = version;
        $._initializing = true;
        _;
        $._initializing = false;
        emit Initialized(version);
    }

    /**
     * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
     * {initializer} and {reinitializer} modifiers, directly or indirectly.
     */
    modifier onlyInitializing() {
        _checkInitializing();
        _;
    }

    /**
     * @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}.
     */
    function _checkInitializing() internal view virtual {
        if (!_isInitializing()) {
            revert NotInitializing();
        }
    }

    /**
     * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
     * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
     * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
     * through proxies.
     *
     * Emits an {Initialized} event the first time it is successfully executed.
     */
    function _disableInitializers() internal virtual {
        // solhint-disable-next-line var-name-mixedcase
        InitializableStorage storage $ = _getInitializableStorage();

        if ($._initializing) {
            revert InvalidInitialization();
        }
        if ($._initialized != type(uint64).max) {
            $._initialized = type(uint64).max;
            emit Initialized(type(uint64).max);
        }
    }

    /**
     * @dev Returns the highest version that has been initialized. See {reinitializer}.
     */
    function _getInitializedVersion() internal view returns (uint64) {
        return _getInitializableStorage()._initialized;
    }

    /**
     * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
     */
    function _isInitializing() internal view returns (bool) {
        return _getInitializableStorage()._initializing;
    }

    /**
     * @dev Returns a pointer to the storage namespace.
     */
    // solhint-disable-next-line var-name-mixedcase
    function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
        assembly {
            $.slot := INITIALIZABLE_STORAGE
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)

pragma solidity ^0.8.20;
import {Initializable} from "../proxy/utils/Initializable.sol";

/**
 * @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 ContextUpgradeable is Initializable {
    function __Context_init() internal onlyInitializing {
    }

    function __Context_init_unchained() internal onlyInitializing {
    }
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

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

    function _contextSuffixLength() internal view virtual returns (uint256) {
        return 0;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/ReentrancyGuard.sol)

pragma solidity ^0.8.20;
import {Initializable} from "../proxy/utils/Initializable.sol";

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

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

    /// @custom:storage-location erc7201:openzeppelin.storage.ReentrancyGuard
    struct ReentrancyGuardStorage {
        uint256 _status;
    }

    // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ReentrancyGuard")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant ReentrancyGuardStorageLocation = 0x9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00;

    function _getReentrancyGuardStorage() private pure returns (ReentrancyGuardStorage storage $) {
        assembly {
            $.slot := ReentrancyGuardStorageLocation
        }
    }

    /**
     * @dev Unauthorized reentrant call.
     */
    error ReentrancyGuardReentrantCall();

    function __ReentrancyGuard_init() internal onlyInitializing {
        __ReentrancyGuard_init_unchained();
    }

    function __ReentrancyGuard_init_unchained() internal onlyInitializing {
        ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
        $._status = NOT_ENTERED;
    }

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

    function _nonReentrantBefore() private {
        ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
        // On the first call to nonReentrant, _status will be NOT_ENTERED
        if ($._status == ENTERED) {
            revert ReentrancyGuardReentrantCall();
        }

        // Any calls to nonReentrant after this point will fail
        $._status = ENTERED;
    }

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

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

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 *
 * ==== Security Considerations
 *
 * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
 * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
 * considered as an intention to spend the allowance in any specific way. The second is that because permits have
 * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
 * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
 * generally recommended is:
 *
 * ```solidity
 * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
 *     try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
 *     doThing(..., value);
 * }
 *
 * function doThing(..., uint256 value) public {
 *     token.safeTransferFrom(msg.sender, address(this), value);
 *     ...
 * }
 * ```
 *
 * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
 * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
 * {SafeERC20-safeTransferFrom}).
 *
 * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
 * contracts should have entry points that don't rely on permit.
 */
interface IERC20Permit {
    /**
     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
     * given ``owner``'s signed approval.
     *
     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
     * ordering also apply here.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
     * over the EIP712-formatted function arguments.
     * - the signature must use ``owner``'s current nonce (see {nonces}).
     *
     * For more information on the signature format, see the
     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
     * section].
     *
     * CAUTION: See Security Considerations above.
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /**
     * @dev Returns the current nonce for `owner`. This value must be
     * included whenever a signature is generated for {permit}.
     *
     * Every successful call to {permit} increases ``owner``'s nonce by one. This
     * prevents a signature from being used multiple times.
     */
    function nonces(address owner) external view returns (uint256);

    /**
     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
     */
    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view returns (bytes32);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the value of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the value of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves a `value` amount of tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 value) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
     * caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the
     * allowance mechanism. `value` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 value) external returns (bool);
}

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

pragma solidity ^0.8.20;

import {IERC20} from "../IERC20.sol";
import {IERC20Permit} from "../extensions/IERC20Permit.sol";
import {Address} from "../../../utils/Address.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    using Address for address;

    /**
     * @dev An operation with an ERC20 token failed.
     */
    error SafeERC20FailedOperation(address token);

    /**
     * @dev Indicates a failed `decreaseAllowance` request.
     */
    error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);

    /**
     * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
    }

    /**
     * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
     * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
     */
    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
    }

    /**
     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 oldAllowance = token.allowance(address(this), spender);
        forceApprove(token, spender, oldAllowance + value);
    }

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
     * value, non-reverting calls are assumed to be successful.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
        unchecked {
            uint256 currentAllowance = token.allowance(address(this), spender);
            if (currentAllowance < requestedDecrease) {
                revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
            }
            forceApprove(token, spender, currentAllowance - requestedDecrease);
        }
    }

    /**
     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
     * to be set to zero before setting it to a non-zero value, such as USDT.
     */
    function forceApprove(IERC20 token, address spender, uint256 value) internal {
        bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));

        if (!_callOptionalReturnBool(token, approvalCall)) {
            _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
            _callOptionalReturn(token, approvalCall);
        }
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.

        bytes memory returndata = address(token).functionCall(data);
        if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
        // and not revert is the subcall reverts.

        (bool success, bytes memory returndata) = address(token).call(data);
        return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)

pragma solidity ^0.8.20;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev The ETH balance of the account is not enough to perform the operation.
     */
    error AddressInsufficientBalance(address account);

    /**
     * @dev There's no code at `target` (it is not a contract).
     */
    error AddressEmptyCode(address target);

    /**
     * @dev A call to an address target failed. The target may have reverted.
     */
    error FailedInnerCall();

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

        (bool success, ) = recipient.call{value: amount}("");
        if (!success) {
            revert FailedInnerCall();
        }
    }

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

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        if (address(this).balance < value) {
            revert AddressInsufficientBalance(address(this));
        }
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

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

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

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
     * was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
     * unsuccessful call.
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata
    ) internal view returns (bytes memory) {
        if (!success) {
            _revert(returndata);
        } else {
            // only check if target is a contract if the call was successful and the return data is empty
            // otherwise we already know that it was a contract
            if (returndata.length == 0 && target.code.length == 0) {
                revert AddressEmptyCode(target);
            }
            return returndata;
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
     * revert reason or with a default {FailedInnerCall} error.
     */
    function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
        if (!success) {
            _revert(returndata);
        } else {
            return returndata;
        }
    }

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

// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.24;

import "@keep-network/tbtc-v2/contracts/integrator/AbstractTBTCDepositor.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol";
import "./interfaces/IERC20WithPermit.sol";

/// @notice BitcoinBridge abstract contract exposes the capability of bridging
///         Bitcoin (using tBTC) from Ethereum to the Mezo chain.
///         The process can be achieved in one of two ways:
///         - Depositing: which is used when the user does not yet have tBTC.
///           In this case the user needs to create a Bitcoin transaction with
///           a specific format and use `initializeBTCBridging` and
///           `finalizeBTCBridging` functions,
///         - Bridging: which is used when the user already has tBTC on Ethereum
///           and would like to bridge it to Mezo. In this case either
///          `bridgeTBTC` or `bridgeTBTCWithPermit` must be used.
/// @dev The contract is supposed to be extended by the MezoBridge contract.
abstract contract BitcoinBridge is
    AbstractTBTCDepositor,
    Ownable2StepUpgradeable,
    ReentrancyGuardUpgradeable
{
    using SafeERC20 for IERC20;

    /// @notice Reflects the BTC deposit state:
    ///         - Unknown deposit has not been initialized yet.
    ///         - Initialized deposit has been initialized with a call to
    ///           `initializeBTCBridging` function and is known to this contract.
    ///         - Finalized deposit led to tBTC ERC20 minting and was finalized
    ///           with a call to `finalizeBTCBridging` function. Deposit
    ///           finalizing leads to tBTC being bridged to the Mezo chain.
    enum BTCDepositState {
        Unknown,
        Initialized,
        Finalized
    }

    /// @notice Reference to the tBTC ERC20 token contract.
    address public tbtcToken;

    /// @notice Holds the BTC deposit state, keyed by the deposit key calculated for
    ///         the individual deposit during the call to `initializeBTCBridging`
    ///         function.
    mapping(uint256 => BTCDepositState) public btcDeposits;

    /// @notice Minimum amount of tBTC that can be bridged using `bridgeTBTC`
    ///         and `bridgeTBTCWithPermit`. It helps to prevent DoS attacks.
    ///         Its value is in tBTC precision. Note that `initializeBTCBridging`
    ///         does not enforce this limit, as the tBTC bridge already has its
    ///         own minimum deposit requirement, which provides adequate
    ///         protection against DoS attacks.
    uint256 public minTBTCAmount;

    // Reserved storage space that allows adding more variables without affecting
    // the storage layout of the child contracts. The convention from OpenZeppelin
    // suggests the storage space should add up to 50 slots. If more variables are
    // added in the upcoming versions one need to reduce the array size accordingly.
    // See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
    // slither-disable-next-line unused-state
    uint256[47] private __gap;

    event BTCDepositInitialized(
        uint256 indexed btcDepositKey,
        address indexed recipient
    );

    event BTCDepositFinalized(
        uint256 indexed btcDepositKey,
        uint256 initialAmount,
        uint256 tbtcAmount
    );

    event MinTBTCAmountUpdated(uint256 minTBTCAmount);

    error MinTBTCAmountIsZero();

    error BTCRecipientIsZeroAddress();

    error TBTCTokenIsZeroAddress();

    error AmountBelowMinTBTCAmount();

    /// @notice Function reverts with this error if the BTC deposit state is not as
    ///         expected. `initializeBTCBridging` can only be called for
    ///         `Unknown` deposits. `finalizeBTCBridging` can only be called
    ///         for `Initialized` deposits.
    error UnexpectedBTCDepositState(
        BTCDepositState actualState,
        BTCDepositState expectedState
    );

    /// @notice `finalizeBTCBridging` reverts with this error if the
    ///         `recipient` passed as the parameters is not the same as passed
    ///         earlier to the `initializeBTCBridging`.
    error UnexpectedExtraData(
        bytes32 actualExtraData,
        bytes32 expectedExtraData
    );

    /// @notice Bridges the `amount` of the `token` to the `recipient` address on Mezo.
    /// @param recipient Recipient of the bridged token.
    /// @param token Address of the bridged token.
    /// @param amount Amount of the bridged token.
    function _bridge(
        address recipient,
        address token,
        uint256 amount
    ) internal virtual;

    /// @notice Initializes the contract.
    /// @dev All addresses passed to the contract must not be 0x0.
    /// @param _tbtcBridge Address to the tBTC Bridge contract.
    /// @param _tbtcVault Address to the tBTC TBTCVault contract.
    /// @param _tbtcToken Address to the tBTC ERC20 token contract.
    function __BitcoinBridge_initialize(
        address _tbtcBridge,
        address _tbtcVault,
        address _tbtcToken
    ) internal {
        __AbstractTBTCDepositor_initialize(_tbtcBridge, _tbtcVault);
        // Note that initializers of OZ upgradeable contracts are not linearized
        // by the compiler like constructors. Therefore, if __BitcoinBridge_initialize
        // is called from within a child contract's initializer that also calls
        // __Ownable_init and __ReentrancyGuard_init somewhere else in the
        // inheritance chain, the Ownable and ReentrancyGuard initializers will
        // be called twice (it's actually the case for the MezoBridge child contract).
        // Although this is not a problem with currently used OZ version where
        // __Ownable_init and __ReentrancyGuard_init are idempotent
        // (as long as __Ownable_init is called with the same argument),
        // it's worth noting this caveat for future.
        //
        // For reference, see the following OZ documentation:
        // https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/v5.0.2/docs/modules/ROOT/pages/upgradeable.adoc#multiple-inheritance
        __Ownable_init(msg.sender);
        __ReentrancyGuard_init();

        if (_tbtcToken == address(0)) {
            revert TBTCTokenIsZeroAddress();
        }
        tbtcToken = _tbtcToken;

        minTBTCAmount = 0.01 * 1e18; // 0.01 BTC
    }

    /// @notice Updates the minimum tBTC amount allowed to be bridged using
    ///         `bridgeTBTC` and `bridgeTBTCWithPermit`.
    /// @param newMinTBTCAmount New minimum tBTC amount (in tBTC precision).
    ///                         Must be positive.
    /// @dev Access restricted with the `onlyOwner` modifier.
    function updateMinTBTCAmount(uint256 newMinTBTCAmount) external onlyOwner {
        if (newMinTBTCAmount == 0) {
            revert MinTBTCAmountIsZero();
        }

        minTBTCAmount = newMinTBTCAmount;

        emit MinTBTCAmountUpdated(newMinTBTCAmount);
    }

    /// @notice Transfers and locks the `amount` of tBTC in the contract and
    ///         calls `_bridge` function thus initiating bridging to Mezo to
    ///         the `recipient` address.
    /// @param amount Amount of tBTC to be bridged.
    /// @param recipient Recipient of the bridged tBTC.
    /// @dev Requirements:
    ///     - The amount must be equal to or greater than the minimum tBTC
    ///       amount allowed to be bridged.
    ///     - The tBTC is transferred using the allowance mechanism. The caller
    ///       must ensure the appropriate amount of tBTC is approved for the
    ///      `BitcoinBridge` contract.
    function bridgeTBTC(
        uint256 amount,
        address recipient
    ) external nonReentrant {
        _bridgeTBTC(amount, recipient);
    }

    /// @notice Private function holding the actual logic for the external
    ///         non-reentrant `bridgeTBTC` function. This function allows
    ///         reusing TBTC bridging logic from another external non-reentrant
    ///         function - `bridgeTBTCWithPermit`. This wouldn't be possible
    ///         without the `_bridgeTBTC` function, as non-reentrant functions
    ///         cannot call themselves.
    function _bridgeTBTC(uint256 amount, address recipient) private {
        if (recipient == address(0)) {
            revert BTCRecipientIsZeroAddress();
        }

        if (amount < minTBTCAmount) {
            revert AmountBelowMinTBTCAmount();
        }

        // Get tbtcToken from storage only once to save gas.
        address _tbtcToken = tbtcToken;

        _bridge(recipient, _tbtcToken, amount);

        IERC20(_tbtcToken).safeTransferFrom(msg.sender, address(this), amount);
    }

    /// @notice Transfers and locks the `amount` of tBTC in the contract and
    ///         calls `_bridge` function thus initializing bridging to Mezo to
    ///         the `recipient` address. If the caller has not approved enough
    ///         tBTC for the `BitcoinBridge` contract, it also increases the
    ///         allowance using the EIP2612 permit functionality.
    /// @dev This function can achieve the same result as `bridgeTBTC`, but
    ///      it does not have to be preceded with a separate transaction
    ///      approving tBTC.
    ///      Requirements:
    ///      - must be called by the same address that generated the signature,
    ///      - the `amount` and `deadline` must be the same as used during the
    ///        signature generation and `v`, `r`, `s` parameters must represent
    ///        a valid signature.
    /// @param amount Amount of tBTC to be bridged.
    /// @param recipient Recipient of the bridged tBTC.
    /// @param deadline EIP2612 deadline
    /// @param v EIP2612 signature v
    /// @param r EIP2612 signature r
    /// @param s EIP2612 signature s
    function bridgeTBTCWithPermit(
        uint256 amount,
        address recipient,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external nonReentrant {
        // If allowance is sufficient, we can fallback to the regular
        // `bridgeTBTC` call. This is also a protection in case someone
        // front-runned the `bridgeTBTCWithPermit` call and already executed
        // permit on the `tbtcToken` contract.
        if (IERC20(tbtcToken).allowance(msg.sender, address(this)) < amount) {
            IERC20WithPermit(tbtcToken).permit(
                msg.sender,
                address(this),
                amount,
                deadline,
                v,
                r,
                s
            );
        }

        _bridgeTBTC(amount, recipient);
    }

    /// @notice Initializes the BTC deposit process after the Bitcoin P2(W)SH deposit
    ///         transaction is performed. Reveals the deposit to tBTC bridge and
    ///         marks the deposit as initialized internally. Once tBTC minting
    ///         is completed, this call should be followed by a call to
    ///         `finalizeBTCBridging`. The P2(W)SH Bitcoin script depositing
    ///         tokens should have the following format:
    ///
    ///         <bridge-address> DROP
    ///         <recipient-extra-data> DROP
    ///         <blinding-factor> DROP
    ///         DUP HASH160 <signingGroupPubkeyHash> EQUAL
    ///         IF
    ///           CHECKSIG
    ///         ELSE
    ///           DUP HASH160 <refundPubkeyHash> EQUALVERIFY
    ///           <locktime> CHECKLOCKTIMEVERIFY DROP
    ///           CHECKSIG
    ///         ENDIF
    ///
    ///         Where:
    ///
    ///         <bridge-address> 20-byte Ethereum address of the BitcoinBridge
    ///         contract.
    ///
    ///         <recipient-extra-data> 32-byte keccak256 hash of abi-encoded
    ///         recipient address.
    ///
    ///         <blinding-factor> 8-byte deposit blinding factor, as used in the
    ///         tBTC bridge
    ///
    ///         <signingGroupPubkeyHash> The compressed Bitcoin public key (33
    ///         bytes and 02 or 03 prefix) of the deposit's wallet hashed in the
    ///         HASH160 Bitcoin opcode style. This must point to the active tBTC
    ///         bridge wallet.
    ///
    ///         <refundPubkeyHash> The compressed Bitcoin public key (33 bytes
    ///         and 02 or 03 prefix) that can be used to make the deposit refund
    ///         after the tBTC bridge refund locktime passed. Hashed in the
    ///         HASH160 Bitcoin opcode style. This is needed only as a security
    ///         measure protecting the user in case tBTC bridge completely stops
    ///         functioning.
    ///
    ///         <locktime> The Bitcoin script refund locktime (4-byte LE),
    ///         according to tBTC bridge rules.
    ///
    ///         Please consult tBTC Bridge.revealDepositWithExtraData function
    ///         documentation for more information.
    /// @dev IMPORTANT NOTE: The amount of tBTC bridged to the Mezo chain may
    ///      not correspond to the actual amount of tBTC minted for the deposit.
    ///      The reason for this is that the fees for optimistic minting and
    ///      Bitcoin transaction are not known precisely upon minting. For the
    ///      great majority of deposits, slightly less tBTC will be bridged and
    ///      the surplus of tBTC will remain in the `BitcoinBridge` contract.
    ///      For a detailed explanation see `_calculateTbtcAmount` from the
    ///      `AbstractTBTCDepositor` contract.
    ///      Requirements:
    ///      - The recipient address must be represented in the Bitcoin deposit
    ///        P2(W)SH script in the <recipient-extra-data>. If the value in the
    ///        Bitcoin script and the value passed as the parameters do not
    ///        match, the function will revert.
    ///      - The function can be called only one time for the given P2(W)SH
    ///        Bitcoin deposit transaction.
    ///      - All the requirements of tBTC Bridge.revealDepositWithExtraData
    ///        must be met.
    /// @param fundingTx Bitcoin funding transaction data, see `BitcoinTx.Info`
    /// @param reveal Deposit reveal data, see `RevealInfo` struct.
    /// @param recipient Recipient of the bridged tBTC in Mezo.
    function initializeBTCBridging(
        IBridgeTypes.BitcoinTxInfo calldata fundingTx,
        IBridgeTypes.DepositRevealInfo calldata reveal,
        address recipient
    ) external {
        if (recipient == address(0)) {
            revert BTCRecipientIsZeroAddress();
        }

        // Store `keccak256` of the recipient address rather than the recipient
        // address in extra data. This gives us the flexibility to add new
        // parameters in the future.
        (uint256 btcDepositKey, ) = _initializeDeposit(
            fundingTx,
            reveal,
            keccak256(abi.encode(recipient))
        );

        if (btcDeposits[btcDepositKey] != BTCDepositState.Unknown) {
            revert UnexpectedBTCDepositState(
                btcDeposits[btcDepositKey],
                BTCDepositState.Unknown
            );
        }

        btcDeposits[btcDepositKey] = BTCDepositState.Initialized;

        emit BTCDepositInitialized(btcDepositKey, recipient);
    }

    /// @notice Finalizes the BTC deposit process by locking tBTC ERC20 token to
    ///         the `BitcoinBridge` contract and calling `_bridge` function
    ///         thus initiating bridging to the Mezo chain. This function should
    ///         be called after the deposit was initialized with a call to
    ///         `initializeBTCBridging` function and after tBTC ERC20 token
    ///         was minted by the bridge to the `BitcoinBridge` contract.
    ///         Please note several hours may pass between `initializeBTCBridging`
    ///         and `finalizeBTCBridging`.
    /// @dev Requirements:
    ///      - `initializeBTCBridging` was called for the given deposit before.
    ///      - tBTC ERC20 was minted by tBTC Bridge to this contract.
    ///      - The function was not called for the given deposit before.
    ///      - The same `recipient` address was passed when initiating the
    ///        deposit.
    /// @param btcDepositKey The BTC deposit key, as emitted in the `BTCDepositInitialized`
    ///        event emitted by the `initializeBTCBridging` function for the deposit.
    /// @param recipient The address of the account that should own the
    ///        deposit. This must be the same value as passed to
    ///        `initializeBTCBridging` for the deposit.
    function finalizeBTCBridging(
        uint256 btcDepositKey,
        address recipient
    ) external {
        if (btcDeposits[btcDepositKey] != BTCDepositState.Initialized) {
            revert UnexpectedBTCDepositState(
                btcDeposits[btcDepositKey],
                BTCDepositState.Initialized
            );
        }

        btcDeposits[btcDepositKey] = BTCDepositState.Finalized;

        (
            uint256 initialDepositAmount,
            uint256 tbtcAmount,
            bytes32 expectedExtraData
        ) = _finalizeDeposit(btcDepositKey);

        bytes32 actualExtraData = keccak256(abi.encode(recipient));

        if (actualExtraData != expectedExtraData) {
            revert UnexpectedExtraData(actualExtraData, expectedExtraData);
        }

        emit BTCDepositFinalized(
            btcDepositKey,
            initialDepositAmount,
            tbtcAmount
        );

        _bridge(recipient, tbtcToken, tbtcAmount);
    }
}

// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.24;

import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol";

/// @notice ERC20Bridge abstract contract exposes the capability of bridging
///         ERC20 tokens from Ethereum to the Mezo chain.
/// @dev The contract is supposed to be extended by the MezoBridge contract.
abstract contract ERC20Bridge is
    Ownable2StepUpgradeable,
    ReentrancyGuardUpgradeable
{
    using SafeERC20 for IERC20;

    /// @notice Maximum number of distinct ERC20 tokens that can be enabled
    ///         in the bridge.
    /// @dev The reason for this limit is to not downgrade the bridge
    ///      performance on the Mezo chain side too much.
    uint256 public constant MAX_ERC20_TOKENS = 20;

    /// @notice Count of ERC20 tokens enabled in the bridge.
    uint256 public ERC20TokensCount;

    /// @notice Mapping of ERC20 tokens to their minimum bridgeable amounts,
    ///         in the token precision.
    mapping(address => uint256) public ERC20Tokens;

    // Reserved storage space that allows adding more variables without affecting
    // the storage layout of the child contracts. The convention from OpenZeppelin
    // suggests the storage space should add up to 50 slots. If more variables are
    // added in the upcoming versions one need to reduce the array size accordingly.
    // See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
    // slither-disable-next-line unused-state
    uint256[48] private __gap;

    event ERC20TokenEnabled(address indexed ERC20Token, uint256 minERC20Amount);

    event ERC20TokenDisabled(address indexed ERC20Token);

    event MinERC20AmountUpdated(
        address indexed ERC20Token,
        uint256 newMinERC20Amount
    );

    error MinERC20AmountIsZero();

    error ERC20TokenIsZeroAddress();

    error ERC20TokenAlreadyEnabled();

    error MaxERC20TokensReached();

    error ERC20TokenNotEnabled();

    error ERC20RecipientIsZeroAddress();

    error AmountBelowMinERC20Amount();

    /// @notice Bridges the `amount` of the `token` to the `recipient` address on Mezo.
    /// @param recipient Recipient of the bridged token.
    /// @param token Address of the bridged token.
    /// @param amount Amount of the bridged token.
    function _bridge(
        address recipient,
        address token,
        uint256 amount
    ) internal virtual;

    /// @notice Initializes the contract.
    function __ERC20Bridge_initialize() internal {
        // Note that initializers of OZ upgradeable contracts are not linearized
        // by the compiler like constructors. Therefore, if __ERC20Bridge_initialize
        // is called from within a child contract's initializer that also calls
        // __Ownable_init and __ReentrancyGuard_init somewhere else in the
        // inheritance chain, the Ownable and ReentrancyGuard initializers will
        // be called twice (it's actually the case for the MezoBridge child contract).
        // Although this is not a problem with currently used OZ version where
        // __Ownable_init and __ReentrancyGuard_init are idempotent
        // (as long as __Ownable_init is called with the same argument),
        // it's worth noting this caveat for future.
        //
        // For reference, see the following OZ documentation:
        // https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/v5.0.2/docs/modules/ROOT/pages/upgradeable.adoc#multiple-inheritance
        __Ownable_init(msg.sender);
        __ReentrancyGuard_init();

        // Just in case.
        ERC20TokensCount = 0;
    }

    /// @notice Enables bridging support for the given ERC20 token, with the given
    ///         minimum bridgeable amount.
    /// @param ERC20Token Address of the ERC20 token to be enabled.
    /// @param minERC20Amount Minimum amount of the ERC20 token that can be bridged,
    ///                       in the token precision.
    /// @dev Requirements:
    ///      - Access restricted with the `onlyOwner` modifier,
    ///      - The ERC20 token must not be the zero address,
    ///      - The minimum ERC20 amount must be positive,
    ///      - The ERC20 token must not be already enabled,
    ///      - The maximum number of ERC20 tokens must not be reached.
    ///
    /// @dev BEWARE!!! BEFORE ENABLING AN ERC20 TOKEN USING THIS FUNCTION,
    ///      MAKE SURE THE CORRECT MAPPING FOR THIS TOKEN EXISTS ON THE
    ///      MEZO CHAIN. OTHERWISE, THE BRIDGE WILL IGNORE THE BRIDGING REQUESTS
    ///      FOR THIS TOKEN. THE AMOUNT BRIDGED WILL BE LOCKED ON THIS
    ///      CONTRACT AND NO TOKENS WILL BE ISSUED ON THE MEZO CHAIN.
    function enableERC20Token(
        address ERC20Token,
        uint256 minERC20Amount
    ) external onlyOwner {
        if (ERC20Token == address(0)) {
            revert ERC20TokenIsZeroAddress();
        }

        if (minERC20Amount == 0) {
            revert MinERC20AmountIsZero();
        }

        if (ERC20Tokens[ERC20Token] != 0) {
            revert ERC20TokenAlreadyEnabled();
        }

        if (ERC20TokensCount >= MAX_ERC20_TOKENS) {
            revert MaxERC20TokensReached();
        }

        ERC20TokensCount++;
        ERC20Tokens[ERC20Token] = minERC20Amount;

        emit ERC20TokenEnabled(ERC20Token, minERC20Amount);
    }

    /// @notice Disabled bridging support for the given ERC20 token.
    /// @param ERC20Token Address of the ERC20 token to be disabled.
    /// @dev Requirements:
    ///      - Access restricted with the `onlyOwner` modifier,
    ///      - The ERC20 token must be enabled.
    function disableERC20Token(address ERC20Token) external onlyOwner {
        // enableERC20Token does not allow ERC20Token to be 0x0 address so, it's
        // enough to check if the token is enabled, without checking if it's 0x0.
        if (ERC20Tokens[ERC20Token] == 0) {
            revert ERC20TokenNotEnabled();
        }

        ERC20TokensCount--;
        delete ERC20Tokens[ERC20Token];

        emit ERC20TokenDisabled(ERC20Token);
    }

    /// @notice Updates the minimum ERC20 amount allowed to be bridged using
    ///         `bridgeERC20`.
    /// @param newMinERC20Amount New minimum ERC20 amount (in the token precision).
    ///                          Must be positive.
    /// @dev Requirements:
    ///      - Access restricted with the `onlyOwner` modifier,
    ///      - The ERC20 token must be enabled,
    ///      - The new minimum ERC20 amount must be positive.
    function updateMinERC20Amount(
        address ERC20Token,
        uint256 newMinERC20Amount
    ) external onlyOwner {
        // enableERC20Token does not allow ERC20Token to be 0x0 address so, it's
        // enough to check if the token is enabled, without checking if it's 0x0.
        if (ERC20Tokens[ERC20Token] == 0) {
            revert ERC20TokenNotEnabled();
        }

        if (newMinERC20Amount == 0) {
            revert MinERC20AmountIsZero();
        }

        ERC20Tokens[ERC20Token] = newMinERC20Amount;

        emit MinERC20AmountUpdated(ERC20Token, newMinERC20Amount);
    }

    /// @notice Bridges the `amount` of the `ERC20Token` to the `recipient` address on Mezo.
    /// @param ERC20Token Address of the bridged ERC20 token.
    /// @param amount Amount of the bridged ERC20 token.
    /// @param recipient Recipient of the bridged ERC20 token on Mezo.
    /// @dev Requirements:
    ///      - The ERC20 token must be enabled,
    ///      - The recipient address must not be the zero address,
    ///      - The amount must be greater than or equal to the minimum ERC20 amount,
    ///      - The caller must have allowed the contract to transfer the `amount` of the `ERC20Token`.
    function bridgeERC20(
        address ERC20Token,
        uint256 amount,
        address recipient
    ) external nonReentrant {
        // enableERC20Token does not allow ERC20Token to be 0x0 address so, it's
        // enough to check if the token is enabled, without checking if it's 0x0.
        uint256 minERC20Amount = ERC20Tokens[ERC20Token];
        if (minERC20Amount == 0) {
            revert ERC20TokenNotEnabled();
        }

        if (recipient == address(0)) {
            revert ERC20RecipientIsZeroAddress();
        }

        if (amount < minERC20Amount) {
            revert AmountBelowMinERC20Amount();
        }

        _bridge(recipient, ERC20Token, amount);

        IERC20(ERC20Token).safeTransferFrom(msg.sender, address(this), amount);
    }
}

File 19 of 19 : IERC20WithPermit.sol
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.24;

/// @notice An interface to be optionally implemented by ERC20 tokens to enable
///         gasless approvals.
interface IERC20WithPermit {
    /// @notice EIP2612 approval made with secp256k1 signature.
    ///         Users can authorize a transfer of their tokens with a signature
    ///         conforming EIP712 standard, rather than an on-chain transaction
    ///         from their address. Anyone can submit this signature on the
    ///         user's behalf by calling the permit function, paying gas fees,
    ///         and possibly performing other actions in the same transaction.
    function permit(
        address owner,
        address spender,
        uint256 amount,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;
}

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

Contract Security Audit

Contract ABI

API
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"AddressInsufficientBalance","type":"error"},{"inputs":[],"name":"AmountBelowMinERC20Amount","type":"error"},{"inputs":[],"name":"AmountBelowMinTBTCAmount","type":"error"},{"inputs":[],"name":"BTCRecipientIsZeroAddress","type":"error"},{"inputs":[],"name":"ERC20RecipientIsZeroAddress","type":"error"},{"inputs":[],"name":"ERC20TokenAlreadyEnabled","type":"error"},{"inputs":[],"name":"ERC20TokenIsZeroAddress","type":"error"},{"inputs":[],"name":"ERC20TokenNotEnabled","type":"error"},{"inputs":[],"name":"FailedInnerCall","type":"error"},{"inputs":[],"name":"InvalidInitialization","type":"error"},{"inputs":[],"name":"MaxERC20TokensReached","type":"error"},{"inputs":[],"name":"MinERC20AmountIsZero","type":"error"},{"inputs":[],"name":"MinTBTCAmountIsZero","type":"error"},{"inputs":[],"name":"NotInitializing","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"inputs":[],"name":"TBTCTokenIsZeroAddress","type":"error"},{"inputs":[{"internalType":"enum BitcoinBridge.BTCDepositState","name":"actualState","type":"uint8"},{"internalType":"enum BitcoinBridge.BTCDepositState","name":"expectedState","type":"uint8"}],"name":"UnexpectedBTCDepositState","type":"error"},{"inputs":[{"internalType":"bytes32","name":"actualExtraData","type":"bytes32"},{"internalType":"bytes32","name":"expectedExtraData","type":"bytes32"}],"name":"UnexpectedExtraData","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"sequenceNumber","type":"uint256"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"AssetsLocked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"btcDepositKey","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"initialAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"tbtcAmount","type":"uint256"}],"name":"BTCDepositFinalized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"btcDepositKey","type":"uint256"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"}],"name":"BTCDepositInitialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"ERC20Token","type":"address"}],"name":"ERC20TokenDisabled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"ERC20Token","type":"address"},{"indexed":false,"internalType":"uint256","name":"minERC20Amount","type":"uint256"}],"name":"ERC20TokenEnabled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"version","type":"uint64"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"ERC20Token","type":"address"},{"indexed":false,"internalType":"uint256","name":"newMinERC20Amount","type":"uint256"}],"name":"MinERC20AmountUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"minTBTCAmount","type":"uint256"}],"name":"MinTBTCAmountUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferStarted","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"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"ERC20Tokens","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ERC20TokensCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_ERC20_TOKENS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SATOSHI_MULTIPLIER","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"bridge","outputs":[{"internalType":"contract IBridge","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"ERC20Token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"}],"name":"bridgeERC20","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"}],"name":"bridgeTBTC","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"},{"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":"bridgeTBTCWithPermit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"btcDeposits","outputs":[{"internalType":"enum BitcoinBridge.BTCDepositState","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"ERC20Token","type":"address"}],"name":"disableERC20Token","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"ERC20Token","type":"address"},{"internalType":"uint256","name":"minERC20Amount","type":"uint256"}],"name":"enableERC20Token","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"btcDepositKey","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"}],"name":"finalizeBTCBridging","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_tbtcBridge","type":"address"},{"internalType":"address","name":"_tbtcVault","type":"address"},{"internalType":"address","name":"_tbtcToken","type":"address"},{"internalType":"uint256","name":"_initialSequence","type":"uint256"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"bytes4","name":"version","type":"bytes4"},{"internalType":"bytes","name":"inputVector","type":"bytes"},{"internalType":"bytes","name":"outputVector","type":"bytes"},{"internalType":"bytes4","name":"locktime","type":"bytes4"}],"internalType":"struct IBridgeTypes.BitcoinTxInfo","name":"fundingTx","type":"tuple"},{"components":[{"internalType":"uint32","name":"fundingOutputIndex","type":"uint32"},{"internalType":"bytes8","name":"blindingFactor","type":"bytes8"},{"internalType":"bytes20","name":"walletPubKeyHash","type":"bytes20"},{"internalType":"bytes20","name":"refundPubKeyHash","type":"bytes20"},{"internalType":"bytes4","name":"refundLocktime","type":"bytes4"},{"internalType":"address","name":"vault","type":"address"}],"internalType":"struct IBridgeTypes.DepositRevealInfo","name":"reveal","type":"tuple"},{"internalType":"address","name":"recipient","type":"address"}],"name":"initializeBTCBridging","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"minTBTCAmount","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":"pendingOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"sequence","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tbtcToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tbtcVault","outputs":[{"internalType":"contract ITBTCVault","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"ERC20Token","type":"address"},{"internalType":"uint256","name":"newMinERC20Amount","type":"uint256"}],"name":"updateMinERC20Amount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newMinTBTCAmount","type":"uint256"}],"name":"updateMinTBTCAmount","outputs":[],"stateMutability":"nonpayable","type":"function"}]

0x60806040523480156200001157600080fd5b506200001c62000022565b620000d6565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff1615620000735760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b0390811614620000d35780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b6129b080620000e66000396000f3fe608060405234801561001057600080fd5b50600436106101a35760003560e01c80638da5cb5b116100ee578063d80687ef11610097578063e30c397811610071578063e30c39781461033d578063e5d3d71414610345578063e78cea9214610358578063f2fde38b1461036b57600080fd5b8063d80687ef14610301578063dab1b4bd14610321578063df4d46631461032a57600080fd5b8063c7ba0347116100c8578063c7ba0347146102d9578063cf756fdf146102e5578063d252bb2c146102f857600080fd5b80638da5cb5b1461028e578063908d272b14610296578063941b1f94146102a957600080fd5b806362fe53e111610150578063715018a61161012a578063715018a61461026b57806374ca12791461027357806379ba50971461028657600080fd5b806362fe53e11461023257806367a68320146102455780636f64aca21461025857600080fd5b8063529d15cc11610181578063529d15cc146102005780635febd8eb14610217578063619121741461021f57600080fd5b80630f36403a146101a857806324f90de9146101d8578063427f9568146101ed575b600080fd5b6001546101bb906001600160a01b031681565b6040516001600160a01b0390911681526020015b60405180910390f35b6101eb6101e6366004611fa9565b61037e565b005b6101eb6101fb366004611fd9565b610511565b61020960955481565b6040519081526020016101cf565b610209601481565b6101eb61022d36600461203b565b61067f565b6101eb61024036600461207d565b6107a3565b6101eb610253366004612096565b610820565b6101eb6102663660046120c2565b61099c565b6101eb610afa565b6101eb610281366004612149565b610b0e565b6101eb610bc0565b6101bb610c21565b6101eb6102a4366004612096565b610c56565b6102cc6102b736600461207d565b60326020526000908152604090205460ff1681565b6040516101cf91906121d0565b6102096402540be40081565b6101eb6102f33660046121de565b610d36565b61020960635481565b61020961030f366004612149565b60646020526000908152604090205481565b61020960335481565b6101eb610338366004611fa9565b610ec8565b6101bb610f07565b6031546101bb906001600160a01b031681565b6000546101bb906001600160a01b031681565b6101eb610379366004612149565b610f30565b600160008381526032602052604090205460ff1660028111156103a3576103a3612166565b146103fa57600082815260326020526040908190205490517fed33029f0000000000000000000000000000000000000000000000000000000081526103f19160ff169060019060040161222f565b60405180910390fd5b600082815260326020526040812080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166002179055808061043c85610fcd565b604080516001600160a01b03891660208201529396509194509250600091016040516020818303038152906040528051906020012090508181146104b6576040517fd961e24c00000000000000000000000000000000000000000000000000000000815260048101829052602481018390526044016103f1565b604080518581526020810185905287917fa81d3c9594b1f3363bfc07d9277c4624e0da8dae3b42d466f1edc0718c62ab53910160405180910390a26031546105099086906001600160a01b03168561124d565b505050505050565b6105196112ad565b6031546040517fdd62ed3e00000000000000000000000000000000000000000000000000000000815233600482015230602482015287916001600160a01b03169063dd62ed3e90604401602060405180830381865afa158015610580573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105a4919061224a565b101561064c576031546040517fd505accf000000000000000000000000000000000000000000000000000000008152336004820152306024820152604481018890526064810186905260ff8516608482015260a4810184905260c481018390526001600160a01b039091169063d505accf9060e401600060405180830381600087803b15801561063357600080fd5b505af1158015610647573d6000803e3d6000fd5b505050505b610656868661132e565b61050960017f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f0055565b6106876112ad565b6001600160a01b038316600090815260646020526040812054908190036106da576040517f881b8d7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b03821661071a576040517f478b9dda00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80831015610754576040517f542f23f600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61075f82858561124d565b6107746001600160a01b0385163330866113fc565b5061079e60017f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f0055565b505050565b6107ab61148a565b806000036107e5576040517f4b81b6b900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60338190556040518181527fe64dbc80c2152cea46e3b80ba80f3e8c125114dc79194e9c947b480cfc80e59c9060200160405180910390a150565b61082861148a565b6001600160a01b038216610868576040517f09efa31000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b806000036108a2576040517f47fba16a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b038216600090815260646020526040902054156108f2576040517f98f1758300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60146063541061092e576040517f17dfdea400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6063805490600061093e83612292565b90915550506001600160a01b03821660008181526064602052604090819020839055517ff17d094161c4f2776fc9caa30094c8ebe1b86cd6f2108db5d9f1d46d8f85494c906109909084815260200190565b60405180910390a25050565b6001600160a01b0381166109dc576040517fb9c50bfb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b604080516001600160a01b0383166020820152600091610a16918691869101604051602081830303815290604052805190602001206114d5565b5090506000808281526032602052604090205460ff166002811115610a3d57610a3d612166565b14610a8a576000818152603260205260408082205490517fed33029f0000000000000000000000000000000000000000000000000000000081526103f19260ff909216919060040161222f565b60008181526032602052604080822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055516001600160a01b0384169183917f2fbc945bad45e66509bad2bda7b97993796881f9ac2543b827d2aaf69f1869239190a350505050565b610b0261148a565b610b0c60006116b3565b565b610b1661148a565b6001600160a01b0381166000908152606460205260408120549003610b67576040517f881b8d7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60638054906000610b77836122ca565b90915550506001600160a01b038116600081815260646020526040808220829055517f9c4edffd5782d54d432f513a2a7d944aac6f743c7ef4a83d8c6189ba21dd42999190a250565b3380610bca610f07565b6001600160a01b031614610c15576040517f118cdaa70000000000000000000000000000000000000000000000000000000081526001600160a01b03821660048201526024016103f1565b610c1e816116b3565b50565b6000807f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c1993005b546001600160a01b031692915050565b610c5e61148a565b6001600160a01b0382166000908152606460205260408120549003610caf576040517f881b8d7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80600003610ce9576040517f47fba16a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b03821660008181526064602052604090819020839055517f886950a2d9ce5c7d214261968375335366c8547e3e5eb5e1744c3cb581c4a672906109909084815260200190565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000810460ff16159067ffffffffffffffff16600081158015610d815750825b905060008267ffffffffffffffff166001148015610d9e5750303b155b905081158015610dac575080155b15610de3576040517ff92ee8a900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b84547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001660011785558315610e445784547fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff16680100000000000000001785555b610e4f898989611703565b610e576117a5565b60958690558315610ebd5784547fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b505050505050505050565b610ed06112ad565b610eda828261132e565b610f0360017f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f0055565b5050565b6000807f237e158222e3e6968b72b9db0d8043aacf074ad9f650f0d1606b4d82ee432c00610c46565b610f3861148a565b7f237e158222e3e6968b72b9db0d8043aacf074ad9f650f0d1606b4d82ee432c0080547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383169081178255610f94610c21565b6001600160a01b03167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270060405160405180910390a35050565b600080546040517fb02c43d0000000000000000000000000000000000000000000000000000000008152600481018490528291829182916001600160a01b03169063b02c43d09060240160e060405180830381865afa158015611034573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611058919061233f565b9050806040015163ffffffff166000036110ce576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f4465706f736974206e6f7420696e697469616c697a656400000000000000000060448201526064016103f1565b6001546040517f6c626aa4000000000000000000000000000000000000000000000000000000008152600481018790526000916001600160a01b031690636c626aa49060240160408051808303816000875af1158015611132573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111569190612412565b9150508160a0015163ffffffff16600014158061117c575067ffffffffffffffff811615155b611208576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602360248201527f4465706f736974206e6f742066696e616c697a6564206279207468652062726960448201527f646765000000000000000000000000000000000000000000000000000000000060648201526084016103f1565b6402540be400826020015167ffffffffffffffff166112279190612445565b945061123b826020015183608001516117bd565b93508160c00151925050509193909250565b816001600160a01b0316836001600160a01b031660956000815461127090612292565b91829055506040518481527f75aa5616721471b8ab0c49ce59500cbad2b7ef1ad10e5eb9449c693c0a5c8fd19060200160405180910390a4505050565b7f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f0080547ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01611328576040517f3ee5aeb500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60029055565b6001600160a01b03811661136e576040517fb9c50bfb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6033548210156113aa576040517f0b84d72c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6031546001600160a01b03166113c182828561124d565b61079e6001600160a01b0382163330866113fc565b60017f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f0055565b604080516001600160a01b0385811660248301528416604482015260648082018490528251808303909101815260849091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f23b872dd00000000000000000000000000000000000000000000000000000000179052611484908590611942565b50505050565b33611493610c21565b6001600160a01b031614610b0c576040517f118cdaa70000000000000000000000000000000000000000000000000000000081523360048201526024016103f1565b60015460009081906001600160a01b03166114f660c0860160a08701612149565b6001600160a01b031614611566576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f5661756c742061646472657373206d69736d617463680000000000000000000060448201526064016103f1565b611584611572866119be565b61157f602087018761245c565b611a23565b6000546040517f86f014390000000000000000000000000000000000000000000000000000000081529193506001600160a01b0316906386f01439906115d29088908890889060040161258d565b600060405180830381600087803b1580156115ec57600080fd5b505af1158015611600573d6000803e3d6000fd5b50506000546040517fb02c43d0000000000000000000000000000000000000000000000000000000008152600481018690526402540be40093506001600160a01b03909116915063b02c43d09060240160e060405180830381865afa15801561166d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611691919061233f565b6020015167ffffffffffffffff166116a99190612445565b9050935093915050565b7f237e158222e3e6968b72b9db0d8043aacf074ad9f650f0d1606b4d82ee432c0080547fffffffffffffffffffffffff0000000000000000000000000000000000000000168155610f0382611aa3565b61170d8383611b2c565b61171633611d00565b61171e611d11565b6001600160a01b03811661175e576040517fe747bdc200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b603180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b03929092169190911790555050662386f26fc10000603355565b6117ae33611d00565b6117b6611d11565b6000606355565b6000806402540be4006117d08486612766565b67ffffffffffffffff166117e49190612445565b90506000600160009054906101000a90046001600160a01b03166001600160a01b03166309b53f516040518163ffffffff1660e01b8152600401602060405180830381865afa15801561183b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061185f919061278e565b63ffffffff1690506000808211611877576000611881565b61188182846127ab565b905060008060009054906101000a90046001600160a01b03166001600160a01b031663c42b64d06040518163ffffffff1660e01b8152600401608060405180830381865afa1580156118d7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118fb91906127e6565b509250505060006402540be4008267ffffffffffffffff1661191d9190612445565b90508061192a848761283c565b611934919061283c565b955050505050505b92915050565b60006119576001600160a01b03841683611d21565b9050805160001415801561197c57508080602001905181019061197a919061284f565b155b1561079e576040517f5274afe70000000000000000000000000000000000000000000000000000000081526001600160a01b03841660048201526024016103f1565b600061193c6119d06020840184612871565b6119dd602085018561288c565b6119ea604087018761288c565b6119fa6080890160608a01612871565b604051602001611a0f969594939291906128f1565b604051602081830303815290604052611d36565b60008282604051602001611a6692919091825260e01b7fffffffff0000000000000000000000000000000000000000000000000000000016602082015260240190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815291905280516020909101209392505050565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080547fffffffffffffffffffffffff000000000000000000000000000000000000000081166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3505050565b6000546001600160a01b0316158015611b4e57506001546001600160a01b0316155b611bda576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602960248201527f4162737472616374544254434465706f7369746f7220616c726561647920696e60448201527f697469616c697a6564000000000000000000000000000000000000000000000060648201526084016103f1565b6001600160a01b038216611c4a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f42726964676520616464726573732063616e6e6f74206265207a65726f00000060448201526064016103f1565b6001600160a01b038116611cba576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f544254435661756c7420616464726573732063616e6e6f74206265207a65726f60448201526064016103f1565b600080546001600160a01b039384167fffffffffffffffffffffffff00000000000000000000000000000000000000009182161790915560018054929093169116179055565b611d08611d5d565b610c1e81611dc4565b611d19611d5d565b610b0c611e0f565b6060611d2f83836000611e17565b9392505050565b60006020600083516020850160025afa50602060006020600060025afa5050600051919050565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a005468010000000000000000900460ff16610b0c576040517fd7e6bcf800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611dcc611d5d565b6001600160a01b038116610c15576040517f1e4fbdf7000000000000000000000000000000000000000000000000000000008152600060048201526024016103f1565b6113d6611d5d565b606081471015611e55576040517fcd7860590000000000000000000000000000000000000000000000000000000081523060048201526024016103f1565b600080856001600160a01b03168486604051611e71919061294b565b60006040518083038185875af1925050503d8060008114611eae576040519150601f19603f3d011682016040523d82523d6000602084013e611eb3565b606091505b5091509150611ec3868383611ecd565b9695505050505050565b606082611ee257611edd82611f42565b611d2f565b8151158015611ef957506001600160a01b0384163b155b15611f3b576040517f9996b3150000000000000000000000000000000000000000000000000000000081526001600160a01b03851660048201526024016103f1565b5080611d2f565b805115611f525780518082602001fd5b6040517f1425ea4200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b0381168114610c1e57600080fd5b8035611fa481611f84565b919050565b60008060408385031215611fbc57600080fd5b823591506020830135611fce81611f84565b809150509250929050565b60008060008060008060c08789031215611ff257600080fd5b86359550602087013561200481611f84565b945060408701359350606087013560ff8116811461202157600080fd5b9598949750929560808101359460a0909101359350915050565b60008060006060848603121561205057600080fd5b833561205b81611f84565b925060208401359150604084013561207281611f84565b809150509250925092565b60006020828403121561208f57600080fd5b5035919050565b600080604083850312156120a957600080fd5b82356120b481611f84565b946020939093013593505050565b60008060008385036101008112156120d957600080fd5b843567ffffffffffffffff8111156120f057600080fd5b85016080818803121561210257600080fd5b935060c07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08201121561213457600080fd5b5060208401915060e084013561207281611f84565b60006020828403121561215b57600080fd5b8135611d2f81611f84565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b600381106121cc577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b9052565b6020810161193c8284612195565b600080600080608085870312156121f457600080fd5b84356121ff81611f84565b9350602085013561220f81611f84565b9250604085013561221f81611f84565b9396929550929360600135925050565b6040810161223d8285612195565b611d2f6020830184612195565b60006020828403121561225c57600080fd5b5051919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036122c3576122c3612263565b5060010190565b6000816122d9576122d9612263565b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190565b8051611fa481611f84565b805167ffffffffffffffff81168114611fa457600080fd5b63ffffffff81168114610c1e57600080fd5b8051611fa481612322565b600060e0828403121561235157600080fd5b60405160e0810181811067ffffffffffffffff8211171561239b577f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040526123a7836122ff565b81526123b56020840161230a565b60208201526123c660408401612334565b60408201526123d7606084016122ff565b60608201526123e86080840161230a565b60808201526123f960a08401612334565b60a082015260c083015160c08201528091505092915050565b6000806040838503121561242557600080fd5b61242e8361230a565b915061243c6020840161230a565b90509250929050565b808202811582820484141761193c5761193c612263565b60006020828403121561246e57600080fd5b8135611d2f81612322565b80357fffffffff0000000000000000000000000000000000000000000000000000000081168114611fa457600080fd5b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126124de57600080fd5b830160208101925035905067ffffffffffffffff8111156124fe57600080fd5b80360382131561250d57600080fd5b9250929050565b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b80357fffffffffffffffffffffffffffffffffffffffff00000000000000000000000081168114611fa457600080fd5b60006101008083527fffffffff00000000000000000000000000000000000000000000000000000000806125c088612479565b16828501526125d260208801886124a9565b925060806101208601526125eb61018086018483612514565b9250506125fb60408801886124a9565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0086850301610140870152612631848284612514565b935050508061264260608901612479565b16610160850152509050833561265781612322565b63ffffffff811660208401525060208401357fffffffffffffffff000000000000000000000000000000000000000000000000811680821461269857600080fd5b80604085015250507fffffffffffffffffffffffffffffffffffffffff0000000000000000000000006126cd6040860161255d565b1660608301526126df6060850161255d565b7fffffffffffffffffffffffffffffffffffffffff000000000000000000000000811660808401525061271460808501612479565b7fffffffff00000000000000000000000000000000000000000000000000000000811660a08401525061274960a08501611f99565b6001600160a01b031660c083015260e09091019190915292915050565b67ffffffffffffffff82811682821603908082111561278757612787612263565b5092915050565b6000602082840312156127a057600080fd5b8151611d2f81612322565b6000826127e1577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b600080600080608085870312156127fc57600080fd5b6128058561230a565b93506128136020860161230a565b92506128216040860161230a565b9150606085015161283181612322565b939692955090935050565b8181038181111561193c5761193c612263565b60006020828403121561286157600080fd5b81518015158114611d2f57600080fd5b60006020828403121561288357600080fd5b611d2f82612479565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126128c157600080fd5b83018035915067ffffffffffffffff8211156128dc57600080fd5b60200191503681900382131561250d57600080fd5b60007fffffffff000000000000000000000000000000000000000000000000000000008089168352868860048501378683016004810160008152868882375093169390920160048101939093525050600801949350505050565b6000825160005b8181101561296c5760208186018101518583015201612952565b50600092019182525091905056fea26469706673582212202b07a67219d0cec9709c40bf7d3beae9fcfb64dce5e53ae2c841fc4b5e478d9e64736f6c63430008180033

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106101a35760003560e01c80638da5cb5b116100ee578063d80687ef11610097578063e30c397811610071578063e30c39781461033d578063e5d3d71414610345578063e78cea9214610358578063f2fde38b1461036b57600080fd5b8063d80687ef14610301578063dab1b4bd14610321578063df4d46631461032a57600080fd5b8063c7ba0347116100c8578063c7ba0347146102d9578063cf756fdf146102e5578063d252bb2c146102f857600080fd5b80638da5cb5b1461028e578063908d272b14610296578063941b1f94146102a957600080fd5b806362fe53e111610150578063715018a61161012a578063715018a61461026b57806374ca12791461027357806379ba50971461028657600080fd5b806362fe53e11461023257806367a68320146102455780636f64aca21461025857600080fd5b8063529d15cc11610181578063529d15cc146102005780635febd8eb14610217578063619121741461021f57600080fd5b80630f36403a146101a857806324f90de9146101d8578063427f9568146101ed575b600080fd5b6001546101bb906001600160a01b031681565b6040516001600160a01b0390911681526020015b60405180910390f35b6101eb6101e6366004611fa9565b61037e565b005b6101eb6101fb366004611fd9565b610511565b61020960955481565b6040519081526020016101cf565b610209601481565b6101eb61022d36600461203b565b61067f565b6101eb61024036600461207d565b6107a3565b6101eb610253366004612096565b610820565b6101eb6102663660046120c2565b61099c565b6101eb610afa565b6101eb610281366004612149565b610b0e565b6101eb610bc0565b6101bb610c21565b6101eb6102a4366004612096565b610c56565b6102cc6102b736600461207d565b60326020526000908152604090205460ff1681565b6040516101cf91906121d0565b6102096402540be40081565b6101eb6102f33660046121de565b610d36565b61020960635481565b61020961030f366004612149565b60646020526000908152604090205481565b61020960335481565b6101eb610338366004611fa9565b610ec8565b6101bb610f07565b6031546101bb906001600160a01b031681565b6000546101bb906001600160a01b031681565b6101eb610379366004612149565b610f30565b600160008381526032602052604090205460ff1660028111156103a3576103a3612166565b146103fa57600082815260326020526040908190205490517fed33029f0000000000000000000000000000000000000000000000000000000081526103f19160ff169060019060040161222f565b60405180910390fd5b600082815260326020526040812080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166002179055808061043c85610fcd565b604080516001600160a01b03891660208201529396509194509250600091016040516020818303038152906040528051906020012090508181146104b6576040517fd961e24c00000000000000000000000000000000000000000000000000000000815260048101829052602481018390526044016103f1565b604080518581526020810185905287917fa81d3c9594b1f3363bfc07d9277c4624e0da8dae3b42d466f1edc0718c62ab53910160405180910390a26031546105099086906001600160a01b03168561124d565b505050505050565b6105196112ad565b6031546040517fdd62ed3e00000000000000000000000000000000000000000000000000000000815233600482015230602482015287916001600160a01b03169063dd62ed3e90604401602060405180830381865afa158015610580573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105a4919061224a565b101561064c576031546040517fd505accf000000000000000000000000000000000000000000000000000000008152336004820152306024820152604481018890526064810186905260ff8516608482015260a4810184905260c481018390526001600160a01b039091169063d505accf9060e401600060405180830381600087803b15801561063357600080fd5b505af1158015610647573d6000803e3d6000fd5b505050505b610656868661132e565b61050960017f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f0055565b6106876112ad565b6001600160a01b038316600090815260646020526040812054908190036106da576040517f881b8d7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b03821661071a576040517f478b9dda00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80831015610754576040517f542f23f600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61075f82858561124d565b6107746001600160a01b0385163330866113fc565b5061079e60017f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f0055565b505050565b6107ab61148a565b806000036107e5576040517f4b81b6b900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60338190556040518181527fe64dbc80c2152cea46e3b80ba80f3e8c125114dc79194e9c947b480cfc80e59c9060200160405180910390a150565b61082861148a565b6001600160a01b038216610868576040517f09efa31000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b806000036108a2576040517f47fba16a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b038216600090815260646020526040902054156108f2576040517f98f1758300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60146063541061092e576040517f17dfdea400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6063805490600061093e83612292565b90915550506001600160a01b03821660008181526064602052604090819020839055517ff17d094161c4f2776fc9caa30094c8ebe1b86cd6f2108db5d9f1d46d8f85494c906109909084815260200190565b60405180910390a25050565b6001600160a01b0381166109dc576040517fb9c50bfb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b604080516001600160a01b0383166020820152600091610a16918691869101604051602081830303815290604052805190602001206114d5565b5090506000808281526032602052604090205460ff166002811115610a3d57610a3d612166565b14610a8a576000818152603260205260408082205490517fed33029f0000000000000000000000000000000000000000000000000000000081526103f19260ff909216919060040161222f565b60008181526032602052604080822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055516001600160a01b0384169183917f2fbc945bad45e66509bad2bda7b97993796881f9ac2543b827d2aaf69f1869239190a350505050565b610b0261148a565b610b0c60006116b3565b565b610b1661148a565b6001600160a01b0381166000908152606460205260408120549003610b67576040517f881b8d7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60638054906000610b77836122ca565b90915550506001600160a01b038116600081815260646020526040808220829055517f9c4edffd5782d54d432f513a2a7d944aac6f743c7ef4a83d8c6189ba21dd42999190a250565b3380610bca610f07565b6001600160a01b031614610c15576040517f118cdaa70000000000000000000000000000000000000000000000000000000081526001600160a01b03821660048201526024016103f1565b610c1e816116b3565b50565b6000807f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c1993005b546001600160a01b031692915050565b610c5e61148a565b6001600160a01b0382166000908152606460205260408120549003610caf576040517f881b8d7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80600003610ce9576040517f47fba16a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b03821660008181526064602052604090819020839055517f886950a2d9ce5c7d214261968375335366c8547e3e5eb5e1744c3cb581c4a672906109909084815260200190565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000810460ff16159067ffffffffffffffff16600081158015610d815750825b905060008267ffffffffffffffff166001148015610d9e5750303b155b905081158015610dac575080155b15610de3576040517ff92ee8a900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b84547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001660011785558315610e445784547fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff16680100000000000000001785555b610e4f898989611703565b610e576117a5565b60958690558315610ebd5784547fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b505050505050505050565b610ed06112ad565b610eda828261132e565b610f0360017f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f0055565b5050565b6000807f237e158222e3e6968b72b9db0d8043aacf074ad9f650f0d1606b4d82ee432c00610c46565b610f3861148a565b7f237e158222e3e6968b72b9db0d8043aacf074ad9f650f0d1606b4d82ee432c0080547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383169081178255610f94610c21565b6001600160a01b03167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270060405160405180910390a35050565b600080546040517fb02c43d0000000000000000000000000000000000000000000000000000000008152600481018490528291829182916001600160a01b03169063b02c43d09060240160e060405180830381865afa158015611034573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611058919061233f565b9050806040015163ffffffff166000036110ce576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f4465706f736974206e6f7420696e697469616c697a656400000000000000000060448201526064016103f1565b6001546040517f6c626aa4000000000000000000000000000000000000000000000000000000008152600481018790526000916001600160a01b031690636c626aa49060240160408051808303816000875af1158015611132573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111569190612412565b9150508160a0015163ffffffff16600014158061117c575067ffffffffffffffff811615155b611208576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602360248201527f4465706f736974206e6f742066696e616c697a6564206279207468652062726960448201527f646765000000000000000000000000000000000000000000000000000000000060648201526084016103f1565b6402540be400826020015167ffffffffffffffff166112279190612445565b945061123b826020015183608001516117bd565b93508160c00151925050509193909250565b816001600160a01b0316836001600160a01b031660956000815461127090612292565b91829055506040518481527f75aa5616721471b8ab0c49ce59500cbad2b7ef1ad10e5eb9449c693c0a5c8fd19060200160405180910390a4505050565b7f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f0080547ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01611328576040517f3ee5aeb500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60029055565b6001600160a01b03811661136e576040517fb9c50bfb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6033548210156113aa576040517f0b84d72c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6031546001600160a01b03166113c182828561124d565b61079e6001600160a01b0382163330866113fc565b60017f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f0055565b604080516001600160a01b0385811660248301528416604482015260648082018490528251808303909101815260849091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f23b872dd00000000000000000000000000000000000000000000000000000000179052611484908590611942565b50505050565b33611493610c21565b6001600160a01b031614610b0c576040517f118cdaa70000000000000000000000000000000000000000000000000000000081523360048201526024016103f1565b60015460009081906001600160a01b03166114f660c0860160a08701612149565b6001600160a01b031614611566576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f5661756c742061646472657373206d69736d617463680000000000000000000060448201526064016103f1565b611584611572866119be565b61157f602087018761245c565b611a23565b6000546040517f86f014390000000000000000000000000000000000000000000000000000000081529193506001600160a01b0316906386f01439906115d29088908890889060040161258d565b600060405180830381600087803b1580156115ec57600080fd5b505af1158015611600573d6000803e3d6000fd5b50506000546040517fb02c43d0000000000000000000000000000000000000000000000000000000008152600481018690526402540be40093506001600160a01b03909116915063b02c43d09060240160e060405180830381865afa15801561166d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611691919061233f565b6020015167ffffffffffffffff166116a99190612445565b9050935093915050565b7f237e158222e3e6968b72b9db0d8043aacf074ad9f650f0d1606b4d82ee432c0080547fffffffffffffffffffffffff0000000000000000000000000000000000000000168155610f0382611aa3565b61170d8383611b2c565b61171633611d00565b61171e611d11565b6001600160a01b03811661175e576040517fe747bdc200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b603180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b03929092169190911790555050662386f26fc10000603355565b6117ae33611d00565b6117b6611d11565b6000606355565b6000806402540be4006117d08486612766565b67ffffffffffffffff166117e49190612445565b90506000600160009054906101000a90046001600160a01b03166001600160a01b03166309b53f516040518163ffffffff1660e01b8152600401602060405180830381865afa15801561183b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061185f919061278e565b63ffffffff1690506000808211611877576000611881565b61188182846127ab565b905060008060009054906101000a90046001600160a01b03166001600160a01b031663c42b64d06040518163ffffffff1660e01b8152600401608060405180830381865afa1580156118d7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118fb91906127e6565b509250505060006402540be4008267ffffffffffffffff1661191d9190612445565b90508061192a848761283c565b611934919061283c565b955050505050505b92915050565b60006119576001600160a01b03841683611d21565b9050805160001415801561197c57508080602001905181019061197a919061284f565b155b1561079e576040517f5274afe70000000000000000000000000000000000000000000000000000000081526001600160a01b03841660048201526024016103f1565b600061193c6119d06020840184612871565b6119dd602085018561288c565b6119ea604087018761288c565b6119fa6080890160608a01612871565b604051602001611a0f969594939291906128f1565b604051602081830303815290604052611d36565b60008282604051602001611a6692919091825260e01b7fffffffff0000000000000000000000000000000000000000000000000000000016602082015260240190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815291905280516020909101209392505050565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080547fffffffffffffffffffffffff000000000000000000000000000000000000000081166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3505050565b6000546001600160a01b0316158015611b4e57506001546001600160a01b0316155b611bda576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602960248201527f4162737472616374544254434465706f7369746f7220616c726561647920696e60448201527f697469616c697a6564000000000000000000000000000000000000000000000060648201526084016103f1565b6001600160a01b038216611c4a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f42726964676520616464726573732063616e6e6f74206265207a65726f00000060448201526064016103f1565b6001600160a01b038116611cba576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f544254435661756c7420616464726573732063616e6e6f74206265207a65726f60448201526064016103f1565b600080546001600160a01b039384167fffffffffffffffffffffffff00000000000000000000000000000000000000009182161790915560018054929093169116179055565b611d08611d5d565b610c1e81611dc4565b611d19611d5d565b610b0c611e0f565b6060611d2f83836000611e17565b9392505050565b60006020600083516020850160025afa50602060006020600060025afa5050600051919050565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a005468010000000000000000900460ff16610b0c576040517fd7e6bcf800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611dcc611d5d565b6001600160a01b038116610c15576040517f1e4fbdf7000000000000000000000000000000000000000000000000000000008152600060048201526024016103f1565b6113d6611d5d565b606081471015611e55576040517fcd7860590000000000000000000000000000000000000000000000000000000081523060048201526024016103f1565b600080856001600160a01b03168486604051611e71919061294b565b60006040518083038185875af1925050503d8060008114611eae576040519150601f19603f3d011682016040523d82523d6000602084013e611eb3565b606091505b5091509150611ec3868383611ecd565b9695505050505050565b606082611ee257611edd82611f42565b611d2f565b8151158015611ef957506001600160a01b0384163b155b15611f3b576040517f9996b3150000000000000000000000000000000000000000000000000000000081526001600160a01b03851660048201526024016103f1565b5080611d2f565b805115611f525780518082602001fd5b6040517f1425ea4200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b0381168114610c1e57600080fd5b8035611fa481611f84565b919050565b60008060408385031215611fbc57600080fd5b823591506020830135611fce81611f84565b809150509250929050565b60008060008060008060c08789031215611ff257600080fd5b86359550602087013561200481611f84565b945060408701359350606087013560ff8116811461202157600080fd5b9598949750929560808101359460a0909101359350915050565b60008060006060848603121561205057600080fd5b833561205b81611f84565b925060208401359150604084013561207281611f84565b809150509250925092565b60006020828403121561208f57600080fd5b5035919050565b600080604083850312156120a957600080fd5b82356120b481611f84565b946020939093013593505050565b60008060008385036101008112156120d957600080fd5b843567ffffffffffffffff8111156120f057600080fd5b85016080818803121561210257600080fd5b935060c07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08201121561213457600080fd5b5060208401915060e084013561207281611f84565b60006020828403121561215b57600080fd5b8135611d2f81611f84565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b600381106121cc577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b9052565b6020810161193c8284612195565b600080600080608085870312156121f457600080fd5b84356121ff81611f84565b9350602085013561220f81611f84565b9250604085013561221f81611f84565b9396929550929360600135925050565b6040810161223d8285612195565b611d2f6020830184612195565b60006020828403121561225c57600080fd5b5051919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036122c3576122c3612263565b5060010190565b6000816122d9576122d9612263565b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190565b8051611fa481611f84565b805167ffffffffffffffff81168114611fa457600080fd5b63ffffffff81168114610c1e57600080fd5b8051611fa481612322565b600060e0828403121561235157600080fd5b60405160e0810181811067ffffffffffffffff8211171561239b577f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040526123a7836122ff565b81526123b56020840161230a565b60208201526123c660408401612334565b60408201526123d7606084016122ff565b60608201526123e86080840161230a565b60808201526123f960a08401612334565b60a082015260c083015160c08201528091505092915050565b6000806040838503121561242557600080fd5b61242e8361230a565b915061243c6020840161230a565b90509250929050565b808202811582820484141761193c5761193c612263565b60006020828403121561246e57600080fd5b8135611d2f81612322565b80357fffffffff0000000000000000000000000000000000000000000000000000000081168114611fa457600080fd5b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126124de57600080fd5b830160208101925035905067ffffffffffffffff8111156124fe57600080fd5b80360382131561250d57600080fd5b9250929050565b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b80357fffffffffffffffffffffffffffffffffffffffff00000000000000000000000081168114611fa457600080fd5b60006101008083527fffffffff00000000000000000000000000000000000000000000000000000000806125c088612479565b16828501526125d260208801886124a9565b925060806101208601526125eb61018086018483612514565b9250506125fb60408801886124a9565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0086850301610140870152612631848284612514565b935050508061264260608901612479565b16610160850152509050833561265781612322565b63ffffffff811660208401525060208401357fffffffffffffffff000000000000000000000000000000000000000000000000811680821461269857600080fd5b80604085015250507fffffffffffffffffffffffffffffffffffffffff0000000000000000000000006126cd6040860161255d565b1660608301526126df6060850161255d565b7fffffffffffffffffffffffffffffffffffffffff000000000000000000000000811660808401525061271460808501612479565b7fffffffff00000000000000000000000000000000000000000000000000000000811660a08401525061274960a08501611f99565b6001600160a01b031660c083015260e09091019190915292915050565b67ffffffffffffffff82811682821603908082111561278757612787612263565b5092915050565b6000602082840312156127a057600080fd5b8151611d2f81612322565b6000826127e1577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b600080600080608085870312156127fc57600080fd5b6128058561230a565b93506128136020860161230a565b92506128216040860161230a565b9150606085015161283181612322565b939692955090935050565b8181038181111561193c5761193c612263565b60006020828403121561286157600080fd5b81518015158114611d2f57600080fd5b60006020828403121561288357600080fd5b611d2f82612479565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126128c157600080fd5b83018035915067ffffffffffffffff8211156128dc57600080fd5b60200191503681900382131561250d57600080fd5b60007fffffffff000000000000000000000000000000000000000000000000000000008089168352868860048501378683016004810160008152868882375093169390920160048101939093525050600801949350505050565b6000825160005b8181101561296c5760208186018101518583015201612952565b50600092019182525091905056fea26469706673582212202b07a67219d0cec9709c40bf7d3beae9fcfb64dce5e53ae2c841fc4b5e478d9e64736f6c63430008180033

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

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.