Mumbai Testnet

Contract

0x79a138c9C6eb56A0dE28990bA92e3a568f372eC3

Overview

MATIC Balance

Multichain Info

N/A
Transaction Hash
Method
Block
From
To
Value
Cc Transfer379635422023-07-15 13:06:39372 days ago1689426399IN
0x79a138c9...68f372eC3
0.00061425 MATIC0.000767371.50000001
Cc Transfer379612722023-07-15 11:41:00372 days ago1689421260IN
0x79a138c9...68f372eC3
0.00063574 MATIC0.000768781.50000001
Cc Transfer378527792023-07-12 15:02:23375 days ago1689174143IN
0x79a138c9...68f372eC3
0.00061425 MATIC0.000792621.50000001
Cc Transfer377433962023-07-09 18:32:51378 days ago1688927571IN
0x79a138c9...68f372eC3
0.00102375 MATIC0.001230342.42500001
Cc Transfer372626602023-06-25 14:46:13392 days ago1687704373IN
0x79a138c9...68f372eC3
0.00061425 MATIC0.000797221.50000001
Cc Transfer372596122023-06-25 12:44:05392 days ago1687697045IN
0x79a138c9...68f372eC3
0.00115589 MATIC0.001534663
Cc Transfer372595232023-06-25 12:40:57392 days ago1687696857IN
0x79a138c9...68f372eC3
0.00119164 MATIC0.001285992.5
Cc Transfer368371512023-06-14 11:58:33403 days ago1686743913IN
0x79a138c9...68f372eC3
0.00061425 MATIC0.000830581.50000001
Set Relay367420102023-06-12 3:11:04406 days ago1686539464IN
0x79a138c9...68f372eC3
0 MATIC0.000045851.50000001
Set Relay367419592023-06-12 3:09:16406 days ago1686539356IN
0x79a138c9...68f372eC3
0 MATIC0.000045851.50000001
Cc Transfer365490382023-06-07 8:56:17411 days ago1686128177IN
0x79a138c9...68f372eC3
0.00061425 MATIC0.001212082.35225002
Cc Transfer359094522023-05-22 13:59:23426 days ago1684763963IN
0x79a138c9...68f372eC3
0.00061425 MATIC0.000770111.50000001
Cc Transfer359094502023-05-22 13:59:19426 days ago1684763959IN
0x79a138c9...68f372eC3
0.00061425 MATIC0.00077011.50000001
Cc Transfer359094482023-05-22 13:59:15426 days ago1684763955IN
0x79a138c9...68f372eC3
0.00061425 MATIC0.000825681.50000001
Set Relay349169412023-04-28 3:42:19451 days ago1682653339IN
0x79a138c9...68f372eC3
0 MATIC0.000055721.82267674
Cc Transfer345367742023-04-18 19:17:44460 days ago1681845464IN
0x79a138c9...68f372eC3
0.00102375 MATIC0.001279662.50000001
Cc Transfer345325332023-04-18 16:47:32460 days ago1681836452IN
0x79a138c9...68f372eC3
0.00100327 MATIC0.001335432.42500001
Cc Transfer329331352023-03-10 8:36:50500 days ago1678437410IN
0x79a138c9...68f372eC3
0.00131597 MATIC0.001279472.50000001
Cc Transfer329331332023-03-10 8:36:46500 days ago1678437406IN
0x79a138c9...68f372eC3
0.00131597 MATIC0.001279632.50000001
Cc Transfer329313152023-03-10 7:32:24500 days ago1678433544IN
0x79a138c9...68f372eC3
0.00092787 MATIC0.001289492.42500003
Cc Transfer328549312023-03-08 10:27:07501 days ago1678271227IN
0x79a138c9...68f372eC3
0.00061425 MATIC0.000767691.50000001
Cc Transfer328544692023-03-08 10:10:45502 days ago1678270245IN
0x79a138c9...68f372eC3
0.00061425 MATIC0.000770511.50000001
Cc Transfer328544672023-03-08 10:10:41502 days ago1678270241IN
0x79a138c9...68f372eC3
0.00061425 MATIC0.000800451.50000001
Cc Transfer327347372023-03-05 11:29:48504 days ago1678015788IN
0x79a138c9...68f372eC3
0.00102375 MATIC0.001199482.17646008
Cc Transfer320750462023-02-15 15:17:33522 days ago1676474253IN
0x79a138c9...68f372eC3
0.00105957 MATIC0.001257972.36593541
View all transactions

Latest 25 internal transactions (View All)

Parent Transaction Hash Block From To Value
379635422023-07-15 13:06:39372 days ago1689426399
0x79a138c9...68f372eC3
0.00014175 MATIC
379635422023-07-15 13:06:39372 days ago1689426399
0x79a138c9...68f372eC3
0.0004725 MATIC
379612722023-07-15 11:41:00372 days ago1689421260
0x79a138c9...68f372eC3
0.0001467 MATIC
379612722023-07-15 11:41:00372 days ago1689421260
0x79a138c9...68f372eC3
0.00048903 MATIC
378527792023-07-12 15:02:23375 days ago1689174143
0x79a138c9...68f372eC3
0.00014175 MATIC
378527792023-07-12 15:02:23375 days ago1689174143
0x79a138c9...68f372eC3
0.0004725 MATIC
377433962023-07-09 18:32:51378 days ago1688927571
0x79a138c9...68f372eC3
0.00023625 MATIC
377433962023-07-09 18:32:51378 days ago1688927571
0x79a138c9...68f372eC3
0.0007875 MATIC
372626602023-06-25 14:46:13392 days ago1687704373
0x79a138c9...68f372eC3
0.00014175 MATIC
372626602023-06-25 14:46:13392 days ago1687704373
0x79a138c9...68f372eC3
0.0004725 MATIC
372596122023-06-25 12:44:05392 days ago1687697045
0x79a138c9...68f372eC3
0.00026674 MATIC
372596122023-06-25 12:44:05392 days ago1687697045
0x79a138c9...68f372eC3
0.00088915 MATIC
372595232023-06-25 12:40:57392 days ago1687696857
0x79a138c9...68f372eC3
0.00027499 MATIC
372595232023-06-25 12:40:57392 days ago1687696857
0x79a138c9...68f372eC3
0.00091665 MATIC
368371512023-06-14 11:58:33403 days ago1686743913
0x79a138c9...68f372eC3
0.00014175 MATIC
368371512023-06-14 11:58:33403 days ago1686743913
0x79a138c9...68f372eC3
0.0004725 MATIC
365490382023-06-07 8:56:17411 days ago1686128177
0x79a138c9...68f372eC3
0.00014175 MATIC
365490382023-06-07 8:56:17411 days ago1686128177
0x79a138c9...68f372eC3
0.0004725 MATIC
359094522023-05-22 13:59:23426 days ago1684763963
0x79a138c9...68f372eC3
0.00014175 MATIC
359094522023-05-22 13:59:23426 days ago1684763963
0x79a138c9...68f372eC3
0.0004725 MATIC
359094502023-05-22 13:59:19426 days ago1684763959
0x79a138c9...68f372eC3
0.00014175 MATIC
359094502023-05-22 13:59:19426 days ago1684763959
0x79a138c9...68f372eC3
0.0004725 MATIC
359094482023-05-22 13:59:15426 days ago1684763955
0x79a138c9...68f372eC3
0.00014175 MATIC
359094482023-05-22 13:59:15426 days ago1684763955
0x79a138c9...68f372eC3
0.0004725 MATIC
345367742023-04-18 19:17:44460 days ago1681845464
0x79a138c9...68f372eC3
0.00023625 MATIC
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
CCTransferRouter

Compiler Version
v0.8.2+commit.661d1103

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion

Contract Source Code (Solidity Standard Json-Input format)

File 1 of 18 : CCTransferRouter.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0 <0.8.4;

import "../libraries/RequestHelper.sol";
import "../libraries/BitcoinHelper.sol";
import "./interfaces/ICCTransferRouter.sol";
import "../erc20/interfaces/ITeleBTC.sol";
import "../relay/interfaces/IBitcoinRelay.sol";
import "./interfaces/IInstantRouter.sol";
import "../lockers/interfaces/ILockers.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";

contract CCTransferRouter is ICCTransferRouter, Ownable, ReentrancyGuard {

    modifier nonZeroAddress(address _address) {
        require(_address != address(0), "CCTransferRouter: address is zero");
        _;
    }

    // Constants
    uint constant MAX_PROTOCOL_FEE = 10000;

    // Public variables
    uint public override startingBlockNumber;
    uint public override chainId;
    uint public override appId;
    uint public override protocolPercentageFee; // A number between 0 to 10000
    address public override relay;
    address public override lockers;
    address public override teleBTC;
    address public override instantRouter;
    address public override treasury;
    mapping(bytes32 => ccTransferRequest) public ccTransferRequests; // TxId to CCTransferRequest structure

    /// @notice                             Gives default params to initiate cc transfer router
    /// @param _startingBlockNumber         Requests that are included in a block older than _startingBlockNumber cannot be executed
    /// @param _protocolPercentageFee       Percentage amount of protocol fee (min: %0.01)
    /// @param _chainId                     Id of the underlying chain
    /// @param _appId                       Id of ccTransfer dApp
    /// @param _relay                       The Relay address to validate data from source chain
    /// @param _lockers                     Lockers' contract address
    /// @param _teleBTC                     TeleportDAO BTC ERC20 token address
    /// @param _treasury                    Address of treasury that collects protocol fees
    constructor(
        uint _startingBlockNumber,
        uint _protocolPercentageFee,
        uint _chainId,
        uint _appId,
        address _relay,
        address _lockers,
        address _teleBTC,
        address _treasury
    ) {
        startingBlockNumber = _startingBlockNumber;
        chainId = _chainId;
        appId = _appId;
        _setProtocolPercentageFee(_protocolPercentageFee);
        _setRelay(_relay);
        _setLockers(_lockers);
        _setTeleBTC(_teleBTC);
        _setTreasury(_treasury);
    }

    function renounceOwnership() public virtual override onlyOwner {}

    /// @notice                             Setter for protocol percentage fee
    /// @dev                                Only owner can call this
    /// @param _protocolPercentageFee       Percentage amount of protocol fee
    function setProtocolPercentageFee(uint _protocolPercentageFee) external override onlyOwner {
        _setProtocolPercentageFee(_protocolPercentageFee);
    }

    /// @notice                             Setter for relay
    /// @dev                                Only owner can call this
    /// @param _relay                       Address of the relay contract
    function setRelay(address _relay) external override nonZeroAddress(_relay) onlyOwner {
        _setRelay(_relay);
    }

    /// @notice                             Setter for lockers
    /// @dev                                Only owner can call this
    /// @param _lockers                     Address of the lockers contract
    function setLockers(address _lockers) external override nonZeroAddress(_lockers) onlyOwner {
        _setLockers(_lockers);
    }

    /// @notice                             Setter for instant router
    /// @dev                                Only owner can call this
    /// @param _instantRouter               Address of the instant router contract
    function setInstantRouter(address _instantRouter) external override nonZeroAddress(_instantRouter) onlyOwner {
        _setInstantRouter(_instantRouter);
    }

    /// @notice                             Setter for teleBTC
    /// @dev                                Only owner can call this
    /// @param _teleBTC                     TeleportDAO BTC ERC20 token address
    function setTeleBTC(address _teleBTC) external override nonZeroAddress(_teleBTC) onlyOwner {
        _setTeleBTC(_teleBTC);
    }

    /// @notice                             Setter for treasury
    /// @dev                                Only owner can call this
    /// @param _treasury                    Treasury address
    function setTreasury(address _treasury) external override nonZeroAddress(_treasury) onlyOwner {
        _setTreasury(_treasury);
    }

    /// @notice                             Internal setter for protocol percentage fee
    /// @param _protocolPercentageFee       Percentage amount of protocol fee
    function _setProtocolPercentageFee(uint _protocolPercentageFee) private {
        require(
            MAX_PROTOCOL_FEE >= _protocolPercentageFee,
            "CCTransferRouter: protocol fee is out of range"
        );
        emit NewProtocolPercentageFee(protocolPercentageFee, _protocolPercentageFee);
        protocolPercentageFee = _protocolPercentageFee;
    }

    /// @notice                             Internal setter for relay
    /// @param _relay                       Address of the relay contract
    function _setRelay(address _relay) private nonZeroAddress(_relay) {
        emit NewRelay(relay, _relay);
        relay = _relay;
    }

    /// @notice                             Internal setter for relay
    /// @param _lockers                     Address of the lockers contract
    function _setLockers(address _lockers) private nonZeroAddress(_lockers) {
        emit NewLockers(lockers, _lockers);
        lockers = _lockers;
    }

    /// @notice                             Internal setter for instant router
    /// @param _instantRouter               Address of the instant router contract
    function _setInstantRouter(address _instantRouter) private nonZeroAddress(_instantRouter) {
        emit NewInstantRouter(instantRouter, _instantRouter);
        instantRouter = _instantRouter;
    }

    /// @notice                             Internal setter for teleBTC
    /// @param _teleBTC                     TeleportDAO BTC ERC20 token address
    function _setTeleBTC(address _teleBTC) private nonZeroAddress(_teleBTC) {
        emit NewTeleBTC(teleBTC, _teleBTC);
        teleBTC = _teleBTC;
    }

    /// @notice                             Internal setter for treasury
    /// @param _treasury                    Treasury address
    function _setTreasury(address _treasury) private nonZeroAddress(_treasury) {
        emit NewTreasury(treasury, _treasury);
        treasury = _treasury;
    }

    /// @notice                             Check if the request has been executed before
    /// @dev                                This is to avoid re-submitting a used request
    /// @param _txId                        The txId of request on the source chain
    /// @return                             True if the request has been executed
    function isRequestUsed(bytes32 _txId) external view override returns (bool) {
        return ccTransferRequests[_txId].isUsed ? true : false;
    }

    /// @notice                             Executes the cross chain transfer request
    /// @dev                                Validates the transfer request, then,
    ///                                     if speed is 1, the request is instant
    ///                                     which pays back the loan,
    ///                                     if the speed is 0, it is a normal transfer
    /// @param _version                     Version of the Bitcoin transaction
    /// @param _vin                         Transaction inputs
    /// @param _vout                        Transaction outputs
    /// @param _locktime                    Bitcoin transaction locktime
    /// @param _blockNumber                 The block number of the request tx
    /// @param _intermediateNodes           Merkle proof for tx
    /// @param _index                       Index of tx in the block
    /// @param _lockerLockingScript         Locking script of locker that user has sent BTC to it
    /// @return                             True if the transfer is successful
    function ccTransfer(
        // Bitcoin tx
        bytes4 _version,
        bytes memory _vin,
        bytes calldata _vout,
        bytes4 _locktime,
        // Bitcoin block number
        uint256 _blockNumber,
        // Merkle proof
        bytes calldata _intermediateNodes,
        uint _index,
        bytes calldata _lockerLockingScript
    ) external payable nonReentrant override returns (bool) {
        require(_blockNumber >= startingBlockNumber, "CCTransferRouter: request is too old");

        // Finds txId on the source chain
        bytes32 txId = BitcoinHelper.calculateTxId(_version, _vin, _vout, _locktime);
        
        require(
            !ccTransferRequests[txId].isUsed,
            "CCTransferRouter: request has been used before"
        );

        require(_locktime == bytes4(0), "CCTransferRouter: lock time is non -zero");

        // Extracts information from the request
        _saveCCTransferRequest(_lockerLockingScript, _vout, txId);

        // Checks if tx has been confirmed on source chain
        require(
            _isConfirmed(
                txId,
                _blockNumber,
                _intermediateNodes,
                _index
            ),
            "CCTransferRouter: transaction has not been finalized yet"
        );

        // Normal cc transfer request
        if (ccTransferRequests[txId].speed == 0) {
            (uint receivedAmount, uint _protocolFee, uint _teleporterFee) = _sendTeleBTC(_lockerLockingScript, txId);
            emit CCTransfer(
                _lockerLockingScript,
                0,
                ILockers(lockers).getLockerTargetAddress(_lockerLockingScript),
                ccTransferRequests[txId].recipientAddress,
                ccTransferRequests[txId].inputAmount,
                receivedAmount,
                ccTransferRequests[txId].speed,
                _msgSender(),
                _teleporterFee,
                0,
                _protocolFee,
                txId
            );
            return true;
        } else {
            // Pays back instant loan (ccTransferRequests[txId].speed == 1)
            (uint receivedAmount, uint _protocolFee, uint _teleporterFee) = _payBackInstantLoan(_lockerLockingScript, txId);
            emit CCTransfer(
                _lockerLockingScript,
                0,
                ILockers(lockers).getLockerTargetAddress(_lockerLockingScript),
                ccTransferRequests[txId].recipientAddress,
                ccTransferRequests[txId].inputAmount,
                receivedAmount,
                ccTransferRequests[txId].speed,
                _msgSender(),
                _teleporterFee,
                0,
                _protocolFee,
                txId
            );
            return true;
        }
    }

    /// @notice                             Sends minted teleBTC to the user
    /// @param _lockerLockingScript         Locker's locking script
    /// @param _txId                        The transaction ID of the request
    /// @return _remainedAmount             Amount of teleBTC that user receives after reducing fees
    function _sendTeleBTC(bytes memory _lockerLockingScript, bytes32 _txId) private returns (
        uint _remainedAmount,
        uint _protocolFee, 
        uint _teleporterFee
    ) {
        // Gets remained amount after reducing fees
        (_remainedAmount, _protocolFee, _teleporterFee) = _mintAndReduceFees(_lockerLockingScript, _txId);

        // Transfers rest of tokens to recipient
        ITeleBTC(teleBTC).transfer(
            ccTransferRequests[_txId].recipientAddress,
            _remainedAmount
        );
    }

    /// @notice                             Executes the paying back instant loan request
    /// @param _lockerLockingScript         Locker's locking script
    /// @param _txId                        The transaction ID of the request
    /// @return _remainedAmount             Amount of teleBTC that user receives after reducing fees
    function _payBackInstantLoan(
        bytes memory _lockerLockingScript, 
        bytes32 _txId
    ) private returns (uint _remainedAmount, uint _protocolFee, uint _teleporterFee) {

        // Gets remained amount after reducing fees
        (_remainedAmount, _protocolFee, _teleporterFee) = _mintAndReduceFees(_lockerLockingScript, _txId);

        // Gives allowance to instant router to transfer remained teleBTC
        ITeleBTC(teleBTC).approve(
            instantRouter,
            _remainedAmount
        );

        // Pays back instant loan
        IInstantRouter(instantRouter).payBackLoan(
            ccTransferRequests[_txId].recipientAddress,
            _remainedAmount
        );
    }

    /// @notice                             Parses and saves the request
    /// @dev                                Checks that user has sent BTC to a valid locker
    /// @param _lockerLockingScript         Locker's locking script
    /// @param _vout                        The outputs of the tx
    /// @param _txId                        The txID of the request
    function _saveCCTransferRequest(
        bytes memory _lockerLockingScript,
        bytes memory _vout,
        bytes32 _txId
    ) private {

        require(
            ILockers(lockers).isLocker(_lockerLockingScript),
            "CCTransferRouter: no locker with the given locking script exists"
        );

        // Extracts value and opreturn data from request
        ccTransferRequest memory request; // Defines it to save gas
        bytes memory arbitraryData;
        
        (request.inputAmount, arbitraryData) = BitcoinHelper.parseValueAndDataHavingLockingScriptSmallPayload(
            _vout, 
            _lockerLockingScript
        );

        // Checks that input amount is not zero
        require(request.inputAmount > 0, "CCTransferRouter: input amount is zero");

        // Checks chain id and app id
        require(RequestHelper.parseChainId(arbitraryData) == chainId, "CCTransferRouter: chain id is not correct");
        require(RequestHelper.parseAppId(arbitraryData) == appId, "CCTransferRouter: app id is not correct");

        // Calculates fee
        uint percentageFee = RequestHelper.parsePercentageFee(arbitraryData);
        require(percentageFee <= MAX_PROTOCOL_FEE, "CCTransferRouter: percentage fee is out of range");
        request.fee = percentageFee*request.inputAmount/MAX_PROTOCOL_FEE;

        // Parses recipient address and request speed
        request.recipientAddress = RequestHelper.parseRecipientAddress(arbitraryData);
        request.speed = RequestHelper.parseSpeed(arbitraryData);
        require(request.speed == 0 || request.speed == 1, "CCTransferRouter: speed is out of range");

        // Marks the request as used
        request.isUsed = true;

        // Saves the request data
        ccTransferRequests[_txId] = request;
    }

    /// @notice                             Checks if tx has been finalized on source chain
    /// @dev                                Pays relay fee using included ETH in the transaction
    /// @param _txId                        The request tx
    /// @param _blockNumber                 The block number of the tx
    /// @param _intermediateNodes           Merkle proof for tx
    /// @param _index                       Index of tx in the block
    /// @return                             True if the tx is finalized on the source chain
    function _isConfirmed(
        bytes32 _txId,
        uint256 _blockNumber,
        bytes memory _intermediateNodes,
        uint _index
    ) private returns (bool) {
        // Calculates fee amount
        uint feeAmount = IBitcoinRelay(relay).getBlockHeaderFee(_blockNumber, 0); // Index 0 is for finalized blocks
        require(msg.value >= feeAmount, "CCTransferRouter: paid fee is not sufficient");

        // Calls relay contract (transfers all msg.value to it)
        bytes memory data = Address.functionCallWithValue(
            relay,
            abi.encodeWithSignature(
                "checkTxProof(bytes32,uint256,bytes,uint256)",
                _txId,
                _blockNumber,
                _intermediateNodes,
                _index
            ),
            feeAmount
        );
        
        // Sends extra ETH back to _msgSender()
        Address.sendValue(payable(_msgSender()), msg.value - feeAmount);

        return abi.decode(data, (bool));
    }

    /// @notice                       Mints teleBTC by calling lockers contract
    /// @param _lockerLockingScript   Locker's locking script
    /// @param _txId                  The transaction ID of the request
    /// @return _remainedAmount       Amount of teleBTC that user receives after reducing all fees (protocol, locker, teleporter)
    function _mintAndReduceFees(
        bytes memory _lockerLockingScript,
        bytes32 _txId
    ) private returns (uint _remainedAmount, uint _protocolFee, uint _teleporterFee) {

        // Mints teleBTC for cc transfer router
        // Lockers contract gets locker's fee
        uint mintedAmount = ILockers(lockers).mint(
            _lockerLockingScript,
            address(this),
            ccTransferRequests[_txId].inputAmount
        );

        // Calculates fees
        _protocolFee = ccTransferRequests[_txId].inputAmount*protocolPercentageFee/MAX_PROTOCOL_FEE;
        _teleporterFee = ccTransferRequests[_txId].fee;

        // Pays Teleporter fee
        if (_teleporterFee > 0) {
            ITeleBTC(teleBTC).transfer(_msgSender(), _teleporterFee);
        }

        // Pays protocol fee
        if (_protocolFee > 0) {
            ITeleBTC(teleBTC).transfer(treasury, _protocolFee);
        }

        _remainedAmount = mintedAmount - _protocolFee - _teleporterFee;
    }

    receive() external payable {}
}

File 2 of 18 : RequestHelper.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0 <0.8.4;

// A library for parsing cc transfer and cc exchange requests
library RequestHelper {

    /// @notice                     Returns chain id of the request
    /// @dev                        Determines the chain that request belongs to
    /// @param _arbitraryData       Data written in Bitcoin tx
    /// @return parsedValue         The parsed value of chain id
    function parseChainId(bytes memory _arbitraryData) internal pure returns (uint8 parsedValue) {
        bytes memory slicedBytes = sliceBytes(_arbitraryData, 0, 0);
        assembly {
            parsedValue := mload(add(slicedBytes, 1))
        }
    }

    /// @notice                     Returns app id of the request
    /// @dev                        Determines the app that request belongs to (e.g. cross-chain transfer app id is 0)
    /// @param _arbitraryData       Data written in Bitcoin tx
    /// @return parsedValue         The parsed value of app id
    function parseAppId(bytes memory _arbitraryData) internal pure returns (uint16 parsedValue) {
        bytes memory slicedBytes = sliceBytes(_arbitraryData, 1, 2);
        assembly {
            parsedValue := mload(add(slicedBytes, 2))
        }
    }

    /// @notice                     Returns recipient address
    /// @dev                        Minted TeleBTC or exchanged tokens will be sent to this address
    /// @param _arbitraryData       Data written in Bitcoin tx
    /// @return parsedValue         The parsed value of recipient address
    function parseRecipientAddress(bytes memory _arbitraryData) internal pure returns (address parsedValue) {
        bytes memory slicedBytes = sliceBytes(_arbitraryData, 3, 22);
        assembly {
            parsedValue := mload(add(slicedBytes, 20))
        }
    }

    /// @notice                     Returns percentage fee (from total minted TeleBTC)
    /// @dev                        This fee goes to Teleporter who submitted the request
    /// @param _arbitraryData       Data written in Bitcoin tx
    /// @return parsedValue         The parsed value of percentage fee
    function parsePercentageFee(bytes memory _arbitraryData) internal pure returns (uint16 parsedValue) {
        bytes memory slicedBytes = sliceBytes(_arbitraryData, 23, 24);
        assembly {
            parsedValue := mload(add(slicedBytes, 2))
        }
    }

    /// @notice                     Returns speed of request
    /// @dev                        0 for normal requests, 1 for instant requests
    ///                             Instant requests are used to pay back an instant loan
    /// @param _arbitraryData       Data written in Bitcoin tx
    /// @return parsedValue         The parsed value of speed parameter
    function parseSpeed(bytes memory _arbitraryData) internal pure returns (uint8 parsedValue) {
        bytes memory slicedBytes = sliceBytes(_arbitraryData, 25, 25);
        assembly {
            parsedValue := mload(add(slicedBytes, 1))
        }
    }

    /// @notice                     Returns address of exchange token
    /// @dev                        Minted TeleBTC will be exchanged to this token
    /// @param _arbitraryData       Data written in Bitcoin tx
    /// @return parsedValue         The parsed value of exchange token
    function parseExchangeToken(bytes memory _arbitraryData) internal pure returns (address parsedValue){
        bytes memory slicedBytes = sliceBytes(_arbitraryData, 26, 45);
        assembly {
            parsedValue := mload(add(slicedBytes, 20))
        }
    }

    /// @notice                     Returns amount of output (exchange) token
    /// @dev                        If input token is fixed, outputAmount means the min expected output amount
    ///                             If output token is fixed, outputAmount is desired output amount
    /// @param _arbitraryData       Data written in Bitcoin tx
    /// @return parsedValue         The parsed value of exchange output amount
    function parseExchangeOutputAmount(bytes memory _arbitraryData) internal pure returns (uint224 parsedValue){
        bytes memory slicedBytes = sliceBytes(_arbitraryData, 46, 73);
        assembly {
            parsedValue := mload(add(slicedBytes, 28))
        }
    }

    /// @notice                     Returns deadline of executing exchange request
    /// @dev                        This value is compared to block.timestamp
    /// @param _arbitraryData       Data written in Bitcoin tx
    /// @return parsedValue         The parsed value of deadline
    function parseDeadline(bytes memory _arbitraryData) internal pure returns (uint32 parsedValue){
        bytes memory slicedBytes = sliceBytes(_arbitraryData, 74, 77);
        assembly {
            parsedValue := mload(add(slicedBytes, 4))
        }
    }

    /// @notice                     Returns true if input token is fixed
    /// @param _arbitraryData       Data written in Bitcoin tx
    /// @return parsedValue         The parsed value of is-fixed-token
    function parseIsFixedToken(bytes memory _arbitraryData) internal pure returns (uint8 parsedValue){
        bytes memory slicedBytes = sliceBytes(_arbitraryData, 78, 78);
        assembly {
            parsedValue := mload(add(slicedBytes, 1))
        }
    }

    /// @notice                 Returns a sliced bytes
    /// @param _data            Data that is sliced
    /// @param _start           Start index of slicing
    /// @param _end             End index of slicing
    /// @return _result         The result of slicing
    function sliceBytes(
        bytes memory _data,
        uint _start,
        uint _end
    ) internal pure returns (bytes memory _result) {
        bytes1 temp;
        for (uint i = _start; i < _end + 1; i++) {
            temp = _data[i];
            _result = abi.encodePacked(_result, temp);
        }
    }

}

File 2 of 18 : BitcoinHelper.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0 <0.8.4;

import "./TypedMemView.sol";
import "../types/ScriptTypesEnum.sol";
import "@openzeppelin/contracts/utils/math/SafeCast.sol";

library BitcoinHelper {

    using SafeCast for uint96;
    using SafeCast for uint256;

    using TypedMemView for bytes;
    using TypedMemView for bytes29;

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

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

    enum BTCTypes {
        Unknown,            // 0x0
        CompactInt,         // 0x1
        ScriptSig,          // 0x2 - with length prefix
        Outpoint,           // 0x3
        TxIn,               // 0x4
        IntermediateTxIns,  // 0x5 - used in vin parsing
        Vin,                // 0x6
        ScriptPubkey,       // 0x7 - with length prefix
        PKH,                // 0x8 - the 20-byte payload digest
        WPKH,               // 0x9 - the 20-byte payload digest
        WSH,                // 0xa - the 32-byte payload digest
        SH,                 // 0xb - the 20-byte payload digest
        OpReturnPayload,    // 0xc
        TxOut,              // 0xd
        IntermediateTxOuts, // 0xe - used in vout parsing
        Vout,               // 0xf
        Header,             // 0x10
        HeaderArray,        // 0x11
        MerkleNode,         // 0x12
        MerkleStep,         // 0x13
        MerkleArray         // 0x14
    }

    /// @notice             requires `memView` to be of a specified type
    /// @dev                passes if it is the correct type, errors if not
    /// @param memView      a 29-byte view with a 5-byte type
    /// @param t            the expected type (e.g. BTCTypes.Outpoint, BTCTypes.TxIn, etc)
    modifier typeAssert(bytes29 memView, BTCTypes t) {
        memView.assertType(uint40(t));
        _;
    }

    // Revert with an error message re: non-minimal VarInts
    function revertNonMinimal(bytes29 ref) private pure returns (string memory) {
        (, uint256 g) = TypedMemView.encodeHex(ref.indexUint(0, ref.len().toUint8()));
        string memory err = string(
            abi.encodePacked(
                "Non-minimal var int. Got 0x",
                uint144(g)
            )
        );
        revert(err);
    }

    /// @notice             reads a compact int from the view at the specified index
    /// @param memView      a 29-byte view with a 5-byte type
    /// @param _index       the index
    /// @return number      returns the compact int at the specified index
    function indexCompactInt(bytes29 memView, uint256 _index) internal pure returns (uint64 number) {
        uint256 flag = memView.indexUint(_index, 1);
        if (flag <= 0xfc) {
            return flag.toUint64();
        } else if (flag == 0xfd) {
            number = memView.indexLEUint(_index + 1, 2).toUint64();
            if (compactIntLength(number) != 3) {revertNonMinimal(memView.slice(_index, 3, 0));}
        } else if (flag == 0xfe) {
            number = memView.indexLEUint(_index + 1, 4).toUint64();
            if (compactIntLength(number) != 5) {revertNonMinimal(memView.slice(_index, 5, 0));}
        } else if (flag == 0xff) {
            number = memView.indexLEUint(_index + 1, 8).toUint64();
            if (compactIntLength(number) != 9) {revertNonMinimal(memView.slice(_index, 9, 0));}
        }
    }

    /// @notice         gives the total length (in bytes) of a CompactInt-encoded number
    /// @param number   the number as uint64
    /// @return         the compact integer length as uint8
    function compactIntLength(uint64 number) private pure returns (uint8) {
        if (number <= 0xfc) {
            return 1;
        } else if (number <= 0xffff) {
            return 3;
        } else if (number <= 0xffffffff) {
            return 5;
        } else {
            return 9;
        }
    }

    /// @notice             extracts the LE txid from an outpoint
    /// @param _outpoint    the outpoint
    /// @return             the LE txid
    function txidLE(bytes29 _outpoint) internal pure typeAssert(_outpoint, BTCTypes.Outpoint) returns (bytes32) {
        return _outpoint.index(0, 32);
    }

    /// @notice                      Calculates the required transaction Id from the transaction details
    /// @dev                         Calculates the hash of transaction details two consecutive times
    /// @param _version              Version of the transaction
    /// @param _vin                  Inputs of the transaction
    /// @param _vout                 Outputs of the transaction
    /// @param _locktime             Lock time of the transaction
    /// @return                      Transaction Id of the transaction (in LE form)
    function calculateTxId(
        bytes4 _version,
        bytes memory _vin,
        bytes memory _vout,
        bytes4 _locktime
    ) internal pure returns (bytes32) {
        bytes32 inputHash1 = sha256(abi.encodePacked(_version, _vin, _vout, _locktime));
        bytes32 inputHash2 = sha256(abi.encodePacked(inputHash1));
        return inputHash2;
    }

    /// @notice                      Reverts a Bytes32 input
    /// @param _input                Bytes32 input that we want to revert
    /// @return                      Reverted bytes32
    function reverseBytes32(bytes32 _input) private pure returns (bytes32) {
        bytes memory temp;
        bytes32 result;
        for (uint i = 0; i < 32; i++) {
            temp = abi.encodePacked(temp, _input[31-i]);
        }
        assembly {
            result := mload(add(temp, 32))
        }
        return result;
    }

    /// @notice                           Parses outpoint info from an input
    /// @dev                              Reverts if vin is null
    /// @param _vin                       The vin of a Bitcoin transaction
    /// @param _index                     Index of the input that we are looking at
    /// @return _txId                     Output tx id
    /// @return _outputIndex              Output tx index
    function extractOutpoint(
        bytes memory _vin, 
        uint _index
    ) internal pure returns (bytes32 _txId, uint _outputIndex) {
        bytes29 vin = tryAsVin(_vin.ref(uint40(BTCTypes.Unknown)));
        require(!vin.isNull(), "BitcoinHelper: vin is null");
        bytes29 input = indexVin(vin, _index);
        bytes29 _outpoint = outpoint(input);
        _txId = txidLE(_outpoint);
        _outputIndex = outpointIdx(_outpoint);
    }

    /// @notice             extracts the index as an integer from the outpoint
    /// @param _outpoint    the outpoint
    /// @return             the index
    function outpointIdx(bytes29 _outpoint) internal pure typeAssert(_outpoint, BTCTypes.Outpoint) returns (uint32) {
        return _outpoint.indexLEUint(32, 4).toUint32();
    }

    /// @notice          extracts the outpoint from an input
    /// @param _input    the input
    /// @return          the outpoint as a typed memory
    function outpoint(bytes29 _input) internal pure typeAssert(_input, BTCTypes.TxIn) returns (bytes29) {
        return _input.slice(0, 36, uint40(BTCTypes.Outpoint));
    }

    /// @notice           extracts the script sig from an input
    /// @param _input     the input
    /// @return           the script sig as a typed memory
    function scriptSig(bytes29 _input) internal pure typeAssert(_input, BTCTypes.TxIn) returns (bytes29) {
        uint64 scriptLength = indexCompactInt(_input, 36);
        return _input.slice(36, compactIntLength(scriptLength) + scriptLength, uint40(BTCTypes.ScriptSig));
    }

    /// @notice         determines the length of the first input in an array of inputs
    /// @param _inputs  the vin without its length prefix
    /// @return         the input length
    function inputLength(bytes29 _inputs) private pure typeAssert(_inputs, BTCTypes.IntermediateTxIns) returns (uint256) {
        uint64 scriptLength = indexCompactInt(_inputs, 36);
        return uint256(compactIntLength(scriptLength)) + uint256(scriptLength) + 36 + 4;
    }

    /// @notice         extracts the input at a specified index
    /// @param _vin     the vin
    /// @param _index   the index of the desired input
    /// @return         the desired input
    function indexVin(bytes29 _vin, uint256 _index) internal pure typeAssert(_vin, BTCTypes.Vin) returns (bytes29) {
        uint256 _nIns = uint256(indexCompactInt(_vin, 0));
        uint256 _viewLen = _vin.len();
        require(_index < _nIns, "Vin read overrun");

        uint256 _offset = uint256(compactIntLength(uint64(_nIns)));
        bytes29 _remaining;
        for (uint256 _i = 0; _i < _index; _i += 1) {
            _remaining = _vin.postfix(_viewLen - _offset, uint40(BTCTypes.IntermediateTxIns));
            _offset += inputLength(_remaining);
        }

        _remaining = _vin.postfix(_viewLen - _offset, uint40(BTCTypes.IntermediateTxIns));
        uint256 _len = inputLength(_remaining);
        return _vin.slice(_offset, _len, uint40(BTCTypes.TxIn));
    }

    /// @notice         extracts the value from an output
    /// @param _output  the output
    /// @return         the value
    function value(bytes29 _output) internal pure typeAssert(_output, BTCTypes.TxOut) returns (uint64) {
        return _output.indexLEUint(0, 8).toUint64();
    }

    /// @notice                   Finds total outputs value
    /// @dev                      Reverts if vout is null
    /// @param _vout              The vout of a Bitcoin transaction
    /// @return _totalValue       Total vout value
    function parseOutputsTotalValue(bytes memory _vout) internal pure returns (uint64 _totalValue) {
        bytes29 voutView = tryAsVout(_vout.ref(uint40(BTCTypes.Unknown)));
        require(!voutView.isNull(), "BitcoinHelper: vout is null");
        bytes29 output;

        // Finds total number of outputs
        uint _numberOfOutputs = uint256(indexCompactInt(voutView, 0));

        for (uint index = 0; index < _numberOfOutputs; index++) {
            output = indexVout(voutView, index);
            _totalValue = _totalValue + value(output);
        }
    }

    /// @notice                           Parses the BTC amount that has been sent to 
    ///                                   a specific script in a specific output
    /// @param _vout                      The vout of a Bitcoin transaction
    /// @param _voutIndex                 Index of the output that we are looking at
    /// @param _script                    Desired recipient script
    /// @param _scriptType                Type of the script (e.g. P2PK)
    /// @return bitcoinAmount             Amount of BTC have been sent to the _script
    function parseValueFromSpecificOutputHavingScript(
        bytes memory _vout,
        uint _voutIndex,
        bytes memory _script,
        ScriptTypes _scriptType
    ) internal pure returns (uint64 bitcoinAmount) {

        bytes29 voutView = tryAsVout(_vout.ref(uint40(BTCTypes.Unknown)));
        require(!voutView.isNull(), "BitcoinHelper: vout is null");
        bytes29 output = indexVout(voutView, _voutIndex);
        bytes29 _scriptPubkey = scriptPubkey(output);

        if (_scriptType == ScriptTypes.P2PK) {
            // note: first byte is Pushdata Bytelength. 
            // note: public key length is 32.           
            bitcoinAmount = keccak256(_script) == keccak256(abi.encodePacked(_scriptPubkey.index(1, 32))) ? value(output) : 0;
        } else if (_scriptType == ScriptTypes.P2PKH) { 
            // note: first three bytes are OP_DUP, OP_HASH160, Pushdata Bytelength. 
            // note: public key hash length is 20.         
            bitcoinAmount = keccak256(_script) == keccak256(abi.encodePacked(_scriptPubkey.indexAddress(3))) ? value(output) : 0;
        } else if (_scriptType == ScriptTypes.P2SH) {
            // note: first two bytes are OP_HASH160, Pushdata Bytelength
            // note: script hash length is 20.                      
            bitcoinAmount = keccak256(_script) == keccak256(abi.encodePacked(_scriptPubkey.indexAddress(2))) ? value(output) : 0;
        } else if (_scriptType == ScriptTypes.P2WPKH) {               
            // note: first two bytes are OP_0, Pushdata Bytelength
            // note: segwit public key hash length is 20. 
            bitcoinAmount = keccak256(_script) == keccak256(abi.encodePacked(_scriptPubkey.indexAddress(2))) ? value(output) : 0;
        } else if (_scriptType == ScriptTypes.P2WSH) {
            // note: first two bytes are OP_0, Pushdata Bytelength 
            // note: segwit script hash length is 32.           
            bitcoinAmount = keccak256(_script) == keccak256(abi.encodePacked(_scriptPubkey.index(2, 32))) ? value(output) : 0;
        }
        
    }

    /// @notice                           Parses the BTC amount of a transaction
    /// @dev                              Finds the BTC amount that has been sent to the locking script
    ///                                   Returns zero if no matching locking scrip is found
    /// @param _vout                      The vout of a Bitcoin transaction
    /// @param _lockingScript             Desired locking script
    /// @return bitcoinAmount             Amount of BTC have been sent to the _lockingScript
    function parseValueHavingLockingScript(
        bytes memory _vout,
        bytes memory _lockingScript
    ) internal view returns (uint64 bitcoinAmount) {
        // Checks that vout is not null
        bytes29 voutView = tryAsVout(_vout.ref(uint40(BTCTypes.Unknown)));
        require(!voutView.isNull(), "BitcoinHelper: vout is null");

        bytes29 output;
        bytes29 _scriptPubkey;
        
        // Finds total number of outputs
        uint _numberOfOutputs = uint256(indexCompactInt(voutView, 0));

        for (uint index = 0; index < _numberOfOutputs; index++) {
            output = indexVout(voutView, index);
            _scriptPubkey = scriptPubkey(output);

            if (
                keccak256(abi.encodePacked(_scriptPubkey.clone())) == keccak256(abi.encodePacked(_lockingScript))
            ) {
                bitcoinAmount = value(output);
                // Stops searching after finding the desired locking script
                break;
            }
        }
    }

    /// @notice                           Parses the BTC amount and the op_return of a transaction
    /// @dev                              Finds the BTC amount that has been sent to the locking script
    ///                                   Assumes that payload size is less than 76 bytes
    /// @param _vout                      The vout of a Bitcoin transaction
    /// @param _lockingScript             Desired locking script
    /// @return bitcoinAmount             Amount of BTC have been sent to the _lockingScript
    /// @return arbitraryData             Opreturn  data of the transaction
    function parseValueAndDataHavingLockingScriptSmallPayload(
        bytes memory _vout,
        bytes memory _lockingScript
    ) internal view returns (uint64 bitcoinAmount, bytes memory arbitraryData) {
        // Checks that vout is not null
        bytes29 voutView = tryAsVout(_vout.ref(uint40(BTCTypes.Unknown)));
        require(!voutView.isNull(), "BitcoinHelper: vout is null");

        bytes29 output;
        bytes29 _scriptPubkey;
        bytes29 _scriptPubkeyWithLength;
        bytes29 _arbitraryData;

        // Finds total number of outputs
        uint _numberOfOutputs = uint256(indexCompactInt(voutView, 0));

        for (uint index = 0; index < _numberOfOutputs; index++) {
            output = indexVout(voutView, index);
            _scriptPubkey = scriptPubkey(output);
            _scriptPubkeyWithLength = scriptPubkeyWithLength(output);
            _arbitraryData = opReturnPayloadSmall(_scriptPubkeyWithLength);

            // Checks whether the output is an arbitarary data or not
            if(_arbitraryData == TypedMemView.NULL) {
                // Output is not an arbitrary data
                if (
                    keccak256(abi.encodePacked(_scriptPubkey.clone())) == keccak256(abi.encodePacked(_lockingScript))
                ) {
                    bitcoinAmount = value(output);
                }
            } else {
                // Returns the whole bytes array
                arbitraryData = _arbitraryData.clone();
            }
        }
    }

    /// @notice                           Parses the BTC amount and the op_return of a transaction
    /// @dev                              Finds the BTC amount that has been sent to the locking script
    ///                                   Assumes that payload size is greater than 75 bytes
    /// @param _vout                      The vout of a Bitcoin transaction
    /// @param _lockingScript             Desired locking script
    /// @return bitcoinAmount             Amount of BTC have been sent to the _lockingScript
    /// @return arbitraryData             Opreturn  data of the transaction
    function parseValueAndDataHavingLockingScriptBigPayload(
        bytes memory _vout,
        bytes memory _lockingScript
    ) internal view returns (uint64 bitcoinAmount, bytes memory arbitraryData) {
        // Checks that vout is not null
        bytes29 voutView = tryAsVout(_vout.ref(uint40(BTCTypes.Unknown)));
        require(!voutView.isNull(), "BitcoinHelper: vout is null");

        bytes29 output;
        bytes29 _scriptPubkey;
        bytes29 _scriptPubkeyWithLength;
        bytes29 _arbitraryData;

        // Finds total number of outputs
        uint _numberOfOutputs = uint256(indexCompactInt(voutView, 0));

        for (uint index = 0; index < _numberOfOutputs; index++) {
            output = indexVout(voutView, index);
            _scriptPubkey = scriptPubkey(output);
            _scriptPubkeyWithLength = scriptPubkeyWithLength(output);
            _arbitraryData = opReturnPayloadBig(_scriptPubkeyWithLength);

            // Checks whether the output is an arbitarary data or not
            if(_arbitraryData == 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) {
                // Output is not an arbitrary data
                if (
                    keccak256(abi.encodePacked(_scriptPubkey.clone())) == keccak256(abi.encodePacked(_lockingScript))
                ) {
                    bitcoinAmount = value(output);
                }
            } else {
                // Returns the whole bytes array
                arbitraryData = _arbitraryData.clone();
            }
        }
    }

    /// @notice             extracts the scriptPubkey from an output
    /// @param _output      the output
    /// @return             the scriptPubkey
    function scriptPubkey(bytes29 _output) internal pure typeAssert(_output, BTCTypes.TxOut) returns (bytes29) {
        uint64 scriptLength = indexCompactInt(_output, 8);
        return _output.slice(8 + compactIntLength(scriptLength), scriptLength, uint40(BTCTypes.ScriptPubkey));
    }

    /// @notice             extracts the scriptPubkey from an output
    /// @param _output      the output
    /// @return             the scriptPubkey
    function scriptPubkeyWithLength(bytes29 _output) internal pure typeAssert(_output, BTCTypes.TxOut) returns (bytes29) {
        uint64 scriptLength = indexCompactInt(_output, 8);
        return _output.slice(8, compactIntLength(scriptLength) + scriptLength, uint40(BTCTypes.ScriptPubkey));
    }

    /// @notice                           Parses locking script from an output
    /// @dev                              Reverts if vout is null
    /// @param _vout                      The vout of a Bitcoin transaction
    /// @param _index                     Index of the output that we are looking at
    /// @return _lockingScript            Parsed locking script
    function getLockingScript(
        bytes memory _vout, 
        uint _index
    ) internal view returns (bytes memory _lockingScript) {
        bytes29 vout = tryAsVout(_vout.ref(uint40(BTCTypes.Unknown)));
        require(!vout.isNull(), "BitcoinHelper: vout is null");
        bytes29 output = indexVout(vout, _index);
        bytes29 _lockingScriptBytes29 = scriptPubkey(output);
        _lockingScript = _lockingScriptBytes29.clone();
    }

    /// @notice                   Returns number of outputs in a vout
    /// @param _vout              The vout of a Bitcoin transaction           
    function numberOfOutputs(bytes memory _vout) internal pure returns (uint _numberOfOutputs) {
        bytes29 voutView = tryAsVout(_vout.ref(uint40(BTCTypes.Unknown)));
        _numberOfOutputs = uint256(indexCompactInt(voutView, 0));
    }

    /// @notice             determines the length of the first output in an array of outputs
    /// @param _outputs     the vout without its length prefix
    /// @return             the output length
    function outputLength(bytes29 _outputs) private pure typeAssert(_outputs, BTCTypes.IntermediateTxOuts) returns (uint256) {
        uint64 scriptLength = indexCompactInt(_outputs, 8);
        return uint256(compactIntLength(scriptLength)) + uint256(scriptLength) + 8;
    }

    /// @notice         extracts the output at a specified index
    /// @param _vout    the vout
    /// @param _index   the index of the desired output
    /// @return         the desired output
    function indexVout(bytes29 _vout, uint256 _index) internal pure typeAssert(_vout, BTCTypes.Vout) returns (bytes29) {
        uint256 _nOuts = uint256(indexCompactInt(_vout, 0));
        uint256 _viewLen = _vout.len();
        require(_index < _nOuts, "Vout read overrun");

        uint256 _offset = uint256(compactIntLength(uint64(_nOuts)));
        bytes29 _remaining;
        for (uint256 _i = 0; _i < _index; _i += 1) {
            _remaining = _vout.postfix(_viewLen - _offset, uint40(BTCTypes.IntermediateTxOuts));
            _offset += outputLength(_remaining);
        }

        _remaining = _vout.postfix(_viewLen - _offset, uint40(BTCTypes.IntermediateTxOuts));
        uint256 _len = outputLength(_remaining);
        return _vout.slice(_offset, _len, uint40(BTCTypes.TxOut));
    }

    /// @notice         extracts the Op Return Payload
    /// @dev            structure of the input is: 1 byte op return + 2 bytes indicating the length of payload + max length for op return payload is 80 bytes
    /// @param _spk     the scriptPubkey
    /// @return         the Op Return Payload (or null if not a valid Op Return output)
    function opReturnPayloadBig(bytes29 _spk) internal pure typeAssert(_spk, BTCTypes.ScriptPubkey) returns (bytes29) {
        uint64 _bodyLength = indexCompactInt(_spk, 0);
        uint64 _payloadLen = _spk.indexUint(3, 1).toUint64();
        if (_bodyLength > 83 || _bodyLength < 4 || _spk.indexUint(1, 1) != 0x6a || _spk.indexUint(3, 1) != _bodyLength - 3) {
            return TypedMemView.nullView();
        }
        return _spk.slice(4, _payloadLen, uint40(BTCTypes.OpReturnPayload));
    }

    /// @notice         extracts the Op Return Payload
    /// @dev            structure of the input is: 1 byte op return + 1 bytes indicating the length of payload + max length for op return payload is 75 bytes
    /// @param _spk     the scriptPubkey
    /// @return         the Op Return Payload (or null if not a valid Op Return output)
    function opReturnPayloadSmall(bytes29 _spk) internal pure typeAssert(_spk, BTCTypes.ScriptPubkey) returns (bytes29) {
        uint64 _bodyLength = indexCompactInt(_spk, 0);
        uint64 _payloadLen = _spk.indexUint(2, 1).toUint64();
        if (_bodyLength > 77 || _bodyLength < 4 || _spk.indexUint(1, 1) != 0x6a || _spk.indexUint(2, 1) != _bodyLength - 2) {
            return TypedMemView.nullView();
        }
        return _spk.slice(3, _payloadLen, uint40(BTCTypes.OpReturnPayload));
    }

    /// @notice     verifies the vin and converts to a typed memory
    /// @dev        will return null in error cases
    /// @param _vin the vin
    /// @return     the typed vin (or null if error)
    function tryAsVin(bytes29 _vin) internal pure typeAssert(_vin, BTCTypes.Unknown) returns (bytes29) {
        if (_vin.len() == 0) {
            return TypedMemView.nullView();
        }
        uint64 _nIns = indexCompactInt(_vin, 0);
        uint256 _viewLen = _vin.len();
        if (_nIns == 0) {
            return TypedMemView.nullView();
        }

        uint256 _offset = uint256(compactIntLength(_nIns));
        for (uint256 i = 0; i < _nIns; i++) {
            if (_offset >= _viewLen) {
                // We've reached the end, but are still trying to read more
                return TypedMemView.nullView();
            }
            bytes29 _remaining = _vin.postfix(_viewLen - _offset, uint40(BTCTypes.IntermediateTxIns));
            _offset += inputLength(_remaining);
        }
        if (_offset != _viewLen) {
            return TypedMemView.nullView();
        }
        return _vin.castTo(uint40(BTCTypes.Vin));
    }

    /// @notice         verifies the vout and converts to a typed memory
    /// @dev            will return null in error cases
    /// @param _vout    the vout
    /// @return         the typed vout (or null if error)
    function tryAsVout(bytes29 _vout) internal pure typeAssert(_vout, BTCTypes.Unknown) returns (bytes29) {
        if (_vout.len() == 0) {
            return TypedMemView.nullView();
        }
        uint64 _nOuts = indexCompactInt(_vout, 0);

        uint256 _viewLen = _vout.len();
        if (_nOuts == 0) {
            return TypedMemView.nullView();
        }

        uint256 _offset = uint256(compactIntLength(_nOuts));
        for (uint256 i = 0; i < _nOuts; i++) {
            if (_offset >= _viewLen) {
                // We've reached the end, but are still trying to read more
                return TypedMemView.nullView();
            }
            bytes29 _remaining = _vout.postfix(_viewLen - _offset, uint40(BTCTypes.IntermediateTxOuts));
            _offset += outputLength(_remaining);
        }
        if (_offset != _viewLen) {
            return TypedMemView.nullView();
        }
        return _vout.castTo(uint40(BTCTypes.Vout));
    }

    /// @notice         verifies the header and converts to a typed memory
    /// @dev            will return null in error cases
    /// @param _header  the header
    /// @return         the typed header (or null if error)
    function tryAsHeader(bytes29 _header) internal pure typeAssert(_header, BTCTypes.Unknown) returns (bytes29) {
        if (_header.len() != 80) {
            return TypedMemView.nullView();
        }
        return _header.castTo(uint40(BTCTypes.Header));
    }


    /// @notice         Index a header array.
    /// @dev            Errors on overruns
    /// @param _arr     The header array
    /// @param index    The 0-indexed location of the header to get
    /// @return         the typed header at `index`
    function indexHeaderArray(bytes29 _arr, uint256 index) internal pure typeAssert(_arr, BTCTypes.HeaderArray) returns (bytes29) {
        uint256 _start = index * 80;
        return _arr.slice(_start, 80, uint40(BTCTypes.Header));
    }


    /// @notice     verifies the header array and converts to a typed memory
    /// @dev        will return null in error cases
    /// @param _arr the header array
    /// @return     the typed header array (or null if error)
    function tryAsHeaderArray(bytes29 _arr) internal pure typeAssert(_arr, BTCTypes.Unknown) returns (bytes29) {
        if (_arr.len() % 80 != 0) {
            return TypedMemView.nullView();
        }
        return _arr.castTo(uint40(BTCTypes.HeaderArray));
    }

    /// @notice     verifies the merkle array and converts to a typed memory
    /// @dev        will return null in error cases
    /// @param _arr the merkle array
    /// @return     the typed merkle array (or null if error)
    function tryAsMerkleArray(bytes29 _arr) internal pure typeAssert(_arr, BTCTypes.Unknown) returns (bytes29) {
        if (_arr.len() % 32 != 0) {
            return TypedMemView.nullView();
        }
        return _arr.castTo(uint40(BTCTypes.MerkleArray));
    }

    /// @notice         extracts the merkle root from the header
    /// @param _header  the header
    /// @return         the merkle root
    function merkleRoot(bytes29 _header) internal pure typeAssert(_header, BTCTypes.Header) returns (bytes32) {
        return _header.index(36, 32);
    }

    /// @notice         extracts the target from the header
    /// @param _header  the header
    /// @return         the target
    function target(bytes29  _header) internal pure typeAssert(_header, BTCTypes.Header) returns (uint256) {
        uint256 _mantissa = _header.indexLEUint(72, 3);
        require(_header.indexUint(75, 1) > 2, "ViewBTC: invalid target difficulty");
        uint256 _exponent = _header.indexUint(75, 1) - 3;
        return _mantissa * (256 ** _exponent);
    }

    /// @notice         calculates the difficulty from a target
    /// @param _target  the target
    /// @return         the difficulty
    function toDiff(uint256  _target) private pure returns (uint256) {
        return DIFF1_TARGET / (_target);
    }

    /// @notice         extracts the difficulty from the header
    /// @param _header  the header
    /// @return         the difficulty
    function diff(bytes29  _header) internal pure typeAssert(_header, BTCTypes.Header) returns (uint256) {
        return toDiff(target(_header));
    }

    /// @notice         extracts the timestamp from the header
    /// @param _header  the header
    /// @return         the timestamp
    function time(bytes29  _header) internal pure typeAssert(_header, BTCTypes.Header) returns (uint32) {
        return uint32(_header.indexLEUint(68, 4));
    }

    /// @notice         extracts the parent hash from the header
    /// @param _header  the header
    /// @return         the parent hash
    function parent(bytes29 _header) internal pure typeAssert(_header, BTCTypes.Header) returns (bytes32) {
        return _header.index(4, 32);
    }

    /// @notice                     Checks validity of header chain
    /// @dev                        Compares current header parent to previous header's digest
    /// @param _header              The raw bytes header
    /// @param _prevHeaderDigest    The previous header's digest
    /// @return                     true if the connect is valid, false otherwise
    function checkParent(bytes29 _header, bytes32 _prevHeaderDigest) internal pure typeAssert(_header, BTCTypes.Header) returns (bool) {
        return parent(_header) == _prevHeaderDigest;
    }

    /// @notice                     Validates a tx inclusion in the block
    /// @dev                        `index` is not a reliable indicator of location within a block
    /// @param _txid                The txid (LE)
    /// @param _merkleRoot          The merkle root
    /// @param _intermediateNodes   The proof's intermediate nodes (digests between leaf and root)
    /// @param _index               The leaf's index in the tree (0-indexed)
    /// @return                     true if fully valid, false otherwise
    function prove( 
        bytes32 _txid,
        bytes32 _merkleRoot,
        bytes29 _intermediateNodes,
        uint _index
    ) internal view typeAssert(_intermediateNodes, BTCTypes.MerkleArray) returns (bool) {
        // Shortcut the empty-block case
        if (
            _txid == _merkleRoot &&
                _index == 0 &&
                    _intermediateNodes.len() == 0
        ) {
            return true;
        }

        return checkMerkle(_txid, _intermediateNodes, _merkleRoot, _index);
    }

    /// @notice         verifies a merkle proof
    /// @dev            leaf, proof, and root are in LE format
    /// @param _leaf    the leaf
    /// @param _proof   the proof nodes
    /// @param _root    the merkle root
    /// @param _index   the index
    /// @return         true if valid, false if otherwise
    function checkMerkle(
        bytes32 _leaf,
        bytes29 _proof,
        bytes32 _root,
        uint256 _index
    ) private view typeAssert(_proof, BTCTypes.MerkleArray) returns (bool) {
        uint256 nodes = _proof.len() / 32;
        if (nodes == 0) {
            return _leaf == _root;
        }

        uint256 _idx = _index;
        bytes32 _current = _leaf;

        for (uint i = 0; i < nodes; i++) {
            bytes32 _next = _proof.index(i * 32, 32);
            if (_idx % 2 == 1) {
                _current = merkleStep(_next, _current);
            } else {
                _current = merkleStep(_current, _next);
            }
            _idx >>= 1;
        }

        return _current == _root;
    }

    /// @notice          Concatenates and hashes two inputs for merkle proving
    /// @dev             Not recommended to call directly.
    /// @param _a        The first hash
    /// @param _b        The second hash
    /// @return digest   The double-sha256 of the concatenated hashes
    function merkleStep(bytes32 _a, bytes32 _b) private view returns (bytes32 digest) {
        assembly {
        // solium-disable-previous-line security/no-inline-assembly
            let ptr := mload(0x40)
            mstore(ptr, _a)
            mstore(add(ptr, 0x20), _b)
            pop(staticcall(gas(), 2, ptr, 0x40, ptr, 0x20)) // sha256 #1
            pop(staticcall(gas(), 2, ptr, 0x20, ptr, 0x20)) // sha256 #2
            digest := mload(ptr)
        }
    }

    /// @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 - _firstTimestamp;

        // Normalize ratio to factor of 4 if very long or very short
        if (_elapsedTime < RETARGET_PERIOD / 4) {
            _elapsedTime = RETARGET_PERIOD / 4;
        }
        if (_elapsedTime > RETARGET_PERIOD * 4) {
            _elapsedTime = RETARGET_PERIOD * 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 / 65536 * _elapsedTime;
        return _adjusted / RETARGET_PERIOD * 65536;
    }
}

File 2 of 18 : ICCTransferRouter.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0 <0.8.4;

interface ICCTransferRouter {

	// Structures

	/// @notice                    Structure for recording cross-chain transfer requests
	/// @param inputAmount         Amount of locked BTC on source chain
	/// @param recipientAddress    Address of transfer recipient
	/// @param fee                 Amount of fee that is paid to Teleporter (tx, relayer and teleporter fees)
	/// @param speed               Speed of the request (normal or instant)
	/// @param isUsed              Whether the tx is used or not
	struct ccTransferRequest {
		uint inputAmount;
		address recipientAddress;
		uint fee;
		uint256 speed;
		bool isUsed;
	}

	// Events

	/// @notice                    	Emits when a cc transfer request gets done
	/// @param lockerLockingScript  Locking script of the locker on bitcoin network
	/// @param lockerScriptType     Script type of the locker locking script
	/// @param lockerTargetAddress  Address of the locker on EVM based target chain
	/// @param user                	Address of teleBTC recipient
	/// @param inputAmount         	Amount of tokens that user locked on source chain
	/// @param receivedAmount      	Amount of tokens that user receives
	/// @param speed               	Speed of the request (normal or instant)
	/// @param teleporter          	Address of teleporter who submitted the request
	/// @param teleporterFee       	Amount of fee that is paid to Teleporter (tx, relayer and teleporter fees)
	/// @param relayFee       	   	Amount of fee that is paid to relay contract
	/// @param protocolFee         	Amount of fee that is paid to the protocol
	/// @param bitcoinTxId         	Address of teleporter who submitted the request
	event CCTransfer(
		bytes indexed lockerLockingScript,
		uint lockerScriptType,
		address lockerTargetAddress,
		address indexed user,
		uint inputAmount,
		uint receivedAmount,
		uint indexed speed,
		address teleporter,
		uint teleporterFee,
		uint relayFee,
		uint protocolFee,
		bytes32 bitcoinTxId
	);

	/// @notice                     Emits when changes made to relay address
    event NewRelay (
        address oldRelay, 
        address newRelay
    );

    /// @notice                     Emits when changes made to InstantRouter address
    event NewInstantRouter (
        address oldInstantRouter, 
        address newInstantRouter
    );

    /// @notice                     Emits when changes made to Lockers address
    event NewLockers (
        address oldLockers, 
        address newLockers
    );

    /// @notice                     Emits when changes made to TeleBTC address
    event NewTeleBTC (
        address oldTeleBTC, 
        address newTeleBTC
    );

    /// @notice                     Emits when changes made to protocol percentage fee
    event NewProtocolPercentageFee (
        uint oldProtocolPercentageFee, 
        uint newProtocolPercentageFee
    );

    /// @notice                     Emits when changes made to Treasury address
    event NewTreasury (
        address oldTreasury, 
        address newTreasury
    );

	// Read-only functions
	
	function startingBlockNumber() external view returns (uint);
	
	function protocolPercentageFee() external view returns (uint);
	
	function chainId() external view returns (uint);

	function appId() external view returns (uint);

	function relay() external view returns (address);

	function instantRouter() external view returns (address);

	function lockers() external view returns (address);

	function teleBTC() external view returns (address);

	function treasury() external view returns (address);

	function isRequestUsed(bytes32 _txId) external view returns (bool);

	// State-changing functions

	function setRelay(address _relay) external;

	function setInstantRouter(address _instantRouter) external;

	function setLockers(address _lockers) external;

	function setTeleBTC(address _teleBTC) external;

	function setTreasury(address _treasury) external;

	function setProtocolPercentageFee(uint _protocolPercentageFee) external;

	function ccTransfer(
		// Bitcoin tx
		bytes4 _version,
		bytes memory _vin,
		bytes calldata _vout,
		bytes4 _locktime,
		// Bitcoin block number
		uint256 _blockNumber,
		// Merkle proof
		bytes calldata _intermediateNodes,
		uint _index,
		bytes calldata _lockerLockingScript
	) external payable returns (bool);
}

File 2 of 18 : ITeleBTC.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0 <0.8.4;

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

interface ITeleBTC is IERC20 {

    // Events
    event Mint(address indexed doer, address indexed receiver, uint value);

    event Burn(address indexed doer, address indexed burner, uint value);

    event MinterAdded(address indexed newMinter);

    event MinterRemoved(address indexed minter);

    event BurnerAdded(address indexed newBurner);

    event BurnerRemoved(address indexed burner);

    event NewMintLimit(uint oldMintLimit, uint newMintLimit);

    event NewEpochLength(uint oldEpochLength, uint newEpochLength);

    // read functions

    function decimals() external view returns (uint8);

    // state-changing functions

    function addMinter(address account) external;

    function removeMinter(address account) external;

    function addBurner(address account) external;

    function removeBurner(address account) external;

    function mint(address receiver, uint amount) external returns(bool);

    function burn(uint256 amount) external returns(bool);

    function setMaxMintLimit(uint _mintLimit) external;

    function setEpochLength(uint _length) external;
}

File 2 of 18 : IBitcoinRelay.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0 <0.8.4;

interface IBitcoinRelay {
    // Structures

    /// @notice                 	Structure for recording block header
    /// @param selfHash             Hash of block header
    /// @param parentHash          	Hash of parent block header
    /// @param merkleRoot       	Merkle root of transactions in the block
    /// @param relayer              Address of relayer who submitted the block header
    /// @param gasPrice             Gas price of tx that relayer submitted the block header
    struct blockHeader {
        bytes32 selfHash;
        bytes32 parentHash;
        bytes32 merkleRoot;
        address relayer;
        uint gasPrice;
    }

    // Events

    /// @notice                     Emits when a block header is added
    /// @param height               Height of submitted header
    /// @param selfHash             Hash of submitted header
    /// @param parentHash           Parent hash of submitted header
    /// @param relayer              Address of relayer who submitted the block header
    event BlockAdded(
        uint indexed height,
        bytes32 selfHash,
        bytes32 indexed parentHash,
        address indexed relayer
    );

    /// @notice                     Emits when a block header gets finalized
    /// @param height               Height of the header
    /// @param selfHash             Hash of the header
    /// @param parentHash           Parent hash of the header
    /// @param relayer              Address of relayer who submitted the block header
    /// @param rewardAmountTNT      Amount of reward that the relayer receives in target native token
    /// @param rewardAmountTDT      Amount of reward that the relayer receives in TDT
    event BlockFinalized(
        uint indexed height,
        bytes32 selfHash,
        bytes32 parentHash,
        address indexed relayer,
        uint rewardAmountTNT,
        uint rewardAmountTDT
    );
         

    /// @notice                     Emits when changes made to reward amount in TDT
    event NewRewardAmountInTDT (
        uint oldRewardAmountInTDT, 
        uint newRewardAmountInTDT
    );

    /// @notice                     Emits when changes made to finalization parameter
    event NewFinalizationParameter (
        uint oldFinalizationParameter, 
        uint newFinalizationParameter
    );

    /// @notice                     Emits when changes made to relayer percentage fee
    event NewRelayerPercentageFee (
        uint oldRelayerPercentageFee, 
        uint newRelayerPercentageFee
    );

    /// @notice                     Emits when changes made to teleportDAO token
    event NewTeleportDAOToken (
        address oldTeleportDAOToken, 
        address newTeleportDAOToken
    );

    /// @notice                     Emits when changes made to epoch length
    event NewEpochLength(
        uint oldEpochLength, 
        uint newEpochLength
    );

    /// @notice                     Emits when changes made to base queries
    event NewBaseQueries(
        uint oldBaseQueries, 
        uint newBaseQueries
    );

    /// @notice                     Emits when changes made to submission gas used
    event NewSubmissionGasUsed(
        uint oldSubmissionGasUsed, 
        uint newSubmissionGasUsed
    );

    // Read-only functions

    function relayGenesisHash() external view returns (bytes32);

    function initialHeight() external view returns(uint);

    function lastSubmittedHeight() external view returns(uint);

    function finalizationParameter() external view returns(uint);

    function TeleportDAOToken() external view returns(address);

    function relayerPercentageFee() external view returns(uint);

    function epochLength() external view returns(uint);

    function lastEpochQueries() external view returns(uint);

    function currentEpochQueries() external view returns(uint);

    function baseQueries() external view returns(uint);

    function submissionGasUsed() external view returns(uint);

    function getBlockHeaderHash(uint height, uint index) external view returns(bytes32);

    function getBlockHeaderFee(uint _height, uint _index) external view returns(uint);

    function getNumberOfSubmittedHeaders(uint height) external view returns (uint);

    function availableTDT() external view returns(uint);

    function availableTNT() external view returns(uint);

    function findHeight(bytes32 _hash) external view returns (uint256);

    function findAncestor(bytes32 _hash, uint256 _offset) external view returns (bytes32); 

    function isAncestor(bytes32 _ancestor, bytes32 _descendant, uint256 _limit) external view returns (bool); 

    function rewardAmountInTDT() external view returns (uint);

    // State-changing functions

    function pauseRelay() external;

    function unpauseRelay() external;

    function setRewardAmountInTDT(uint _rewardAmountInTDT) external;

    function setFinalizationParameter(uint _finalizationParameter) external;

    function setRelayerPercentageFee(uint _relayerPercentageFee) external;

    function setTeleportDAOToken(address _TeleportDAOToken) external;

    function setEpochLength(uint _epochLength) external;

    function setBaseQueries(uint _baseQueries) external;

    function setSubmissionGasUsed(uint _submissionGasUsed) external;

    function checkTxProof(
        bytes32 txid,
        uint blockHeight,
        bytes calldata intermediateNodes,
        uint index
    ) external payable returns (bool);

    function addHeaders(bytes calldata _anchor, bytes calldata _headers) external returns (bool);

    function addHeadersWithRetarget(
        bytes calldata _oldPeriodStartHeader,
        bytes calldata _oldPeriodEndHeader,
        bytes calldata _headers
    ) external returns (bool);

    function ownerAddHeaders(bytes calldata _anchor, bytes calldata _headers) external returns (bool);

    function ownerAddHeadersWithRetarget(
        bytes calldata _oldPeriodStartHeader,
        bytes calldata _oldPeriodEndHeader,
        bytes calldata _headers
    ) external returns (bool);
}

File 2 of 18 : IInstantRouter.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0 <0.8.4;

interface IInstantRouter {
    // Structures

    /// @notice                                 Structure for recording instant requests
    /// @param user                             Address of user who recieves loan
    /// @param collateralPool                   Address of collateral pool
    /// @param collateralToken                  Address of underlying collateral token
    /// @param paybackAmount                    Amount of (loan + instant fee)
    /// @param lockedCollateralPoolTokenAmount  Amount of locked collateral pool token for getting loan
    /// @param deadline                         Deadline for paying back the loan
    /// @param requestCounterOfUser             The index of the request for a specific user
    struct instantRequest {
        address user;
        address collateralPool;
		address collateralToken;
        uint paybackAmount;
        uint lockedCollateralPoolTokenAmount;
        uint deadline;
        uint requestCounterOfUser;
    }

    // Events

    /// @notice                             Emits when a user gets loan for transfer
    /// @param user                         Address of the user who made the request
    /// @param receiver                     Address of the loan receiver
    /// @param loanAmount                   Amount of the loan
    /// @param instantFee                   Amount of the instant loan fee
    /// @param deadline                     Deadline of paying back the loan
    /// @param collateralToken              Address of the collateral token
    /// @param lockedCollateralPoolToken    Amount of collateral pool token that got locked
    event InstantTransfer(
        address indexed user, 
        address receiver, 
        uint loanAmount, 
        uint instantFee, 
        uint indexed deadline, 
        address indexed collateralToken,
        uint lockedCollateralPoolToken,
        uint requestCounterOfUser
    );

    /// @notice                             Emits when a user gets loan for exchange
    /// @param user                         Address of the user who made the request
    /// @param receiver                     Address of the loan receiver
    /// @param loanAmount                   Amount of the loan
    /// @param instantFee                   Amount of the instant loan fee
    /// @param amountOut                    Amount of the output token
    /// @param path                         Path of exchanging tokens
    /// @param isFixed                      Shows whether input or output is fixed in exchange
    /// @param deadline                     Deadline of getting the loan
    /// @param collateralToken              Address of the collateral token
    /// @param lockedCollateralPoolToken    Amount of collateral pool token that got locked
    event InstantExchange(
        address indexed user, 
        address receiver, 
        uint loanAmount, 
        uint instantFee,
        uint amountOut,
        address[] path,
        bool isFixed,
        uint indexed deadline, 
        address indexed collateralToken,
        uint lockedCollateralPoolToken,
        uint requestCounterOfUser
    );

    /// @notice                            Emits when a loan gets paid back
    /// @param user                        Address of user who recieves loan
    /// @param paybackAmount               Amount of (loan + fee) that should be paid back
    /// @param collateralToken             Address of underlying collateral token
    /// @param lockedCollateralPoolToken   Amount of locked collateral pool token for getting loan
    event PaybackLoan(
		address indexed user, 
		uint paybackAmount, 
		address indexed collateralToken, 
		uint lockedCollateralPoolToken,
        uint requestCounterOfUser
	);

    /// @notice                         Emits when a user gets slashed
    /// @param user                     Address of user who recieves loan
    /// @param collateralToken          Address of collateral underlying token
	/// @param slashedAmount            How much user got slashed
	/// @param paybackAmount            Amount of teleBTC paid back to the protocol
	/// @param slasher                  Address of slasher
	/// @param slasherReward            Slasher reward (in collateral token)
    event SlashUser(
		address indexed user, 
		address indexed collateralToken, 
		uint slashedAmount, 
		uint paybackAmount,
        address indexed slasher,
        uint slasherReward,
        uint requestCounterOfUser
	);

    /// @notice                     	Emits when changes made to payback deadline
    event NewPaybackDeadline(
        uint oldPaybackDeadline, 
        uint newPaybackDeadline
    );

    /// @notice                     	Emits when changes made to slasher percentage reward
    event NewSlasherPercentageReward(
        uint oldSlasherPercentageReward, 
        uint newSlasherPercentageReward
    );

    /// @notice                     	Emits when changes made to treasuray overhead percnet
    event NewTreasuaryAddress(
        address oldTreasuaryAddress, 
        address newTreasuaryAddress
    );

    /// @notice                     	Emits when changes made to max price difference percent
    event NewMaxPriceDifferencePercent(
        uint oldMaxPriceDifferencePercent, 
        uint newMaxPriceDifferencePercent
    );

    /// @notice                     	Emits when changes made to TeleBTC address
    event NewTeleBTC(
        address oldTeleBTC, 
        address newTeleBTC
    );

    /// @notice                     	Emits when changes made to relay address
    event NewRelay(
        address oldRelay, 
        address newRelay
    );

    /// @notice                     	Emits when changes made to collateral pool factory address
    event NewCollateralPoolFactory(
        address oldCollateralPoolFactory, 
        address newCollateralPoolFactory
    );

    /// @notice                     	Emits when changes made to price oracle address
    event NewPriceOracle(
        address oldPriceOracle, 
        address newPriceOracle
    );

    /// @notice                     	Emits when changes made to TeleBTC instant pool address
    event NewTeleBTCInstantPool(
        address oldTeleBTCInstantPool, 
        address newTeleBTCInstantPool
    );

    /// @notice                     	Emits when changes made to default exchange connector address
    event NewDefaultExchangeConnector(
        address oldDefaultExchangeConnector, 
        address newDefaultExchangeConnector
    );


    // Read-only functions

    function pause() external;

    function unpause() external;

    function teleBTCInstantPool() external view returns (address);

    function teleBTC() external view returns (address);

    function relay() external view returns (address);

	function collateralPoolFactory() external view returns (address);

	function priceOracle() external view returns (address);

    function slasherPercentageReward() external view returns (uint);

    function paybackDeadline() external view returns (uint);

    function defaultExchangeConnector() external view returns (address);
    
    function getLockedCollateralPoolTokenAmount(address _user, uint _index) external view returns (uint);

    function getUserRequestsLength(address _user) external view returns (uint);

    function getUserRequestDeadline(address _user, uint _index) external view returns (uint);

    function maxPriceDifferencePercent() external view returns (uint);

    function treasuaryAddress() external view returns (address);

    // State-changing functions

    function setPaybackDeadline(uint _paybackDeadline) external;

    function setSlasherPercentageReward(uint _slasherPercentageReward) external;

    function setPriceOracle(address _priceOracle) external;

    function setCollateralPoolFactory(address _collateralPoolFactory) external;

    function setRelay(address _relay) external;

    function setTeleBTC(address _teleBTC) external;

    function setTeleBTCInstantPool(address _teleBTCInstantPool) external;

    function setDefaultExchangeConnector(address _defaultExchangeConnector) external;

    function setTreasuaryAddress(address _treasuaryAddres) external;
    
    function setMaxPriceDifferencePercent(uint _maxPriceDifferencePercent) external;

    function instantCCTransfer(
        address _receiver,
        uint _loanAmount,
        uint _deadline,
        address _collateralPool
    ) external returns (bool);

    function instantCCExchange(
		address _exchangeConnector,
        address _receiver,
        uint _loanAmount, 
        uint _amountOut, 
        address[] memory _path, 
        uint _deadline,
        address _collateralToken,
        bool _isFixedToken
    ) external returns (uint[] memory);

    function payBackLoan(address _user, uint _teleBTCAmount) external returns (bool);

    function slashUser(
		address _user, 
		uint _requestIndex
	) external returns (bool);

}

File 2 of 18 : ILockers.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0 <0.8.4;

import "./ILockersStorage.sol";

interface ILockers is ILockersStorage {

     // Events

    event RequestAddLocker(
        address indexed lockerTargetAddress,
        bytes lockerLockingScript,
        uint TDTLockedAmount,
        uint nativeTokenLockedAmount
    );

    event RevokeAddLockerRequest(
        address indexed lockerTargetAddress,
        bytes lockerLockingScript,
        uint TDTLockedAmount,
        uint nativeTokenLockedAmount
    );

    event RequestInactivateLocker(
        address indexed lockerTargetAddress,
        uint indexed inactivationTimestamp,
        bytes lockerLockingScript,
        uint TDTLockedAmount,
        uint nativeTokenLockedAmount,
        uint netMinted
    );

    event ActivateLocker(
        address indexed lockerTargetAddress,
        bytes lockerLockingScript,
        uint TDTLockedAmount,
        uint nativeTokenLockedAmount,
        uint netMinted
    );

    event LockerAdded(
        address indexed lockerTargetAddress,
        bytes lockerLockingScript,
        uint TDTLockedAmount,
        uint nativeTokenLockedAmount,
        uint addingTime
    );

    event LockerRemoved(
        address indexed lockerTargetAddress,
        bytes lockerLockingScript,
        uint TDTUnlockedAmount,
        uint nativeTokenUnlockedAmount
    );

    event LockerSlashed(
        address indexed lockerTargetAddress,
        uint rewardAmount,
        address indexed rewardRecipient,
        uint amount,
        address indexed recipient,
        uint slashedCollateralAmount,
        uint slashTime,
        bool isForCCBurn
    );

    event LockerLiquidated(
        address indexed lockerTargetAddress,
        address indexed liquidatorAddress,
        uint collateralAmount,
        uint teleBTCAmount,
        uint liquidateTime
    );

    event LockerSlashedCollateralSold(
        address indexed lockerTargetAddress,
        address indexed buyerAddress,
        uint slashingAmount,
        uint teleBTCAmount,
        uint slashingTime
    );

    event CollateralAdded(
        address indexed lockerTargetAddress,
        uint addedCollateral,
        uint totalCollateral,
        uint addingTime
    );

    event CollateralRemoved(
        address indexed lockerTargetAddress,
        uint removedCollateral,
        uint totalCollateral,
        uint removingTime
    );

    event MintByLocker(
        address indexed lockerTargetAddress,
        address indexed receiver,
        uint mintedAmount,
        uint lockerFee,
        uint mintingTime
    );

    event BurnByLocker(
        address indexed lockerTargetAddress,
        uint burntAmount,
        uint lockerFee,
        uint burningTime
    );

    event MinterAdded(
        address indexed account
    );

    event MinterRemoved(
        address indexed account
    );

    event BurnerAdded(
        address indexed account
    );
    
    event BurnerRemoved(
        address indexed account
    );

    event NewLockerPercentageFee(
        uint oldLockerPercentageFee,
        uint newLockerPercentageFee
    );

    event NewPriceWithDiscountRatio(
        uint oldPriceWithDiscountRatio,
        uint newPriceWithDiscountRatio
    );

    event NewMinRequiredTDTLockedAmount(
        uint oldMinRequiredTDTLockedAmount,
        uint newMinRequiredTDTLockedAmount
    );

    event NewMinRequiredTNTLockedAmount(
        uint oldMinRequiredTNTLockedAmount,
        uint newMinRequiredTNTLockedAmount
    );

    event NewPriceOracle(
        address oldPriceOracle,
        address newPriceOracle
    );

    event NewCCBurnRouter(
        address oldCCBurnRouter,
        address newCCBurnRouter
    );

    event NewExchangeConnector(
        address oldExchangeConnector,
        address newExchangeConnector
    );

    event NewTeleportDAOToken(
        address oldTDTToken,
        address newTDTToken
    ); 

    event NewTeleBTC(
        address oldTeleBTC,
        address newTeleBTC
    );   

    event NewCollateralRatio(
        uint oldCollateralRatio,
        uint newCollateralRatio
    );  

    event NewLiquidationRatio(
        uint oldLiquidationRatio,
        uint newLiquidationRatio
    );   

    // Read-only functions

    function getLockerTargetAddress(bytes calldata _lockerLockingScript) external view returns (address);

    function isLocker(bytes calldata _lockerLockingScript) external view returns (bool);

    function getNumberOfLockers() external view returns (uint);

    function getLockerLockingScript(address _lockerTargetAddress) external view returns (bytes memory);

    function isLockerActive(address _lockerTargetAddress) external view returns (bool);

    function getLockerCapacity(address _lockerTargetAddress) external view returns (uint);

    function priceOfOneUnitOfCollateralInBTC() external view returns (uint);

    function isMinter(address account) external view returns(bool);

    function isBurner(address account) external view returns(bool);

    // State-changing functions

    function pauseLocker() external;

    function unPauseLocker() external;

    function addMinter(address _account) external;

    function removeMinter(address _account) external;

    function addBurner(address _account) external;

    function removeBurner(address _account) external;

    function mint(bytes calldata _lockerLockingScript, address _receiver, uint _amount) external returns(uint);

    function burn(bytes calldata _lockerLockingScript, uint256 _amount) external returns(uint);

    function setTeleportDAOToken(address _tdtTokenAddress) external;

    function setLockerPercentageFee(uint _lockerPercentageFee) external;

    function setPriceWithDiscountRatio(uint _priceWithDiscountRatio) external;

    function setMinRequiredTDTLockedAmount(uint _minRequiredTDTLockedAmount) external;

    function setMinRequiredTNTLockedAmount(uint _minRequiredTNTLockedAmount) external;

    function setPriceOracle(address _priceOracle) external;

    function setCCBurnRouter(address _ccBurnRouter) external;

    function setExchangeConnector(address _exchangeConnector) external;

    function setTeleBTC(address _teleBTC) external;

    function setCollateralRatio(uint _collateralRatio) external;

    function setLiquidationRatio(uint _liquidationRatio) external;

    function liquidateLocker(
        address _lockerTargetAddress,
        uint _btcAmount
    ) external returns (bool);

    function addCollateral(
        address _lockerTargetAddress,
        uint _addingNativeTokenAmount
    ) external payable returns (bool);

    function removeCollateral(
        uint _removingNativeTokenAmount
    ) external payable returns (bool);

    function requestToBecomeLocker(
        bytes calldata _lockerLockingScript,
        uint _lockedTDTAmount,
        uint _lockedNativeTokenAmount,
        ScriptTypes _lockerRescueType,
        bytes calldata _lockerRescueScript
    ) external payable returns (bool);

    function revokeRequest() external returns (bool);

    function addLocker(address _lockerTargetAddress) external returns (bool);

    function requestInactivation() external returns (bool);

    function requestActivation() external returns (bool);

    function selfRemoveLocker() external returns (bool);

    function slashIdleLocker(
        address _lockerTargetAddress,
        uint _rewardAmount,
        address _rewardRecipient,
        uint _amount,
        address _recipient
    ) external returns(bool);

    function slashThiefLocker(
        address _lockerTargetAddress,
        uint _rewardAmount,
        address _rewardRecipient,
        uint _amount
    ) external returns(bool);

    function buySlashedCollateralOfLocker(
        address _lockerTargetAddress,
        uint _collateralAmount
    ) external returns (bool);

}

File 2 of 18 : Ownable.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

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

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

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

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

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

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
        _;
    }

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

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

    function _setOwner(address newOwner) private {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}

File 2 of 18 : Address.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize, which returns 0 for contracts in
        // construction, since the code is only stored at the end of the
        // constructor execution.

        uint256 size;
        assembly {
            size := extcodesize(account)
        }
        return size > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://diligence.consensys.net/posts/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.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

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

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

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

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

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

        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

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

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        require(isContract(target), "Address: static call to non-contract");

        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

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

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(isContract(target), "Address: delegate call to non-contract");

        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

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

                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}

File 2 of 18 : ReentrancyGuard.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

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

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

    uint256 private _status;

    constructor() {
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and make it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        // On the first call to nonReentrant, _notEntered will be true
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

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

        _;

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

File 2 of 18 : TypedMemView.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0 <0.8.4;

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

/*
    Original version: https://github.com/summa-tx/memview-sol/blob/main/contracts/TypedMemView.sol
    We made few changes to the original version:
    1. Use solidity version 8 compiler
    2. Remove SafeMath library
    3. Add unchecked in line 522
*/

library TypedMemView {

    // Why does this exist?
    // the solidity `bytes memory` type has a few weaknesses.
    // 1. You can't index ranges effectively
    // 2. You can't slice without copying
    // 3. The underlying data may represent any type
    // 4. Solidity never deallocates memory, and memory costs grow
    //    superlinearly

    // By using a memory view instead of a `bytes memory` we get the following
    // advantages:
    // 1. Slices are done on the stack, by manipulating the pointer
    // 2. We can index arbitrary ranges and quickly convert them to stack types
    // 3. We can insert type info into the pointer, and typecheck at runtime

    // This makes `TypedMemView` a useful tool for efficient zero-copy
    // algorithms.

    // Why bytes29?
    // We want to avoid confusion between views, digests, and other common
    // types so we chose a large and uncommonly used odd number of bytes
    //
    // Note that while bytes are left-aligned in a word, integers and addresses
    // are right-aligned. This means when working in assembly we have to
    // account for the 3 unused bytes on the righthand side
    //
    // First 5 bytes are a type flag.
    // - ff_ffff_fffe is reserved for unknown type.
    // - ff_ffff_ffff is reserved for invalid types/errors.
    // next 12 are memory address
    // next 12 are len
    // bottom 3 bytes are empty

    // Assumptions:
    // - non-modification of memory.
    // - No Solidity updates
    // - - wrt free mem point
    // - - wrt bytes representation in memory
    // - - wrt memory addressing in general

    // Usage:
    // - create type constants
    // - use `assertType` for runtime type assertions
    // - - unfortunately we can't do this at compile time yet :(
    // - recommended: implement modifiers that perform type checking
    // - - e.g.
    // - - `uint40 constant MY_TYPE = 3;`
    // - - ` modifer onlyMyType(bytes29 myView) { myView.assertType(MY_TYPE); }`
    // - instantiate a typed view from a bytearray using `ref`
    // - use `index` to inspect the contents of the view
    // - use `slice` to create smaller views into the same memory
    // - - `slice` can increase the offset
    // - - `slice can decrease the length`
    // - - must specify the output type of `slice`
    // - - `slice` will return a null view if you try to overrun
    // - - make sure to explicitly check for this with `notNull` or `assertType`
    // - use `equal` for typed comparisons.


    // The null view
    bytes29 internal constant NULL = hex"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff";
    uint256 constant LOW_12_MASK = 0xffffffffffffffffffffffff;
    uint8 constant TWELVE_BYTES = 96;

    /**
     * @notice      Returns the encoded hex character that represents the lower 4 bits of the argument.
     * @param _b    The byte
     * @return      char - The encoded hex character
     */
    function nibbleHex(uint8 _b) internal pure returns (uint8 char) {
        // This can probably be done more efficiently, but it's only in error
        // paths, so we don't really care :)
        uint8 _nibble = _b | 0xf0; // set top 4, keep bottom 4
        if (_nibble == 0xf0) {return 0x30;} // 0
        if (_nibble == 0xf1) {return 0x31;} // 1
        if (_nibble == 0xf2) {return 0x32;} // 2
        if (_nibble == 0xf3) {return 0x33;} // 3
        if (_nibble == 0xf4) {return 0x34;} // 4
        if (_nibble == 0xf5) {return 0x35;} // 5
        if (_nibble == 0xf6) {return 0x36;} // 6
        if (_nibble == 0xf7) {return 0x37;} // 7
        if (_nibble == 0xf8) {return 0x38;} // 8
        if (_nibble == 0xf9) {return 0x39;} // 9
        if (_nibble == 0xfa) {return 0x61;} // a
        if (_nibble == 0xfb) {return 0x62;} // b
        if (_nibble == 0xfc) {return 0x63;} // c
        if (_nibble == 0xfd) {return 0x64;} // d
        if (_nibble == 0xfe) {return 0x65;} // e
        if (_nibble == 0xff) {return 0x66;} // f
    }

    /**
     * @notice      Returns a uint16 containing the hex-encoded byte.
     *              `the first 8 bits of encoded is the nibbleHex of top 4 bits of _b`
     *              `the second 8 bits of encoded is the nibbleHex of lower 4 bits of _b`
     * @param _b    The byte
     * @return      encoded - The hex-encoded byte
     */
    function byteHex(uint8 _b) internal pure returns (uint16 encoded) {
        encoded |= nibbleHex(_b >> 4); // top 4 bits
        encoded <<= 8;
        encoded |= nibbleHex(_b); // lower 4 bits
    }

    /**
     * @notice      Encodes the uint256 to hex. `first` contains the encoded top 16 bytes.
     *              `second` contains the encoded lower 16 bytes.
     *
     * @param _b    The 32 bytes as uint256
     * @return      first - The top 16 bytes
     * @return      second - The bottom 16 bytes
     */
    function encodeHex(uint256 _b) internal pure returns (uint256 first, uint256 second) {
        for (uint8 i = 31; i > 15; i -= 1) {
            uint8 _byte = uint8(_b >> (i * 8));
            first |= byteHex(_byte);
            if (i != 16) {
                first <<= 16;
            }
        }

        unchecked {
            // abusing underflow here =_=
            for (uint8 i = 15; i < 255 ; i -= 1) {
                uint8 _byte = uint8(_b >> (i * 8));
                second |= byteHex(_byte);
                if (i != 0) {
                    second <<= 16;
                }
            }
        }
        
    }

    /**
     * @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      Create a mask with the highest `_len` bits set.
     * @param _len  The length
     * @return      mask - The mask
     */
    function leftMask(uint8 _len) private pure returns (uint256 mask) {
        assembly {
        // solium-disable-previous-line security/no-inline-assembly
            mask := sar(
            sub(_len, 1),
            0x8000000000000000000000000000000000000000000000000000000000000000
            )
        }
    }

    /**
     * @notice      Return the null view.
     * @return      bytes29 - The null view
     */
    function nullView() internal pure returns (bytes29) {
        return NULL;
    }

    /**
     * @notice      Check if the view is null.
     * @return      bool - True if the view is null
     */
    function isNull(bytes29 memView) internal pure returns (bool) {
        return memView == NULL;
    }

    /**
     * @notice      Check if the view is not null.
     * @return      bool - True if the view is not null
     */
    function notNull(bytes29 memView) internal pure returns (bool) {
        return !isNull(memView);
    }

    /**
     * @notice          Check if the view is of a valid type and points to a valid location
     *                  in memory.
     * @dev             We perform this check by examining solidity's unallocated memory
     *                  pointer and ensuring that the view's upper bound is less than that.
     * @param memView   The view
     * @return          ret - True if the view is valid
     */
    function isValid(bytes29 memView) internal pure returns (bool ret) {
        if (typeOf(memView) == 0xffffffffff) {return false;}
        uint256 _end = end(memView);
        assembly {
        // solium-disable-previous-line security/no-inline-assembly
            ret := not(gt(_end, mload(0x40)))
        }
    }

    /**
     * @notice          Require that a typed memory view be valid.
     * @dev             Returns the view for easy chaining.
     * @param memView   The view
     * @return          bytes29 - The validated view
     */
    function assertValid(bytes29 memView) internal pure returns (bytes29) {
        require(isValid(memView), "Validity assertion failed");
        return memView;
    }

    /**
     * @notice          Return true if the memview is of the expected type. Otherwise false.
     * @param memView   The view
     * @param _expected The expected type
     * @return          bool - True if the memview is of the expected type
     */
    function isType(bytes29 memView, uint40 _expected) internal pure returns (bool) {
        return typeOf(memView) == _expected;
    }

    /**
     * @notice          Require that a typed memory view has a specific type.
     * @dev             Returns the view for easy chaining.
     * @param memView   The view
     * @param _expected The expected type
     * @return          bytes29 - The view with validated type
     */
    function assertType(bytes29 memView, uint40 _expected) internal pure returns (bytes29) {
        if (!isType(memView, _expected)) {
            (, uint256 g) = encodeHex(uint256(typeOf(memView)));
            (, uint256 e) = encodeHex(uint256(_expected));
            string memory err = string(
                abi.encodePacked(
                    "Type assertion failed. Got 0x",
                    uint80(g),
                    ". Expected 0x",
                    uint80(e)
                )
            );
            revert(err);
        }
        return memView;
    }

    /**
     * @notice          Return an identical view with a different type.
     * @param memView   The view
     * @param _newType  The new type
     * @return          newView - The new view with the specified type
     */
    function castTo(bytes29 memView, uint40 _newType) internal pure returns (bytes29 newView) {
        // then | in the new type
        assembly {
        // solium-disable-previous-line security/no-inline-assembly
        // shift off the top 5 bytes
            newView := or(newView, shr(40, shl(40, memView)))
            newView := or(newView, shl(216, _newType))
        }
    }

    /**
     * @notice          Unsafe raw pointer construction. This should generally not be called
     *                  directly. Prefer `ref` wherever possible.
     * @dev             Unsafe raw pointer construction. This should generally not be called
     *                  directly. Prefer `ref` wherever possible.
     * @param _type     The type
     * @param _loc      The memory address
     * @param _len      The length
     * @return          newView - The new view with the specified type, location and length
     */
    function unsafeBuildUnchecked(uint256 _type, uint256 _loc, uint256 _len) private pure returns (bytes29 newView) {
        assembly {
        // solium-disable-previous-line security/no-inline-assembly
            newView := shl(96, or(newView, _type)) // insert type
            newView := shl(96, or(newView, _loc))  // insert loc
            newView := shl(24, or(newView, _len))  // empty bottom 3 bytes
        }
    }

    /**
     * @notice          Instantiate a new memory view. This should generally not be called
     *                  directly. Prefer `ref` wherever possible.
     * @dev             Instantiate a new memory view. This should generally not be called
     *                  directly. Prefer `ref` wherever possible.
     * @param _type     The type
     * @param _loc      The memory address
     * @param _len      The length
     * @return          newView - The new view with the specified type, location and length
     */
    function build(uint256 _type, uint256 _loc, uint256 _len) internal pure returns (bytes29 newView) {
        uint256 _end = _loc + _len;
        assembly {
        // solium-disable-previous-line security/no-inline-assembly
            if gt(_end, mload(0x40)) {
                _end := 0
            }
        }
        if (_end == 0) {
            return NULL;
        }
        newView = unsafeBuildUnchecked(_type, _loc, _len);
    }

    /**
     * @notice          Instantiate a memory view from a byte array.
     * @dev             Note that due to Solidity memory representation, it is not possible to
     *                  implement a deref, as the `bytes` type stores its len in memory.
     * @param arr       The byte array
     * @param newType   The type
     * @return          bytes29 - The memory view
     */
    function ref(bytes memory arr, uint40 newType) internal pure returns (bytes29) {
        uint256 _len = arr.length;

        uint256 _loc;
        assembly {
        // solium-disable-previous-line security/no-inline-assembly
            _loc := add(arr, 0x20)  // our view is of the data, not the struct
        }

        return build(newType, _loc, _len);
    }

    /**
     * @notice          Return the associated type information.
     * @param memView   The memory view
     * @return          _type - The type associated with the view
     */
    function typeOf(bytes29 memView) internal pure returns (uint40 _type) {
        assembly {
        // solium-disable-previous-line security/no-inline-assembly
        // 216 == 256 - 40
            _type := shr(216, memView) // shift out lower (12 + 12 + 3) bytes
        }
    }

    /**
     * @notice          Optimized type comparison. Checks that the 5-byte type flag is equal.
     * @param left      The first view
     * @param right     The second view
     * @return          bool - True if the 5-byte type flag is equal
     */
    function sameType(bytes29 left, bytes29 right) internal pure returns (bool) {
        // XOR the inputs to check their difference
        return (left ^ right) >> (2 * TWELVE_BYTES) == 0;
    }

    /**
     * @notice          Return the memory address of the underlying bytes.
     * @param memView   The view
     * @return          _loc - The memory address
     */
    function loc(bytes29 memView) internal pure returns (uint96 _loc) {
        uint256 _mask = LOW_12_MASK;  // assembly can't use globals
        assembly {
        // solium-disable-previous-line security/no-inline-assembly
        // 120 bits = 12 bytes (the encoded loc) + 3 bytes (empty low space)
            _loc := and(shr(120, memView), _mask)
        }
    }

    /**
     * @notice          The number of memory words this memory view occupies, rounded up.
     * @param memView   The view
     * @return          uint256 - The number of memory words
     */
    function words(bytes29 memView) internal pure returns (uint256) {
        return (uint256(len(memView)) + 32) / 32;
    }

    /**
     * @notice          The in-memory footprint of a fresh copy of the view.
     * @param memView   The view
     * @return          uint256 - The in-memory footprint of a fresh copy of the view.
     */
    function footprint(bytes29 memView) internal pure returns (uint256) {
        return words(memView) * 32;
    }

    /**
     * @notice          The number of bytes of the view.
     * @param memView   The view
     * @return          _len - The length of the view
     */
    function len(bytes29 memView) internal pure returns (uint96 _len) {
        uint256 _mask = LOW_12_MASK;  // assembly can't use globals
        assembly {
        // solium-disable-previous-line security/no-inline-assembly
            _len := and(shr(24, memView), _mask)
        }
    }

    /**
     * @notice          Returns the endpoint of `memView`.
     * @param memView   The view
     * @return          uint256 - The endpoint of `memView`
     */
    function end(bytes29 memView) internal pure returns (uint256) {
        return loc(memView) + len(memView);
    }

    /**
     * @notice          Safe slicing without memory modification.
     * @param memView   The view
     * @param _index    The start index
     * @param _len      The length
     * @param newType   The new type
     * @return          bytes29 - The new view
     */
    function slice(bytes29 memView, uint256 _index, uint256 _len, uint40 newType) internal pure returns (bytes29) {
        uint256 _loc = loc(memView);

        // Ensure it doesn't overrun the view
        if (_loc + _index + _len > end(memView)) {
            return NULL;
        }

        _loc = _loc + _index;
        return build(newType, _loc, _len);
    }

    /**
     * @notice          Shortcut to `slice`. Gets a view representing the first `_len` bytes.
     * @param memView   The view
     * @param _len      The length
     * @param newType   The new type
     * @return          bytes29 - The new view
     */
    function prefix(bytes29 memView, uint256 _len, uint40 newType) internal pure returns (bytes29) {
        return slice(memView, 0, _len, newType);
    }

    /**
     * @notice          Shortcut to `slice`. Gets a view representing the last `_len` bytes.
     * @param memView   The view
     * @param _len      The length
     * @param newType   The new type
     * @return          bytes29 - The new view
     */
    function postfix(bytes29 memView, uint256 _len, uint40 newType) internal pure returns (bytes29) {
        return slice(memView, uint256(len(memView)) - _len, _len, newType);
    }

    /**
     * @notice          Construct an error message for an indexing overrun.
     * @param _loc      The memory address
     * @param _len      The length
     * @param _index    The index
     * @param _slice    The slice where the overrun occurred
     * @return          err - The err
     */
    function indexErrOverrun(
        uint256 _loc,
        uint256 _len,
        uint256 _index,
        uint256 _slice
    ) internal pure returns (string memory err) {
        (, uint256 a) = encodeHex(_loc);
        (, uint256 b) = encodeHex(_len);
        (, uint256 c) = encodeHex(_index);
        (, uint256 d) = encodeHex(_slice);
        err = string(
            abi.encodePacked(
                "TypedMemView/index - Overran the view. Slice is at 0x",
                uint48(a),
                " with length 0x",
                uint48(b),
                ". Attempted to index at offset 0x",
                uint48(c),
                " with length 0x",
                uint48(d),
                "."
            )
        );
    }

    /**
     * @notice          Load up to 32 bytes from the view onto the stack.
     * @dev             Returns a bytes32 with only the `_bytes` highest bytes set.
     *                  This can be immediately cast to a smaller fixed-length byte array.
     *                  To automatically cast to an integer, use `indexUint`.
     * @param memView   The view
     * @param _index    The index
     * @param _bytes    The bytes length
     * @return          result - The 32 byte result
     */
    function index(bytes29 memView, uint256 _index, uint8 _bytes) internal pure returns (bytes32 result) {
        if (_bytes == 0) {return bytes32(0);}
        if (_index + _bytes > len(memView)) {
            revert(indexErrOverrun(loc(memView), len(memView), _index, uint256(_bytes)));
        }
        require(_bytes <= 32, "TypedMemView/index - Attempted to index more than 32 bytes");

        unchecked {
            uint8 bitLength = _bytes * 8;
            uint256 _loc = loc(memView);
            uint256 _mask = leftMask(bitLength);
            assembly {
                // solium-disable-previous-line security/no-inline-assembly
                result := and(mload(add(_loc, _index)), _mask)
            }   
        }

    }

    /**
     * @notice          Parse an unsigned integer from the view at `_index`.
     * @dev             Requires that the view has >= `_bytes` bytes following that index.
     * @param memView   The view
     * @param _index    The index
     * @param _bytes    The bytes length
     * @return          result - The unsigned integer
     */
    function indexUint(bytes29 memView, uint256 _index, uint8 _bytes) internal pure returns (uint256 result) {
        return uint256(index(memView, _index, _bytes)) >> ((32 - _bytes) * 8);
    }

    /**
     * @notice          Parse an unsigned integer from LE bytes.
     * @param memView   The view
     * @param _index    The index
     * @param _bytes    The bytes length
     * @return          result - The unsigned integer
     */
    function indexLEUint(bytes29 memView, uint256 _index, uint8 _bytes) internal pure returns (uint256 result) {
        return reverseUint256(uint256(index(memView, _index, _bytes)));
    }

    /**
     * @notice          Parse an address from the view at `_index`. Requires that the view have >= 20 bytes
     *                  following that index.
     * @param memView   The view
     * @param _index    The index
     * @return          address - The address
     */
    function indexAddress(bytes29 memView, uint256 _index) internal pure returns (address) {
        return address(uint160(indexUint(memView, _index, 20)));
    }

    /**
     * @notice          Return the keccak256 hash of the underlying memory
     * @param memView   The view
     * @return          digest - The keccak256 hash of the underlying memory
     */
    function keccak(bytes29 memView) internal pure returns (bytes32 digest) {
        uint256 _loc = loc(memView);
        uint256 _len = len(memView);
        assembly {
        // solium-disable-previous-line security/no-inline-assembly
            digest := keccak256(_loc, _len)
        }
    }

    /**
     * @notice          Return the sha2 digest of the underlying memory.
     * @dev             We explicitly deallocate memory afterwards.
     * @param memView   The view
     * @return          digest - The sha2 hash of the underlying memory
     */
    function sha2(bytes29 memView) internal view returns (bytes32 digest) {
        uint256 _loc = loc(memView);
        uint256 _len = len(memView);
        assembly {
        // solium-disable-previous-line security/no-inline-assembly
            let ptr := mload(0x40)
            pop(staticcall(gas(), 2, _loc, _len, ptr, 0x20)) // sha2 #1
            digest := mload(ptr)
        }
    }

    /**
     * @notice          Implements bitcoin's hash160 (rmd160(sha2()))
     * @param memView   The pre-image
     * @return          digest - the Digest
     */
    function hash160(bytes29 memView) internal view returns (bytes20 digest) {
        uint256 _loc = loc(memView);
        uint256 _len = len(memView);
        assembly {
        // solium-disable-previous-line security/no-inline-assembly
            let ptr := mload(0x40)
            pop(staticcall(gas(), 2, _loc, _len, ptr, 0x20)) // sha2
            pop(staticcall(gas(), 3, ptr, 0x20, ptr, 0x20)) // rmd160
            digest := mload(add(ptr, 0xc)) // return value is 0-prefixed.
        }
    }

    /**
     * @notice          Implements bitcoin's hash256 (double sha2)
     * @param memView   A view of the preimage
     * @return          digest - the Digest
     */
    function hash256(bytes29 memView) internal view returns (bytes32 digest) {
        uint256 _loc = loc(memView);
        uint256 _len = len(memView);
        assembly {
        // solium-disable-previous-line security/no-inline-assembly
            let ptr := mload(0x40)
            pop(staticcall(gas(), 2, _loc, _len, ptr, 0x20)) // sha2 #1
            pop(staticcall(gas(), 2, ptr, 0x20, ptr, 0x20)) // sha2 #2
            digest := mload(ptr)
        }
    }

    /**
     * @notice          Return true if the underlying memory is equal. Else false.
     * @param left      The first view
     * @param right     The second view
     * @return          bool - True if the underlying memory is equal
     */
    function untypedEqual(bytes29 left, bytes29 right) internal pure returns (bool) {
        return (loc(left) == loc(right) && len(left) == len(right)) || keccak(left) == keccak(right);
    }

    /**
     * @notice          Return false if the underlying memory is equal. Else true.
     * @param left      The first view
     * @param right     The second view
     * @return          bool - False if the underlying memory is equal
     */
    function untypedNotEqual(bytes29 left, bytes29 right) internal pure returns (bool) {
        return !untypedEqual(left, right);
    }

    /**
     * @notice          Compares type equality.
     * @dev             Shortcuts if the pointers are identical, otherwise compares type and digest.
     * @param left      The first view
     * @param right     The second view
     * @return          bool - True if the types are the same
     */
    function equal(bytes29 left, bytes29 right) internal pure returns (bool) {
        return left == right || (typeOf(left) == typeOf(right) && keccak(left) == keccak(right));
    }

    /**
     * @notice          Compares type inequality.
     * @dev             Shortcuts if the pointers are identical, otherwise compares type and digest.
     * @param left      The first view
     * @param right     The second view
     * @return          bool - True if the types are not the same
     */
    function notEqual(bytes29 left, bytes29 right) internal pure returns (bool) {
        return !equal(left, right);
    }

    /**
     * @notice          Copy the view to a location, return an unsafe memory reference
     * @dev             Super Dangerous direct memory access.
     *
     *                  This reference can be overwritten if anything else modifies memory (!!!).
     *                  As such it MUST be consumed IMMEDIATELY.
     *                  This function is private to prevent unsafe usage by callers.
     * @param memView   The view
     * @param _newLoc   The new location
     * @return          written - the unsafe memory reference
     */
    function unsafeCopyTo(bytes29 memView, uint256 _newLoc) private view returns (bytes29 written) {
        require(notNull(memView), "TypedMemView/copyTo - Null pointer deref");
        require(isValid(memView), "TypedMemView/copyTo - Invalid pointer deref");
        uint256 _len = len(memView);
        uint256 _oldLoc = loc(memView);

        uint256 ptr;
        assembly {
        // solium-disable-previous-line security/no-inline-assembly
            ptr := mload(0x40)
        // revert if we're writing in occupied memory
            if gt(ptr, _newLoc) {
                revert(0x60, 0x20) // empty revert message
            }

        // use the identity precompile to copy
        // guaranteed not to fail, so pop the success
            pop(staticcall(gas(), 4, _oldLoc, _len, _newLoc, _len))
        }

        written = unsafeBuildUnchecked(typeOf(memView), _newLoc, _len);
    }

    /**
     * @notice          Copies the referenced memory to a new loc in memory, returning a `bytes` pointing to
     *                  the new memory
     * @dev             Shortcuts if the pointers are identical, otherwise compares type and digest.
     * @param memView   The view
     * @return          ret - The view pointing to the new memory
     */
    function clone(bytes29 memView) internal view returns (bytes memory ret) {
        uint256 ptr;
        uint256 _len = len(memView);
        assembly {
        // solium-disable-previous-line security/no-inline-assembly
            ptr := mload(0x40) // load unused memory pointer
            ret := ptr
        }
        unsafeCopyTo(memView, ptr + 0x20);
        assembly {
        // solium-disable-previous-line security/no-inline-assembly
            mstore(0x40, add(add(ptr, _len), 0x20)) // write new unused pointer
            mstore(ptr, _len) // write len of new array (in bytes)
        }
    }

    /**
     * @notice          Join the views in memory, return an unsafe reference to the memory.
     * @dev             Super Dangerous direct memory access.
     *
     *                  This reference can be overwritten if anything else modifies memory (!!!).
     *                  As such it MUST be consumed IMMEDIATELY.
     *                  This function is private to prevent unsafe usage by callers.
     * @param memViews  The views
     * @return          unsafeView - The conjoined view pointing to the new memory
     */
    function unsafeJoin(bytes29[] memory memViews, uint256 _location) private view returns (bytes29 unsafeView) {
        assembly {
        // solium-disable-previous-line security/no-inline-assembly
            let ptr := mload(0x40)
        // revert if we're writing in occupied memory
            if gt(ptr, _location) {
                revert(0x60, 0x20) // empty revert message
            }
        }

        uint256 _offset = 0;
        for (uint256 i = 0; i < memViews.length; i ++) {
            bytes29 memView = memViews[i];
            unsafeCopyTo(memView, _location + _offset);
            _offset += len(memView);
        }
        unsafeView = unsafeBuildUnchecked(0, _location, _offset);
    }

    /**
     * @notice          Produce the keccak256 digest of the concatenated contents of multiple views.
     * @param memViews  The views
     * @return          bytes32 - The keccak256 digest
     */
    function joinKeccak(bytes29[] memory memViews) internal view returns (bytes32) {
        uint256 ptr;
        assembly {
        // solium-disable-previous-line security/no-inline-assembly
            ptr := mload(0x40) // load unused memory pointer
        }
        return keccak(unsafeJoin(memViews, ptr));
    }

    /**
     * @notice          Produce the sha256 digest of the concatenated contents of multiple views.
     * @param memViews  The views
     * @return          bytes32 - The sha256 digest
     */
    function joinSha2(bytes29[] memory memViews) internal view returns (bytes32) {
        uint256 ptr;
        assembly {
        // solium-disable-previous-line security/no-inline-assembly
            ptr := mload(0x40) // load unused memory pointer
        }
        return sha2(unsafeJoin(memViews, ptr));
    }

    /**
     * @notice          copies all views, joins them into a new bytearray.
     * @param memViews  The views
     * @return          ret - The new byte array
     */
    function join(bytes29[] memory memViews) internal view returns (bytes memory ret) {
        uint256 ptr;
        assembly {
        // solium-disable-previous-line security/no-inline-assembly
            ptr := mload(0x40) // load unused memory pointer
        }

        bytes29 _newView = unsafeJoin(memViews, ptr + 0x20);
        uint256 _written = len(_newView);
        uint256 _footprint = footprint(_newView);

        assembly {
        // solium-disable-previous-line security/no-inline-assembly
        // store the legnth
            mstore(ptr, _written)
        // new pointer is old + 0x20 + the footprint of the body
            mstore(0x40, add(add(ptr, _footprint), 0x20))
            ret := ptr
        }
    }
}

File 2 of 18 : ScriptTypesEnum.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0 <0.8.4;

    enum ScriptTypes {
        P2PK, // 32 bytes
        P2PKH, // 20 bytes        
        P2SH, // 20 bytes          
        P2WPKH, // 20 bytes          
        P2WSH // 32 bytes               
    }

File 2 of 18 : SafeCast.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow
 * checks.
 *
 * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
 * easily result in undesired exploitation or bugs, since developers usually
 * assume that overflows raise errors. `SafeCast` restores this intuition by
 * reverting the transaction when such an operation overflows.
 *
 * Using this library instead of the unchecked operations eliminates an entire
 * class of bugs, so it's recommended to use it always.
 *
 * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing
 * all math on `uint256` and `int256` and then downcasting.
 */
library SafeCast {
    /**
     * @dev Returns the downcasted uint224 from uint256, reverting on
     * overflow (when the input is greater than largest uint224).
     *
     * Counterpart to Solidity's `uint224` operator.
     *
     * Requirements:
     *
     * - input must fit into 224 bits
     */
    function toUint224(uint256 value) internal pure returns (uint224) {
        require(value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits");
        return uint224(value);
    }

    /**
     * @dev Returns the downcasted uint128 from uint256, reverting on
     * overflow (when the input is greater than largest uint128).
     *
     * Counterpart to Solidity's `uint128` operator.
     *
     * Requirements:
     *
     * - input must fit into 128 bits
     */
    function toUint128(uint256 value) internal pure returns (uint128) {
        require(value <= type(uint128).max, "SafeCast: value doesn't fit in 128 bits");
        return uint128(value);
    }

    /**
     * @dev Returns the downcasted uint96 from uint256, reverting on
     * overflow (when the input is greater than largest uint96).
     *
     * Counterpart to Solidity's `uint96` operator.
     *
     * Requirements:
     *
     * - input must fit into 96 bits
     */
    function toUint96(uint256 value) internal pure returns (uint96) {
        require(value <= type(uint96).max, "SafeCast: value doesn't fit in 96 bits");
        return uint96(value);
    }

    /**
     * @dev Returns the downcasted uint64 from uint256, reverting on
     * overflow (when the input is greater than largest uint64).
     *
     * Counterpart to Solidity's `uint64` operator.
     *
     * Requirements:
     *
     * - input must fit into 64 bits
     */
    function toUint64(uint256 value) internal pure returns (uint64) {
        require(value <= type(uint64).max, "SafeCast: value doesn't fit in 64 bits");
        return uint64(value);
    }

    /**
     * @dev Returns the downcasted uint32 from uint256, reverting on
     * overflow (when the input is greater than largest uint32).
     *
     * Counterpart to Solidity's `uint32` operator.
     *
     * Requirements:
     *
     * - input must fit into 32 bits
     */
    function toUint32(uint256 value) internal pure returns (uint32) {
        require(value <= type(uint32).max, "SafeCast: value doesn't fit in 32 bits");
        return uint32(value);
    }

    /**
     * @dev Returns the downcasted uint16 from uint256, reverting on
     * overflow (when the input is greater than largest uint16).
     *
     * Counterpart to Solidity's `uint16` operator.
     *
     * Requirements:
     *
     * - input must fit into 16 bits
     */
    function toUint16(uint256 value) internal pure returns (uint16) {
        require(value <= type(uint16).max, "SafeCast: value doesn't fit in 16 bits");
        return uint16(value);
    }

    /**
     * @dev Returns the downcasted uint8 from uint256, reverting on
     * overflow (when the input is greater than largest uint8).
     *
     * Counterpart to Solidity's `uint8` operator.
     *
     * Requirements:
     *
     * - input must fit into 8 bits.
     */
    function toUint8(uint256 value) internal pure returns (uint8) {
        require(value <= type(uint8).max, "SafeCast: value doesn't fit in 8 bits");
        return uint8(value);
    }

    /**
     * @dev Converts a signed int256 into an unsigned uint256.
     *
     * Requirements:
     *
     * - input must be greater than or equal to 0.
     */
    function toUint256(int256 value) internal pure returns (uint256) {
        require(value >= 0, "SafeCast: value must be positive");
        return uint256(value);
    }

    /**
     * @dev Returns the downcasted int128 from int256, reverting on
     * overflow (when the input is less than smallest int128 or
     * greater than largest int128).
     *
     * Counterpart to Solidity's `int128` operator.
     *
     * Requirements:
     *
     * - input must fit into 128 bits
     *
     * _Available since v3.1._
     */
    function toInt128(int256 value) internal pure returns (int128) {
        require(value >= type(int128).min && value <= type(int128).max, "SafeCast: value doesn't fit in 128 bits");
        return int128(value);
    }

    /**
     * @dev Returns the downcasted int64 from int256, reverting on
     * overflow (when the input is less than smallest int64 or
     * greater than largest int64).
     *
     * Counterpart to Solidity's `int64` operator.
     *
     * Requirements:
     *
     * - input must fit into 64 bits
     *
     * _Available since v3.1._
     */
    function toInt64(int256 value) internal pure returns (int64) {
        require(value >= type(int64).min && value <= type(int64).max, "SafeCast: value doesn't fit in 64 bits");
        return int64(value);
    }

    /**
     * @dev Returns the downcasted int32 from int256, reverting on
     * overflow (when the input is less than smallest int32 or
     * greater than largest int32).
     *
     * Counterpart to Solidity's `int32` operator.
     *
     * Requirements:
     *
     * - input must fit into 32 bits
     *
     * _Available since v3.1._
     */
    function toInt32(int256 value) internal pure returns (int32) {
        require(value >= type(int32).min && value <= type(int32).max, "SafeCast: value doesn't fit in 32 bits");
        return int32(value);
    }

    /**
     * @dev Returns the downcasted int16 from int256, reverting on
     * overflow (when the input is less than smallest int16 or
     * greater than largest int16).
     *
     * Counterpart to Solidity's `int16` operator.
     *
     * Requirements:
     *
     * - input must fit into 16 bits
     *
     * _Available since v3.1._
     */
    function toInt16(int256 value) internal pure returns (int16) {
        require(value >= type(int16).min && value <= type(int16).max, "SafeCast: value doesn't fit in 16 bits");
        return int16(value);
    }

    /**
     * @dev Returns the downcasted int8 from int256, reverting on
     * overflow (when the input is less than smallest int8 or
     * greater than largest int8).
     *
     * Counterpart to Solidity's `int8` operator.
     *
     * Requirements:
     *
     * - input must fit into 8 bits.
     *
     * _Available since v3.1._
     */
    function toInt8(int256 value) internal pure returns (int8) {
        require(value >= type(int8).min && value <= type(int8).max, "SafeCast: value doesn't fit in 8 bits");
        return int8(value);
    }

    /**
     * @dev Converts an unsigned uint256 into a signed int256.
     *
     * Requirements:
     *
     * - input must be less than or equal to maxInt256.
     */
    function toInt256(uint256 value) internal pure returns (int256) {
        // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
        require(value <= uint256(type(int256).max), "SafeCast: value doesn't fit in an int256");
        return int256(value);
    }
}

File 2 of 18 : IERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

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

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

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

    /**
     * @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);
}

File 2 of 18 : ILockersStorage.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0 <0.8.4;

import "../../types/DataTypes.sol";

interface ILockersStorage {
    // Read-only functions

    function TeleportDAOToken() external view returns(address);

    function teleBTC() external view returns(address);

    function ccBurnRouter() external view returns(address);

    function exchangeConnector() external view returns(address);

    function priceOracle() external view returns(address);

    function minRequiredTDTLockedAmount() external view returns(uint);

    function minRequiredTNTLockedAmount() external view returns(uint);

    function lockerPercentageFee() external view returns(uint);

    function collateralRatio() external view returns(uint);

    function liquidationRatio() external view returns(uint);

    function priceWithDiscountRatio() external view returns(uint);

    function totalNumberOfCandidates() external view returns(uint);

    function totalNumberOfLockers() external view returns(uint);
  
}

File 2 of 18 : DataTypes.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0 <0.8.4;

import "./ScriptTypesEnum.sol";

library DataTypes {

    /// @notice                             Structure for registering lockers
    /// @dev
    /// @param lockerLockingScript          Locker redeem script
    /// @param lockerRescueType             Locker script type in case of getting BTCs back
    /// @param lockerRescueScript           Locker script in case of getting BTCs back
    /// @param TDTLockedAmount              Bond amount of locker in TDT
    /// @param nativeTokenLockedAmount      Bond amount of locker in native token of the target chain
    /// @param netMinted                    Total minted - total burnt
    /// @param slashingTeleBTCAmount        Total amount of teleBTC a locker must be slashed
    /// @param reservedNativeTokenForSlash  Total native token reserved to support slashing teleBTC
    /// @param isLocker                     Indicates that is already a locker or not
    /// @param isCandidate                  Indicates that is a candidate or not
    /// @param isScriptHash                 Shows if it's script hash
    ///                                     has enough collateral to accept more minting requests)
    struct locker {
        bytes lockerLockingScript;
        ScriptTypes lockerRescueType;
        bytes lockerRescueScript;
        uint TDTLockedAmount;
        uint nativeTokenLockedAmount;
        uint netMinted;
        uint slashingTeleBTCAmount;
        uint reservedNativeTokenForSlash;
        bool isLocker;
        bool isCandidate;
        bool isScriptHash;
    }

    struct lockersLibConstants {
        uint OneHundredPercent;
        uint HealthFactor;
        uint UpperHealthFactor;
        uint MaxLockerFee;
        uint NativeTokenDecimal;
        address NativeToken;
    }

    struct lockersLibParam {
        address teleportDAOToken;
        address teleBTC;
        address ccBurnRouter;
        address exchangeConnector;
        address priceOracle;

        uint minRequiredTDTLockedAmount;
        uint minRequiredTNTLockedAmount;
        uint lockerPercentageFee;
        uint collateralRatio;
        uint liquidationRatio;
        uint priceWithDiscountRatio;
        uint inactivationDelay;
    }
}

File 2 of 18 : Context.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

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

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

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

Contract ABI

[{"inputs":[{"internalType":"uint256","name":"_startingBlockNumber","type":"uint256"},{"internalType":"uint256","name":"_protocolPercentageFee","type":"uint256"},{"internalType":"uint256","name":"_chainId","type":"uint256"},{"internalType":"uint256","name":"_appId","type":"uint256"},{"internalType":"address","name":"_relay","type":"address"},{"internalType":"address","name":"_lockers","type":"address"},{"internalType":"address","name":"_teleBTC","type":"address"},{"internalType":"address","name":"_treasury","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes","name":"lockerLockingScript","type":"bytes"},{"indexed":false,"internalType":"uint256","name":"lockerScriptType","type":"uint256"},{"indexed":false,"internalType":"address","name":"lockerTargetAddress","type":"address"},{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"inputAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"receivedAmount","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"speed","type":"uint256"},{"indexed":false,"internalType":"address","name":"teleporter","type":"address"},{"indexed":false,"internalType":"uint256","name":"teleporterFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"relayFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"protocolFee","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"bitcoinTxId","type":"bytes32"}],"name":"CCTransfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldInstantRouter","type":"address"},{"indexed":false,"internalType":"address","name":"newInstantRouter","type":"address"}],"name":"NewInstantRouter","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldLockers","type":"address"},{"indexed":false,"internalType":"address","name":"newLockers","type":"address"}],"name":"NewLockers","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldProtocolPercentageFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newProtocolPercentageFee","type":"uint256"}],"name":"NewProtocolPercentageFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldRelay","type":"address"},{"indexed":false,"internalType":"address","name":"newRelay","type":"address"}],"name":"NewRelay","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldTeleBTC","type":"address"},{"indexed":false,"internalType":"address","name":"newTeleBTC","type":"address"}],"name":"NewTeleBTC","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldTreasury","type":"address"},{"indexed":false,"internalType":"address","name":"newTreasury","type":"address"}],"name":"NewTreasury","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":[],"name":"appId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"_version","type":"bytes4"},{"internalType":"bytes","name":"_vin","type":"bytes"},{"internalType":"bytes","name":"_vout","type":"bytes"},{"internalType":"bytes4","name":"_locktime","type":"bytes4"},{"internalType":"uint256","name":"_blockNumber","type":"uint256"},{"internalType":"bytes","name":"_intermediateNodes","type":"bytes"},{"internalType":"uint256","name":"_index","type":"uint256"},{"internalType":"bytes","name":"_lockerLockingScript","type":"bytes"}],"name":"ccTransfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"ccTransferRequests","outputs":[{"internalType":"uint256","name":"inputAmount","type":"uint256"},{"internalType":"address","name":"recipientAddress","type":"address"},{"internalType":"uint256","name":"fee","type":"uint256"},{"internalType":"uint256","name":"speed","type":"uint256"},{"internalType":"bool","name":"isUsed","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"chainId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"instantRouter","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_txId","type":"bytes32"}],"name":"isRequestUsed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lockers","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"protocolPercentageFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"relay","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_instantRouter","type":"address"}],"name":"setInstantRouter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_lockers","type":"address"}],"name":"setLockers","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_protocolPercentageFee","type":"uint256"}],"name":"setProtocolPercentageFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_relay","type":"address"}],"name":"setRelay","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_teleBTC","type":"address"}],"name":"setTeleBTC","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_treasury","type":"address"}],"name":"setTreasury","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"startingBlockNumber","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"teleBTC","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"treasury","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]

60806040523480156200001157600080fd5b5060405162003d2538038062003d258339810160408190526200003491620004a4565b6200003f3362000097565b600180556002889055600386905560048590556200005d87620000e7565b620000688462000197565b620000738362000253565b6200007e826200030f565b6200008981620003cb565b505050505050505062000527565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b806127101015620001565760405162461bcd60e51b815260206004820152602e60248201527f43435472616e73666572526f757465723a2070726f746f636f6c20666565206960448201526d73206f7574206f662072616e676560901b60648201526084015b60405180910390fd5b60055460408051918252602082018390527f7d205f60891f60851c61cd244ba387d72b6589e7c0ac41b9d909ab0ce2c2a53b910160405180910390a1600555565b806001600160a01b038116620001e95760405162461bcd60e51b8152602060048201526021602482015260008051602062003d058339815191526044820152606f60f81b60648201526084016200014d565b600654604080516001600160a01b03928316815291841660208301527f4c28a3f61a715259c4dc930c23e7423b8fa52e13232c061a6e488729c66184f4910160405180910390a150600680546001600160a01b0319166001600160a01b0392909216919091179055565b806001600160a01b038116620002a55760405162461bcd60e51b8152602060048201526021602482015260008051602062003d058339815191526044820152606f60f81b60648201526084016200014d565b600754604080516001600160a01b03928316815291841660208301527ffa021c2ec0f46429a53e7f88b88065e40001f50c9d38b2e8b713beaf9828d4c3910160405180910390a150600780546001600160a01b0319166001600160a01b0392909216919091179055565b806001600160a01b038116620003615760405162461bcd60e51b8152602060048201526021602482015260008051602062003d058339815191526044820152606f60f81b60648201526084016200014d565b600854604080516001600160a01b03928316815291841660208301527f36a4c08a38b736dcecb6c328dba61238529620e83ccb23db2cc43cd34ec26096910160405180910390a150600880546001600160a01b0319166001600160a01b0392909216919091179055565b806001600160a01b0381166200041d5760405162461bcd60e51b8152602060048201526021602482015260008051602062003d058339815191526044820152606f60f81b60648201526084016200014d565b600a54604080516001600160a01b03928316815291841660208301527f567657fa3f286518b318f4a29870674f433f622fdfc819691acb13105b228225910160405180910390a150600a80546001600160a01b0319166001600160a01b0392909216919091179055565b80516001600160a01b03811681146200049f57600080fd5b919050565b600080600080600080600080610100898b031215620004c1578384fd5b88519750602089015196506040890151955060608901519450620004e860808a0162000487565b9350620004f860a08a0162000487565b92506200050860c08a0162000487565b91506200051860e08a0162000487565b90509295985092959890939650565b6137ce80620005376000396000f3fe60806040526004361061012e5760003560e01c806380bc4726116100ab578063b14d35321161006f578063b14d35321461037e578063b59589d11461039e578063c805f68b146103be578063e35a0a88146103de578063f0f44260146103fe578063f2fde38b1461041e57610135565b806380bc4726146102ea578063870ba6cf1461030a5780638d3d78cd1461032a5780638da5cb5b1461034a5780639a8a05921461036857610135565b8063765f0233116100f2578063765f0233146101e857806378943c73146102715780637ed55748146102945780637f0f1817146102b457806380afdea8146102d457610135565b80631812adb61461013a5780631876dec41461016357806361d027b31461019b57806370872aa5146101bb578063715018a6146101d157610135565b3661013557005b600080fd5b34801561014657600080fd5b5061015060055481565b6040519081526020015b60405180910390f35b34801561016f57600080fd5b50600754610183906001600160a01b031681565b6040516001600160a01b03909116815260200161015a565b3480156101a757600080fd5b50600a54610183906001600160a01b031681565b3480156101c757600080fd5b5061015060025481565b3480156101dd57600080fd5b506101e661043e565b005b3480156101f457600080fd5b5061023f61020336600461327a565b600b602052600090815260409020805460018201546002830154600384015460049094015492936001600160a01b039092169290919060ff1685565b604080519586526001600160a01b0390941660208601529284019190915260608301521515608082015260a00161015a565b61028461027f3660046132aa565b610473565b604051901515815260200161015a565b3480156102a057600080fd5b506101e66102af36600461327a565b6109a4565b3480156102c057600080fd5b506101e66102cf366004613222565b6109da565b3480156102e057600080fd5b5061015060045481565b3480156102f657600080fd5b5061028461030536600461327a565b610a38565b34801561031657600080fd5b506101e6610325366004613222565b610a63565b34801561033657600080fd5b50600854610183906001600160a01b031681565b34801561035657600080fd5b506000546001600160a01b0316610183565b34801561037457600080fd5b5061015060035481565b34801561038a57600080fd5b506101e6610399366004613222565b610abd565b3480156103aa57600080fd5b50600654610183906001600160a01b031681565b3480156103ca57600080fd5b506101e66103d9366004613222565b610b17565b3480156103ea57600080fd5b50600954610183906001600160a01b031681565b34801561040a57600080fd5b506101e6610419366004613222565b610b71565b34801561042a57600080fd5b506101e6610439366004613222565b610bcb565b6000546001600160a01b031633146104715760405162461bcd60e51b81526004016104689061355a565b60405180910390fd5b565b6000600260015414156104c85760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610468565b600260018190555487101561052b5760405162461bcd60e51b8152602060048201526024808201527f43435472616e73666572526f757465723a207265717565737420697320746f6f604482015263081bdb1960e21b6064820152608401610468565b60006105708d8d8d8d8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152508f9250610c63915050565b6000818152600b602052604090206004015490915060ff16156105ec5760405162461bcd60e51b815260206004820152602e60248201527f43435472616e73666572526f757465723a20726571756573742068617320626560448201526d656e2075736564206265666f726560901b6064820152608401610468565b6001600160e01b03198916156106555760405162461bcd60e51b815260206004820152602860248201527f43435472616e73666572526f757465723a206c6f636b2074696d65206973206e6044820152676f6e202d7a65726f60c01b6064820152608401610468565b6106db84848080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050508c8c8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250869250610d59915050565b61071e818989898080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152508b9250611182915050565b6107905760405162461bcd60e51b815260206004820152603860248201527f43435472616e73666572526f757465723a207472616e73616374696f6e20686160448201527f73206e6f74206265656e2066696e616c697a65642079657400000000000000006064820152608401610468565b6000818152600b602052604090206003015461094b5760008060006107ec87878080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152508892506112f7915050565b6000878152600b6020526040908190206003810154600190910154915194975092955090935090916001600160a01b039091169061082d908a908a9061341e565b60405190819003812060075463626885b560e11b835290917f4e5aca8a7de151db886a79d287fb0335e05705a91fd8071e29a61f08346197b9916000916001600160a01b039091169063c4d10b6a9061088c908f908f906004016134a9565b60206040518083038186803b1580156108a457600080fd5b505afa1580156108b8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108dc919061323e565b60008a8152600b60205260409020548933604080519586526001600160a01b039485166020870152850192909252606084015216608082015260a08101869052600060c082015260e0810187905261010081018990526101200160405180910390a46001945050505050610991565b60008060006107ec87878080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152508892506113af915050565b600180559b9a5050505050505050505050565b6000546001600160a01b031633146109ce5760405162461bcd60e51b81526004016104689061355a565b6109d78161149a565b50565b806001600160a01b038116610a015760405162461bcd60e51b815260040161046890613519565b6000546001600160a01b03163314610a2b5760405162461bcd60e51b81526004016104689061355a565b610a3482611544565b5050565b6000818152600b602052604081206004015460ff16610a58576000610a5b565b60015b90505b919050565b806001600160a01b038116610a8a5760405162461bcd60e51b815260040161046890613519565b6000546001600160a01b03163314610ab45760405162461bcd60e51b81526004016104689061355a565b610a34826115d5565b806001600160a01b038116610ae45760405162461bcd60e51b815260040161046890613519565b6000546001600160a01b03163314610b0e5760405162461bcd60e51b81526004016104689061355a565b610a3482611666565b806001600160a01b038116610b3e5760405162461bcd60e51b815260040161046890613519565b6000546001600160a01b03163314610b685760405162461bcd60e51b81526004016104689061355a565b610a34826116f7565b806001600160a01b038116610b985760405162461bcd60e51b815260040161046890613519565b6000546001600160a01b03163314610bc25760405162461bcd60e51b81526004016104689061355a565b610a3482611788565b6000546001600160a01b03163314610bf55760405162461bcd60e51b81526004016104689061355a565b6001600160a01b038116610c5a5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401610468565b6109d781611819565b600080600286868686604051602001610c7f94939291906133cb565b60408051601f1981840301815290829052610c999161342e565b602060405180830381855afa158015610cb6573d6000803e3d6000fd5b5050506040513d601f19601f82011682018060405250810190610cd99190613292565b90506000600282604051602001610cf291815260200190565b60408051601f1981840301815290829052610d0c9161342e565b602060405180830381855afa158015610d29573d6000803e3d6000fd5b5050506040513d601f19601f82011682018060405250810190610d4c9190613292565b925050505b949350505050565b60075460405163aff2faa360e01b81526001600160a01b039091169063aff2faa390610d899086906004016134d8565b60206040518083038186803b158015610da157600080fd5b505afa158015610db5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610dd9919061325a565b610e4d576040805162461bcd60e51b81526020600482015260248101919091527f43435472616e73666572526f757465723a206e6f206c6f636b6572207769746860448201527f2074686520676976656e206c6f636b696e6720736372697074206578697374736064820152608401610468565b610e8a6040518060a001604052806000815260200160006001600160a01b0316815260200160008152602001600081526020016000151581525090565b6060610e968486611869565b6001600160401b03909116808452909150610f025760405162461bcd60e51b815260206004820152602660248201527f43435472616e73666572526f757465723a20696e70757420616d6f756e74206960448201526573207a65726f60d01b6064820152608401610468565b600354610f0e826119e1565b60ff1614610f705760405162461bcd60e51b815260206004820152602960248201527f43435472616e73666572526f757465723a20636861696e206964206973206e6f6044820152681d0818dbdc9c9958dd60ba1b6064820152608401610468565b600454610f7c826119fb565b61ffff1614610fdd5760405162461bcd60e51b815260206004820152602760248201527f43435472616e73666572526f757465723a20617070206964206973206e6f742060448201526618dbdc9c9958dd60ca1b6064820152608401610468565b6000610fe882611a16565b61ffff1690506127108111156110595760405162461bcd60e51b815260206004820152603060248201527f43435472616e73666572526f757465723a2070657263656e746167652066656560448201526f206973206f7574206f662072616e676560801b6064820152608401610468565b8251612710906110699083613639565b6110739190613619565b604084015261108182611a26565b6001600160a01b0316602084015261109882611a41565b60ff166060840181905215806110b2575082606001516001145b61110e5760405162461bcd60e51b815260206004820152602760248201527f43435472616e73666572526f757465723a207370656564206973206f7574206f604482015266662072616e676560c81b6064820152608401610468565b50506001608082018181526000938452600b6020908152604094859020845181559084015192810180546001600160a01b0319166001600160a01b0390941693909317909255928201516002820155606090910151600382015590516004909101805460ff19169115159190911790555050565b60065460405163d0a8973960e01b8152600481018590526000602482018190529182916001600160a01b039091169063d0a897399060440160206040518083038186803b1580156111d257600080fd5b505afa1580156111e6573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061120a9190613292565b9050803410156112715760405162461bcd60e51b815260206004820152602c60248201527f43435472616e73666572526f757465723a207061696420666565206973206e6f60448201526b1d081cdd59999a58da595b9d60a21b6064820152608401610468565b6006546040516000916112ce916001600160a01b039091169061129e908a908a908a908a90602401613479565b60408051601f198184030181529190526020810180516001600160e01b0316634354da2560e01b17905284611a50565b90506112e3336112de8434613681565b611a80565b80806020019051810190610d4c919061325a565b60008060006113068585611b9e565b6008546000888152600b60205260409081902060010154905163a9059cbb60e01b81526001600160a01b03918216600482015260248101869052949750929550909350169063a9059cbb906044015b602060405180830381600087803b15801561136f57600080fd5b505af1158015611383573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113a7919061325a565b509250925092565b60008060006113be8585611b9e565b60085460095460405163095ea7b360e01b81526001600160a01b03918216600482015260248101869052949750929550909350169063095ea7b390604401602060405180830381600087803b15801561141657600080fd5b505af115801561142a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061144e919061325a565b506009546000858152600b602052604090819020600101549051631b6396ab60e31b81526001600160a01b0391821660048201526024810186905291169063db1cb55890604401611355565b8061271010156115035760405162461bcd60e51b815260206004820152602e60248201527f43435472616e73666572526f757465723a2070726f746f636f6c20666565206960448201526d73206f7574206f662072616e676560901b6064820152608401610468565b60055460408051918252602082018390527f7d205f60891f60851c61cd244ba387d72b6589e7c0ac41b9d909ab0ce2c2a53b910160405180910390a1600555565b806001600160a01b03811661156b5760405162461bcd60e51b815260040161046890613519565b600854604080516001600160a01b03928316815291841660208301527f36a4c08a38b736dcecb6c328dba61238529620e83ccb23db2cc43cd34ec26096910160405180910390a150600880546001600160a01b0319166001600160a01b0392909216919091179055565b806001600160a01b0381166115fc5760405162461bcd60e51b815260040161046890613519565b600954604080516001600160a01b03928316815291841660208301527f12ad124e13af4c31364ad22aa74320e167b37f005dafb75d71a210c49da3629e910160405180910390a150600980546001600160a01b0319166001600160a01b0392909216919091179055565b806001600160a01b03811661168d5760405162461bcd60e51b815260040161046890613519565b600754604080516001600160a01b03928316815291841660208301527ffa021c2ec0f46429a53e7f88b88065e40001f50c9d38b2e8b713beaf9828d4c3910160405180910390a150600780546001600160a01b0319166001600160a01b0392909216919091179055565b806001600160a01b03811661171e5760405162461bcd60e51b815260040161046890613519565b600654604080516001600160a01b03928316815291841660208301527f4c28a3f61a715259c4dc930c23e7423b8fa52e13232c061a6e488729c66184f4910160405180910390a150600680546001600160a01b0319166001600160a01b0392909216919091179055565b806001600160a01b0381166117af5760405162461bcd60e51b815260040161046890613519565b600a54604080516001600160a01b03928316815291841660208301527f567657fa3f286518b318f4a29870674f433f622fdfc819691acb13105b228225910160405180910390a150600a80546001600160a01b0319166001600160a01b0392909216919091179055565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b600060608161188061187b8683611dc4565b611dea565b905062ffffff1980821614156118d85760405162461bcd60e51b815260206004820152601b60248201527f426974636f696e48656c7065723a20766f7574206973206e756c6c00000000006044820152606401610468565b60008060008060006118eb866000611f14565b6001600160401b0316905060005b818110156119d35761190b878261205d565b955061191686612174565b9450611921866121d1565b935061192c8461221b565b925062ffffff1980841614156119af578960405160200161194d919061342e565b604051602081830303815290604052805190602001206119728662ffffff19166122f7565b604051602001611982919061342e565b6040516020818303038152906040528051906020012014156119aa576119a786612347565b98505b6119c1565b6119be62ffffff1984166122f7565b97505b806119cb81613713565b9150506118f9565b505050505050509250929050565b6000806119f08360008061236c565b600101519392505050565b600080611a0b836001600261236c565b600201519392505050565b600080611a0b836017601861236c565b600080611a36836003601661236c565b601401519392505050565b6000806119f08360198061236c565b6060611a76848484604051806060016040528060298152602001613770602991396123f1565b90505b9392505050565b80471015611ad05760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a20696e73756666696369656e742062616c616e63650000006044820152606401610468565b6000826001600160a01b03168260405160006040518083038185875af1925050503d8060008114611b1d576040519150601f19603f3d011682016040523d82523d6000602084013e611b22565b606091505b5050905080611b995760405162461bcd60e51b815260206004820152603a60248201527f416464726573733a20756e61626c6520746f2073656e642076616c75652c207260448201527f6563697069656e74206d617920686176652072657665727465640000000000006064820152608401610468565b505050565b6007546000828152600b60205260408082205490516307f3f86760e21b815291928392839283926001600160a01b031691631fcfe19c91611be5918a9130916004016134eb565b602060405180830381600087803b158015611bff57600080fd5b505af1158015611c13573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c379190613292565b6005546000878152600b602052604090205491925061271091611c5a9190613639565b611c649190613619565b6000868152600b602052604090206002015490935091508115611d15576008546001600160a01b031663a9059cbb336040516001600160e01b031960e084901b1681526001600160a01b03909116600482015260248101859052604401602060405180830381600087803b158015611cdb57600080fd5b505af1158015611cef573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d13919061325a565b505b8215611da557600854600a5460405163a9059cbb60e01b81526001600160a01b0391821660048201526024810186905291169063a9059cbb90604401602060405180830381600087803b158015611d6b57600080fd5b505af1158015611d7f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611da3919061325a565b505b81611db08483613681565b611dba9190613681565b9350509250925092565b815160009060208401611ddf64ffffffffff8516828461250e565b925050505b92915050565b60008181611e00815b62ffffff19841690612552565b50601884901c6001600160601b0316611e1f5762ffffff199250611f0d565b6000611e2c856000611f14565b9050601885901c6001600160601b03166001600160401b038216611e595762ffffff195b94505050611f0d565b6000611e648361262c565b60ff16905060005b836001600160401b0316811015611edd57828210611e945762ffffff19965050505050611f0d565b6000611eb2611ea38486613681565b62ffffff198b1690600e612685565b9050611ebd816126b9565b611ec7908461358f565b9250508080611ed590613713565b915050611e6c565b50818114611ef45762ffffff199550505050611f0d565b6301000000600160d81b038716600f60d81b1795505050505b5050919050565b600080611f2962ffffff198516846001612702565b905060fc8111611f4457611f3c81612732565b915050611de4565b8060fd1415611faa57611f72611f6d611f5e85600161358f565b62ffffff19871690600261279e565b612732565b9150611f7d8261262c565b60ff16600314611fa557611fa3611f9e62ffffff19861685600360006128e8565b612958565b505b612056565b8060fe1415611fff57611fd3611f6d611fc485600161358f565b62ffffff19871690600461279e565b9150611fde8261262c565b60ff16600514611fa557611fa3611f9e62ffffff19861685600560006128e8565b8060ff141561205657612028611f6d61201985600161358f565b62ffffff19871690600861279e565b91506120338261262c565b60ff1660091461205657612054611f9e62ffffff19861685600960006128e8565b505b5092915050565b600082600f61206b81611df3565b506000612079866000611f14565b6001600160401b03169050601886901c6001600160601b03168186106120d55760405162461bcd60e51b81526020600482015260116024820152702b37baba103932b0b21037bb32b9393ab760791b6044820152606401610468565b60006120e08361262c565b60ff1690506000805b888110156121365761210d6120fe8486613681565b62ffffff198c1690600e612685565b9150612118826126b9565b612122908461358f565b925061212f60018261358f565b90506120e9565b50612144611ea38385613681565b90506000612151826126b9565b905061216662ffffff198b168483600d6128e8565b9a9950505050505050505050565b600081600d61218281611df3565b506000612190856008611f14565b90506121c861219e8261262c565b6121a99060086135d2565b60ff166001600160401b03831660075b62ffffff1989169291906128e8565b95945050505050565b600081600d6121df81611df3565b5060006121ed856008611f14565b90506121c86008826121fe8461262c565b60ff1661220b91906135a7565b6001600160401b031660076121b9565b600081600761222981611df3565b506000612237856000611f14565b90506000612251611f6d62ffffff19881660026001612702565b9050604d826001600160401b0316118061227457506004826001600160401b0316105b80612291575061228c62ffffff198716600180612702565b606a14155b806122c157506122a2600283613698565b6001600160401b03166122be62ffffff19881660026001612702565b14155b156122d05762ffffff19611e50565b6122ed62ffffff19871660036001600160401b038416600c6128e8565b9695505050505050565b606060008061230f8460181c6001600160601b031690565b60405193508392506001600160601b031690506123368461233184602061358f565b6129df565b508181016020016040529052919050565b600081600d61235581611df3565b50610d51611f6d62ffffff1986166000600861279e565b60606000835b61237d84600161358f565b8110156123e8578581815181106123a457634e487b7160e01b600052603260045260246000fd5b602001015160f81c60f81b915082826040516020016123c492919061344a565b604051602081830303815290604052925080806123e090613713565b915050612372565b50509392505050565b6060824710156124525760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b6064820152608401610468565b843b6124a05760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610468565b600080866001600160a01b031685876040516124bc919061342e565b60006040518083038185875af1925050503d80600081146124f9576040519150601f19603f3d011682016040523d82523d6000602084013e6124fe565b606091505b5091509150610d4c828286612b37565b60008061251b838561358f565b905060405181111561252b575060005b8061253d5762ffffff19915050611a79565b5050606092831b9190911790911b1760181b90565b600061255e8383612b70565b61262557600061257d6125718560d81c90565b64ffffffffff16612b93565b91505060006125928464ffffffffff16612b93565b6040517f5479706520617373657274696f6e206661696c65642e20476f7420307800000060208201526001600160b01b031960b086811b8216603d8401526c05c408af0e0cac6e8cac84060f609b1b604784015283901b16605482015290925060009150605e015b60405160208183030381529060405290508060405162461bcd60e51b815260040161046891906134d8565b5090919050565b600060fc826001600160401b03161161264757506001610a5e565b61ffff826001600160401b03161161266157506003610a5e565b63ffffffff826001600160401b03161161267d57506005610a5e565b506009610a5e565b6000611a76848461269f8760181c6001600160601b031690565b6001600160601b03166126b29190613681565b85856128e8565b600081600e6126c781611df3565b5060006126d5856008611f14565b9050806001600160401b03166126ea8261262c565b60ff166126f7919061358f565b6121c890600861358f565b600061270f8260206136c0565b61271a906008613658565b60ff16612728858585612c3f565b901c949350505050565b60006001600160401b0382111561279a5760405162461bcd60e51b815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203660448201526534206269747360d01b6064820152608401610468565b5090565b6000611a766127ae858585612c3f565b600881811c7eff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff167fff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff009290911b9190911617601081811c7dffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff167fffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff00009290911b9190911617602081811c7bffffffff00000000ffffffff00000000ffffffff00000000ffffffff167fffffffff00000000ffffffff00000000ffffffff00000000ffffffff000000009290911b9190911617604081811c77ffffffffffffffff0000000000000000ffffffffffffffff1677ffffffffffffffff0000000000000000ffffffffffffffff199290911b9190911617608081811c91901b1790565b6000806128fe8660781c6001600160601b031690565b6001600160601b0316905061291286612d98565b8461291d878461358f565b612927919061358f565b111561293a5762ffffff19915050610d51565b612944858261358f565b90506122ed8364ffffffffff16828661250e565b6060600061298c61298782612979601887901c6001600160601b0316612dd9565b62ffffff1987169190612702565b612b93565b6040517f4e6f6e2d6d696e696d616c2076617220696e742e20476f74203078000000000060208201526dffffffffffffffffffffffffffff19607083901b16603b82015290925060009150604d016125fa565b600062ffffff198084161415612a485760405162461bcd60e51b815260206004820152602860248201527f54797065644d656d566965772f636f7079546f202d204e756c6c20706f696e7460448201526732b9103232b932b360c11b6064820152608401610468565b612a5183612e3a565b612ab15760405162461bcd60e51b815260206004820152602b60248201527f54797065644d656d566965772f636f7079546f202d20496e76616c696420706f60448201526a34b73a32b9103232b932b360a91b6064820152608401610468565b6000612ac68460181c6001600160601b031690565b6001600160601b031690506000612ae68560781c6001600160601b031690565b6001600160601b031690506000604051905084811115612b065760206060fd5b8285848460045afa506122ed612b1c8760d81c90565b64ffffffffff60601b606091821b168717901b841760181b90565b60608315612b46575081611a79565b825115612b565782518084602001fd5b8160405162461bcd60e51b815260040161046891906134d8565b60008164ffffffffff16612b848460d81c90565b64ffffffffff16149392505050565b600080601f5b600f8160ff161115612bf1576000612bb2826008613658565b60ff1685901c9050612bc381612e78565b61ffff16841793508160ff16601014612bde57601084901b93505b50612bea6001826136c0565b9050612b99565b50600f5b60ff8160ff161015612c395760ff600882021684901c612c1481612e78565b61ffff16831792508160ff16600014612c2f57601083901b92505b5060001901612bf5565b50915091565b600060ff8216612c5157506000611a79565b612c648460181c6001600160601b031690565b6001600160601b0316612c7a60ff84168561358f565b1115612cde57612cc5612c968560781c6001600160601b031690565b6001600160601b0316612cb28660181c6001600160601b031690565b6001600160601b0316858560ff16612ea8565b60405162461bcd60e51b815260040161046891906134d8565b60208260ff161115612d585760405162461bcd60e51b815260206004820152603a60248201527f54797065644d656d566965772f696e646578202d20417474656d70746564207460448201527f6f20696e646578206d6f7265207468616e2033322062797465730000000000006064820152608401610468565b600882026000612d718660781c6001600160601b031690565b6001600160601b031690506000600160ff1b60001984011d91909501511695945050505050565b6000612dad8260181c6001600160601b031690565b612dc08360781c6001600160601b031690565b612dca91906135f7565b6001600160601b031692915050565b600060ff82111561279a5760405162461bcd60e51b815260206004820152602560248201527f53616665436173743a2076616c756520646f65736e27742066697420696e2038604482015264206269747360d81b6064820152608401610468565b6000612e468260d81c90565b64ffffffffff1664ffffffffff1415612e6157506000610a5e565b6000612e6c83612d98565b60405110199392505050565b6000612e8a60048360ff16901c612fd2565b60ff161760081b62ffff0016612e9f82612fd2565b60ff1617919050565b60606000612eb586612b93565b9150506000612ec386612b93565b9150506000612ed186612b93565b9150506000612edf86612b93565b604080517f54797065644d656d566965772f696e646578202d204f76657272616e20746865602082015274040ecd2caee5c40a6d8d2c6ca40d2e640c2e84060f605b1b818301526001600160d01b031960d098891b811660558301526e040eed2e8d040d8cadccee8d04060f608b1b605b830181905297891b8116606a8301527f2e20417474656d7074656420746f20696e646578206174206f666673657420306070830152600f60fb1b609083015295881b861660918201526097810196909652951b90921660a68401525050601760f91b60ac8201528151808203608d01815260ad90910190915295945050505050565b600060f08083179060ff82161415612fee576030915050610a5e565b8060ff1660f11415613004576031915050610a5e565b8060ff1660f2141561301a576032915050610a5e565b8060ff1660f31415613030576033915050610a5e565b8060ff1660f41415613046576034915050610a5e565b8060ff1660f5141561305c576035915050610a5e565b8060ff1660f61415613072576036915050610a5e565b8060ff1660f71415613088576037915050610a5e565b8060ff1660f8141561309e576038915050610a5e565b8060ff1660f914156130b4576039915050610a5e565b8060ff1660fa14156130ca576061915050610a5e565b8060ff1660fb14156130e0576062915050610a5e565b8060ff1660fc14156130f6576063915050610a5e565b8060ff1660fd141561310c576064915050610a5e565b8060ff1660fe1415613122576065915050610a5e565b8060ff1660ff1415613138576066915050610a5e565b50919050565b80356001600160e01b031981168114610a5e57600080fd5b60008083601f840112613167578182fd5b5081356001600160401b0381111561317d578182fd5b60208301915083602082850101111561319557600080fd5b9250929050565b600082601f8301126131ac578081fd5b81356001600160401b03808211156131c6576131c6613744565b604051601f8301601f19908116603f011681019082821181831017156131ee576131ee613744565b81604052838152866020858801011115613206578485fd5b8360208701602083013792830160200193909352509392505050565b600060208284031215613233578081fd5b8135611a798161375a565b60006020828403121561324f578081fd5b8151611a798161375a565b60006020828403121561326b578081fd5b81518015158114611a79578182fd5b60006020828403121561328b578081fd5b5035919050565b6000602082840312156132a3578081fd5b5051919050565b60008060008060008060008060008060006101008c8e0312156132cb578687fd5b6132d48c61313e565b9a506001600160401b038060208e013511156132ee578788fd5b6132fe8e60208f01358f0161319c565b9a508060408e01351115613310578788fd5b6133208e60408f01358f01613156565b909a50985061333160608e0161313e565b975060808d013596508060a08e0135111561334a578586fd5b61335a8e60a08f01358f01613156565b909650945060c08d0135935060e08d0135811015613376578283fd5b506133878d60e08e01358e01613156565b81935080925050509295989b509295989b9093969950565b600081518084526133b78160208601602086016136e3565b601f01601f19169290920160200192915050565b600063ffffffff60e01b808716835285516133ed816004860160208a016136e3565b855190840190613404816004840160208a016136e3565b919094169301600481019390935250506008019392505050565b6000828483379101908152919050565b600082516134408184602087016136e3565b9190910192915050565b6000835161345c8184602088016136e3565b6001600160f81b0319939093169190920190815260010192915050565b600085825284602083015260806040830152613498608083018561339f565b905082606083015295945050505050565b60006020825282602083015282846040840137818301604090810191909152601f909201601f19160101919050565b600060208252611a79602083018461339f565b6000606082526134fe606083018661339f565b6001600160a01b039490941660208301525060400152919050565b60208082526021908201527f43435472616e73666572526f757465723a2061646472657373206973207a65726040820152606f60f81b606082015260800190565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b600082198211156135a2576135a261372e565b500190565b60006001600160401b038083168185168083038211156135c9576135c961372e565b01949350505050565b600060ff821660ff84168060ff038211156135ef576135ef61372e565b019392505050565b60006001600160601b038083168185168083038211156135c9576135c961372e565b60008261363457634e487b7160e01b81526012600452602481fd5b500490565b60008160001904831182151516156136535761365361372e565b500290565b600060ff821660ff84168160ff04811182151516156136795761367961372e565b029392505050565b6000828210156136935761369361372e565b500390565b60006001600160401b03838116908316818110156136b8576136b861372e565b039392505050565b600060ff821660ff8416808210156136da576136da61372e565b90039392505050565b60005b838110156136fe5781810151838201526020016136e6565b8381111561370d576000848401525b50505050565b60006000198214156137275761372761372e565b5060010190565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6001600160a01b03811681146109d757600080fdfe416464726573733a206c6f772d6c6576656c2063616c6c20776974682076616c7565206661696c6564a26469706673582212201f4999e7228b4e35bcf177c77172c30dd5518228e6391f21ea67c5e7d00f2eca64736f6c6343000802003343435472616e73666572526f757465723a2061646472657373206973207a6572000000000000000000000000000000000000000000000000000000000024ea9b000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000890000000000000000000000000000000000000000000000000000000000000001000000000000000000000000c7a7c6ab475138565fe0d2476ee4616862bf2467000000000000000000000000f5d6d369a7f4147f720aeadd4c4f903ae8046166000000000000000000000000515d720b9d219f1931205d5b8d842be1fe2febde0000000000000000000000005364e3557572bd5d5903c0e9c21be359f2eac1da

Deployed Bytecode

0x60806040526004361061012e5760003560e01c806380bc4726116100ab578063b14d35321161006f578063b14d35321461037e578063b59589d11461039e578063c805f68b146103be578063e35a0a88146103de578063f0f44260146103fe578063f2fde38b1461041e57610135565b806380bc4726146102ea578063870ba6cf1461030a5780638d3d78cd1461032a5780638da5cb5b1461034a5780639a8a05921461036857610135565b8063765f0233116100f2578063765f0233146101e857806378943c73146102715780637ed55748146102945780637f0f1817146102b457806380afdea8146102d457610135565b80631812adb61461013a5780631876dec41461016357806361d027b31461019b57806370872aa5146101bb578063715018a6146101d157610135565b3661013557005b600080fd5b34801561014657600080fd5b5061015060055481565b6040519081526020015b60405180910390f35b34801561016f57600080fd5b50600754610183906001600160a01b031681565b6040516001600160a01b03909116815260200161015a565b3480156101a757600080fd5b50600a54610183906001600160a01b031681565b3480156101c757600080fd5b5061015060025481565b3480156101dd57600080fd5b506101e661043e565b005b3480156101f457600080fd5b5061023f61020336600461327a565b600b602052600090815260409020805460018201546002830154600384015460049094015492936001600160a01b039092169290919060ff1685565b604080519586526001600160a01b0390941660208601529284019190915260608301521515608082015260a00161015a565b61028461027f3660046132aa565b610473565b604051901515815260200161015a565b3480156102a057600080fd5b506101e66102af36600461327a565b6109a4565b3480156102c057600080fd5b506101e66102cf366004613222565b6109da565b3480156102e057600080fd5b5061015060045481565b3480156102f657600080fd5b5061028461030536600461327a565b610a38565b34801561031657600080fd5b506101e6610325366004613222565b610a63565b34801561033657600080fd5b50600854610183906001600160a01b031681565b34801561035657600080fd5b506000546001600160a01b0316610183565b34801561037457600080fd5b5061015060035481565b34801561038a57600080fd5b506101e6610399366004613222565b610abd565b3480156103aa57600080fd5b50600654610183906001600160a01b031681565b3480156103ca57600080fd5b506101e66103d9366004613222565b610b17565b3480156103ea57600080fd5b50600954610183906001600160a01b031681565b34801561040a57600080fd5b506101e6610419366004613222565b610b71565b34801561042a57600080fd5b506101e6610439366004613222565b610bcb565b6000546001600160a01b031633146104715760405162461bcd60e51b81526004016104689061355a565b60405180910390fd5b565b6000600260015414156104c85760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610468565b600260018190555487101561052b5760405162461bcd60e51b8152602060048201526024808201527f43435472616e73666572526f757465723a207265717565737420697320746f6f604482015263081bdb1960e21b6064820152608401610468565b60006105708d8d8d8d8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152508f9250610c63915050565b6000818152600b602052604090206004015490915060ff16156105ec5760405162461bcd60e51b815260206004820152602e60248201527f43435472616e73666572526f757465723a20726571756573742068617320626560448201526d656e2075736564206265666f726560901b6064820152608401610468565b6001600160e01b03198916156106555760405162461bcd60e51b815260206004820152602860248201527f43435472616e73666572526f757465723a206c6f636b2074696d65206973206e6044820152676f6e202d7a65726f60c01b6064820152608401610468565b6106db84848080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050508c8c8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250869250610d59915050565b61071e818989898080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152508b9250611182915050565b6107905760405162461bcd60e51b815260206004820152603860248201527f43435472616e73666572526f757465723a207472616e73616374696f6e20686160448201527f73206e6f74206265656e2066696e616c697a65642079657400000000000000006064820152608401610468565b6000818152600b602052604090206003015461094b5760008060006107ec87878080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152508892506112f7915050565b6000878152600b6020526040908190206003810154600190910154915194975092955090935090916001600160a01b039091169061082d908a908a9061341e565b60405190819003812060075463626885b560e11b835290917f4e5aca8a7de151db886a79d287fb0335e05705a91fd8071e29a61f08346197b9916000916001600160a01b039091169063c4d10b6a9061088c908f908f906004016134a9565b60206040518083038186803b1580156108a457600080fd5b505afa1580156108b8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108dc919061323e565b60008a8152600b60205260409020548933604080519586526001600160a01b039485166020870152850192909252606084015216608082015260a08101869052600060c082015260e0810187905261010081018990526101200160405180910390a46001945050505050610991565b60008060006107ec87878080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152508892506113af915050565b600180559b9a5050505050505050505050565b6000546001600160a01b031633146109ce5760405162461bcd60e51b81526004016104689061355a565b6109d78161149a565b50565b806001600160a01b038116610a015760405162461bcd60e51b815260040161046890613519565b6000546001600160a01b03163314610a2b5760405162461bcd60e51b81526004016104689061355a565b610a3482611544565b5050565b6000818152600b602052604081206004015460ff16610a58576000610a5b565b60015b90505b919050565b806001600160a01b038116610a8a5760405162461bcd60e51b815260040161046890613519565b6000546001600160a01b03163314610ab45760405162461bcd60e51b81526004016104689061355a565b610a34826115d5565b806001600160a01b038116610ae45760405162461bcd60e51b815260040161046890613519565b6000546001600160a01b03163314610b0e5760405162461bcd60e51b81526004016104689061355a565b610a3482611666565b806001600160a01b038116610b3e5760405162461bcd60e51b815260040161046890613519565b6000546001600160a01b03163314610b685760405162461bcd60e51b81526004016104689061355a565b610a34826116f7565b806001600160a01b038116610b985760405162461bcd60e51b815260040161046890613519565b6000546001600160a01b03163314610bc25760405162461bcd60e51b81526004016104689061355a565b610a3482611788565b6000546001600160a01b03163314610bf55760405162461bcd60e51b81526004016104689061355a565b6001600160a01b038116610c5a5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401610468565b6109d781611819565b600080600286868686604051602001610c7f94939291906133cb565b60408051601f1981840301815290829052610c999161342e565b602060405180830381855afa158015610cb6573d6000803e3d6000fd5b5050506040513d601f19601f82011682018060405250810190610cd99190613292565b90506000600282604051602001610cf291815260200190565b60408051601f1981840301815290829052610d0c9161342e565b602060405180830381855afa158015610d29573d6000803e3d6000fd5b5050506040513d601f19601f82011682018060405250810190610d4c9190613292565b925050505b949350505050565b60075460405163aff2faa360e01b81526001600160a01b039091169063aff2faa390610d899086906004016134d8565b60206040518083038186803b158015610da157600080fd5b505afa158015610db5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610dd9919061325a565b610e4d576040805162461bcd60e51b81526020600482015260248101919091527f43435472616e73666572526f757465723a206e6f206c6f636b6572207769746860448201527f2074686520676976656e206c6f636b696e6720736372697074206578697374736064820152608401610468565b610e8a6040518060a001604052806000815260200160006001600160a01b0316815260200160008152602001600081526020016000151581525090565b6060610e968486611869565b6001600160401b03909116808452909150610f025760405162461bcd60e51b815260206004820152602660248201527f43435472616e73666572526f757465723a20696e70757420616d6f756e74206960448201526573207a65726f60d01b6064820152608401610468565b600354610f0e826119e1565b60ff1614610f705760405162461bcd60e51b815260206004820152602960248201527f43435472616e73666572526f757465723a20636861696e206964206973206e6f6044820152681d0818dbdc9c9958dd60ba1b6064820152608401610468565b600454610f7c826119fb565b61ffff1614610fdd5760405162461bcd60e51b815260206004820152602760248201527f43435472616e73666572526f757465723a20617070206964206973206e6f742060448201526618dbdc9c9958dd60ca1b6064820152608401610468565b6000610fe882611a16565b61ffff1690506127108111156110595760405162461bcd60e51b815260206004820152603060248201527f43435472616e73666572526f757465723a2070657263656e746167652066656560448201526f206973206f7574206f662072616e676560801b6064820152608401610468565b8251612710906110699083613639565b6110739190613619565b604084015261108182611a26565b6001600160a01b0316602084015261109882611a41565b60ff166060840181905215806110b2575082606001516001145b61110e5760405162461bcd60e51b815260206004820152602760248201527f43435472616e73666572526f757465723a207370656564206973206f7574206f604482015266662072616e676560c81b6064820152608401610468565b50506001608082018181526000938452600b6020908152604094859020845181559084015192810180546001600160a01b0319166001600160a01b0390941693909317909255928201516002820155606090910151600382015590516004909101805460ff19169115159190911790555050565b60065460405163d0a8973960e01b8152600481018590526000602482018190529182916001600160a01b039091169063d0a897399060440160206040518083038186803b1580156111d257600080fd5b505afa1580156111e6573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061120a9190613292565b9050803410156112715760405162461bcd60e51b815260206004820152602c60248201527f43435472616e73666572526f757465723a207061696420666565206973206e6f60448201526b1d081cdd59999a58da595b9d60a21b6064820152608401610468565b6006546040516000916112ce916001600160a01b039091169061129e908a908a908a908a90602401613479565b60408051601f198184030181529190526020810180516001600160e01b0316634354da2560e01b17905284611a50565b90506112e3336112de8434613681565b611a80565b80806020019051810190610d4c919061325a565b60008060006113068585611b9e565b6008546000888152600b60205260409081902060010154905163a9059cbb60e01b81526001600160a01b03918216600482015260248101869052949750929550909350169063a9059cbb906044015b602060405180830381600087803b15801561136f57600080fd5b505af1158015611383573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113a7919061325a565b509250925092565b60008060006113be8585611b9e565b60085460095460405163095ea7b360e01b81526001600160a01b03918216600482015260248101869052949750929550909350169063095ea7b390604401602060405180830381600087803b15801561141657600080fd5b505af115801561142a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061144e919061325a565b506009546000858152600b602052604090819020600101549051631b6396ab60e31b81526001600160a01b0391821660048201526024810186905291169063db1cb55890604401611355565b8061271010156115035760405162461bcd60e51b815260206004820152602e60248201527f43435472616e73666572526f757465723a2070726f746f636f6c20666565206960448201526d73206f7574206f662072616e676560901b6064820152608401610468565b60055460408051918252602082018390527f7d205f60891f60851c61cd244ba387d72b6589e7c0ac41b9d909ab0ce2c2a53b910160405180910390a1600555565b806001600160a01b03811661156b5760405162461bcd60e51b815260040161046890613519565b600854604080516001600160a01b03928316815291841660208301527f36a4c08a38b736dcecb6c328dba61238529620e83ccb23db2cc43cd34ec26096910160405180910390a150600880546001600160a01b0319166001600160a01b0392909216919091179055565b806001600160a01b0381166115fc5760405162461bcd60e51b815260040161046890613519565b600954604080516001600160a01b03928316815291841660208301527f12ad124e13af4c31364ad22aa74320e167b37f005dafb75d71a210c49da3629e910160405180910390a150600980546001600160a01b0319166001600160a01b0392909216919091179055565b806001600160a01b03811661168d5760405162461bcd60e51b815260040161046890613519565b600754604080516001600160a01b03928316815291841660208301527ffa021c2ec0f46429a53e7f88b88065e40001f50c9d38b2e8b713beaf9828d4c3910160405180910390a150600780546001600160a01b0319166001600160a01b0392909216919091179055565b806001600160a01b03811661171e5760405162461bcd60e51b815260040161046890613519565b600654604080516001600160a01b03928316815291841660208301527f4c28a3f61a715259c4dc930c23e7423b8fa52e13232c061a6e488729c66184f4910160405180910390a150600680546001600160a01b0319166001600160a01b0392909216919091179055565b806001600160a01b0381166117af5760405162461bcd60e51b815260040161046890613519565b600a54604080516001600160a01b03928316815291841660208301527f567657fa3f286518b318f4a29870674f433f622fdfc819691acb13105b228225910160405180910390a150600a80546001600160a01b0319166001600160a01b0392909216919091179055565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b600060608161188061187b8683611dc4565b611dea565b905062ffffff1980821614156118d85760405162461bcd60e51b815260206004820152601b60248201527f426974636f696e48656c7065723a20766f7574206973206e756c6c00000000006044820152606401610468565b60008060008060006118eb866000611f14565b6001600160401b0316905060005b818110156119d35761190b878261205d565b955061191686612174565b9450611921866121d1565b935061192c8461221b565b925062ffffff1980841614156119af578960405160200161194d919061342e565b604051602081830303815290604052805190602001206119728662ffffff19166122f7565b604051602001611982919061342e565b6040516020818303038152906040528051906020012014156119aa576119a786612347565b98505b6119c1565b6119be62ffffff1984166122f7565b97505b806119cb81613713565b9150506118f9565b505050505050509250929050565b6000806119f08360008061236c565b600101519392505050565b600080611a0b836001600261236c565b600201519392505050565b600080611a0b836017601861236c565b600080611a36836003601661236c565b601401519392505050565b6000806119f08360198061236c565b6060611a76848484604051806060016040528060298152602001613770602991396123f1565b90505b9392505050565b80471015611ad05760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a20696e73756666696369656e742062616c616e63650000006044820152606401610468565b6000826001600160a01b03168260405160006040518083038185875af1925050503d8060008114611b1d576040519150601f19603f3d011682016040523d82523d6000602084013e611b22565b606091505b5050905080611b995760405162461bcd60e51b815260206004820152603a60248201527f416464726573733a20756e61626c6520746f2073656e642076616c75652c207260448201527f6563697069656e74206d617920686176652072657665727465640000000000006064820152608401610468565b505050565b6007546000828152600b60205260408082205490516307f3f86760e21b815291928392839283926001600160a01b031691631fcfe19c91611be5918a9130916004016134eb565b602060405180830381600087803b158015611bff57600080fd5b505af1158015611c13573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c379190613292565b6005546000878152600b602052604090205491925061271091611c5a9190613639565b611c649190613619565b6000868152600b602052604090206002015490935091508115611d15576008546001600160a01b031663a9059cbb336040516001600160e01b031960e084901b1681526001600160a01b03909116600482015260248101859052604401602060405180830381600087803b158015611cdb57600080fd5b505af1158015611cef573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d13919061325a565b505b8215611da557600854600a5460405163a9059cbb60e01b81526001600160a01b0391821660048201526024810186905291169063a9059cbb90604401602060405180830381600087803b158015611d6b57600080fd5b505af1158015611d7f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611da3919061325a565b505b81611db08483613681565b611dba9190613681565b9350509250925092565b815160009060208401611ddf64ffffffffff8516828461250e565b925050505b92915050565b60008181611e00815b62ffffff19841690612552565b50601884901c6001600160601b0316611e1f5762ffffff199250611f0d565b6000611e2c856000611f14565b9050601885901c6001600160601b03166001600160401b038216611e595762ffffff195b94505050611f0d565b6000611e648361262c565b60ff16905060005b836001600160401b0316811015611edd57828210611e945762ffffff19965050505050611f0d565b6000611eb2611ea38486613681565b62ffffff198b1690600e612685565b9050611ebd816126b9565b611ec7908461358f565b9250508080611ed590613713565b915050611e6c565b50818114611ef45762ffffff199550505050611f0d565b6301000000600160d81b038716600f60d81b1795505050505b5050919050565b600080611f2962ffffff198516846001612702565b905060fc8111611f4457611f3c81612732565b915050611de4565b8060fd1415611faa57611f72611f6d611f5e85600161358f565b62ffffff19871690600261279e565b612732565b9150611f7d8261262c565b60ff16600314611fa557611fa3611f9e62ffffff19861685600360006128e8565b612958565b505b612056565b8060fe1415611fff57611fd3611f6d611fc485600161358f565b62ffffff19871690600461279e565b9150611fde8261262c565b60ff16600514611fa557611fa3611f9e62ffffff19861685600560006128e8565b8060ff141561205657612028611f6d61201985600161358f565b62ffffff19871690600861279e565b91506120338261262c565b60ff1660091461205657612054611f9e62ffffff19861685600960006128e8565b505b5092915050565b600082600f61206b81611df3565b506000612079866000611f14565b6001600160401b03169050601886901c6001600160601b03168186106120d55760405162461bcd60e51b81526020600482015260116024820152702b37baba103932b0b21037bb32b9393ab760791b6044820152606401610468565b60006120e08361262c565b60ff1690506000805b888110156121365761210d6120fe8486613681565b62ffffff198c1690600e612685565b9150612118826126b9565b612122908461358f565b925061212f60018261358f565b90506120e9565b50612144611ea38385613681565b90506000612151826126b9565b905061216662ffffff198b168483600d6128e8565b9a9950505050505050505050565b600081600d61218281611df3565b506000612190856008611f14565b90506121c861219e8261262c565b6121a99060086135d2565b60ff166001600160401b03831660075b62ffffff1989169291906128e8565b95945050505050565b600081600d6121df81611df3565b5060006121ed856008611f14565b90506121c86008826121fe8461262c565b60ff1661220b91906135a7565b6001600160401b031660076121b9565b600081600761222981611df3565b506000612237856000611f14565b90506000612251611f6d62ffffff19881660026001612702565b9050604d826001600160401b0316118061227457506004826001600160401b0316105b80612291575061228c62ffffff198716600180612702565b606a14155b806122c157506122a2600283613698565b6001600160401b03166122be62ffffff19881660026001612702565b14155b156122d05762ffffff19611e50565b6122ed62ffffff19871660036001600160401b038416600c6128e8565b9695505050505050565b606060008061230f8460181c6001600160601b031690565b60405193508392506001600160601b031690506123368461233184602061358f565b6129df565b508181016020016040529052919050565b600081600d61235581611df3565b50610d51611f6d62ffffff1986166000600861279e565b60606000835b61237d84600161358f565b8110156123e8578581815181106123a457634e487b7160e01b600052603260045260246000fd5b602001015160f81c60f81b915082826040516020016123c492919061344a565b604051602081830303815290604052925080806123e090613713565b915050612372565b50509392505050565b6060824710156124525760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b6064820152608401610468565b843b6124a05760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610468565b600080866001600160a01b031685876040516124bc919061342e565b60006040518083038185875af1925050503d80600081146124f9576040519150601f19603f3d011682016040523d82523d6000602084013e6124fe565b606091505b5091509150610d4c828286612b37565b60008061251b838561358f565b905060405181111561252b575060005b8061253d5762ffffff19915050611a79565b5050606092831b9190911790911b1760181b90565b600061255e8383612b70565b61262557600061257d6125718560d81c90565b64ffffffffff16612b93565b91505060006125928464ffffffffff16612b93565b6040517f5479706520617373657274696f6e206661696c65642e20476f7420307800000060208201526001600160b01b031960b086811b8216603d8401526c05c408af0e0cac6e8cac84060f609b1b604784015283901b16605482015290925060009150605e015b60405160208183030381529060405290508060405162461bcd60e51b815260040161046891906134d8565b5090919050565b600060fc826001600160401b03161161264757506001610a5e565b61ffff826001600160401b03161161266157506003610a5e565b63ffffffff826001600160401b03161161267d57506005610a5e565b506009610a5e565b6000611a76848461269f8760181c6001600160601b031690565b6001600160601b03166126b29190613681565b85856128e8565b600081600e6126c781611df3565b5060006126d5856008611f14565b9050806001600160401b03166126ea8261262c565b60ff166126f7919061358f565b6121c890600861358f565b600061270f8260206136c0565b61271a906008613658565b60ff16612728858585612c3f565b901c949350505050565b60006001600160401b0382111561279a5760405162461bcd60e51b815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203660448201526534206269747360d01b6064820152608401610468565b5090565b6000611a766127ae858585612c3f565b600881811c7eff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff167fff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff009290911b9190911617601081811c7dffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff167fffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff00009290911b9190911617602081811c7bffffffff00000000ffffffff00000000ffffffff00000000ffffffff167fffffffff00000000ffffffff00000000ffffffff00000000ffffffff000000009290911b9190911617604081811c77ffffffffffffffff0000000000000000ffffffffffffffff1677ffffffffffffffff0000000000000000ffffffffffffffff199290911b9190911617608081811c91901b1790565b6000806128fe8660781c6001600160601b031690565b6001600160601b0316905061291286612d98565b8461291d878461358f565b612927919061358f565b111561293a5762ffffff19915050610d51565b612944858261358f565b90506122ed8364ffffffffff16828661250e565b6060600061298c61298782612979601887901c6001600160601b0316612dd9565b62ffffff1987169190612702565b612b93565b6040517f4e6f6e2d6d696e696d616c2076617220696e742e20476f74203078000000000060208201526dffffffffffffffffffffffffffff19607083901b16603b82015290925060009150604d016125fa565b600062ffffff198084161415612a485760405162461bcd60e51b815260206004820152602860248201527f54797065644d656d566965772f636f7079546f202d204e756c6c20706f696e7460448201526732b9103232b932b360c11b6064820152608401610468565b612a5183612e3a565b612ab15760405162461bcd60e51b815260206004820152602b60248201527f54797065644d656d566965772f636f7079546f202d20496e76616c696420706f60448201526a34b73a32b9103232b932b360a91b6064820152608401610468565b6000612ac68460181c6001600160601b031690565b6001600160601b031690506000612ae68560781c6001600160601b031690565b6001600160601b031690506000604051905084811115612b065760206060fd5b8285848460045afa506122ed612b1c8760d81c90565b64ffffffffff60601b606091821b168717901b841760181b90565b60608315612b46575081611a79565b825115612b565782518084602001fd5b8160405162461bcd60e51b815260040161046891906134d8565b60008164ffffffffff16612b848460d81c90565b64ffffffffff16149392505050565b600080601f5b600f8160ff161115612bf1576000612bb2826008613658565b60ff1685901c9050612bc381612e78565b61ffff16841793508160ff16601014612bde57601084901b93505b50612bea6001826136c0565b9050612b99565b50600f5b60ff8160ff161015612c395760ff600882021684901c612c1481612e78565b61ffff16831792508160ff16600014612c2f57601083901b92505b5060001901612bf5565b50915091565b600060ff8216612c5157506000611a79565b612c648460181c6001600160601b031690565b6001600160601b0316612c7a60ff84168561358f565b1115612cde57612cc5612c968560781c6001600160601b031690565b6001600160601b0316612cb28660181c6001600160601b031690565b6001600160601b0316858560ff16612ea8565b60405162461bcd60e51b815260040161046891906134d8565b60208260ff161115612d585760405162461bcd60e51b815260206004820152603a60248201527f54797065644d656d566965772f696e646578202d20417474656d70746564207460448201527f6f20696e646578206d6f7265207468616e2033322062797465730000000000006064820152608401610468565b600882026000612d718660781c6001600160601b031690565b6001600160601b031690506000600160ff1b60001984011d91909501511695945050505050565b6000612dad8260181c6001600160601b031690565b612dc08360781c6001600160601b031690565b612dca91906135f7565b6001600160601b031692915050565b600060ff82111561279a5760405162461bcd60e51b815260206004820152602560248201527f53616665436173743a2076616c756520646f65736e27742066697420696e2038604482015264206269747360d81b6064820152608401610468565b6000612e468260d81c90565b64ffffffffff1664ffffffffff1415612e6157506000610a5e565b6000612e6c83612d98565b60405110199392505050565b6000612e8a60048360ff16901c612fd2565b60ff161760081b62ffff0016612e9f82612fd2565b60ff1617919050565b60606000612eb586612b93565b9150506000612ec386612b93565b9150506000612ed186612b93565b9150506000612edf86612b93565b604080517f54797065644d656d566965772f696e646578202d204f76657272616e20746865602082015274040ecd2caee5c40a6d8d2c6ca40d2e640c2e84060f605b1b818301526001600160d01b031960d098891b811660558301526e040eed2e8d040d8cadccee8d04060f608b1b605b830181905297891b8116606a8301527f2e20417474656d7074656420746f20696e646578206174206f666673657420306070830152600f60fb1b609083015295881b861660918201526097810196909652951b90921660a68401525050601760f91b60ac8201528151808203608d01815260ad90910190915295945050505050565b600060f08083179060ff82161415612fee576030915050610a5e565b8060ff1660f11415613004576031915050610a5e565b8060ff1660f2141561301a576032915050610a5e565b8060ff1660f31415613030576033915050610a5e565b8060ff1660f41415613046576034915050610a5e565b8060ff1660f5141561305c576035915050610a5e565b8060ff1660f61415613072576036915050610a5e565b8060ff1660f71415613088576037915050610a5e565b8060ff1660f8141561309e576038915050610a5e565b8060ff1660f914156130b4576039915050610a5e565b8060ff1660fa14156130ca576061915050610a5e565b8060ff1660fb14156130e0576062915050610a5e565b8060ff1660fc14156130f6576063915050610a5e565b8060ff1660fd141561310c576064915050610a5e565b8060ff1660fe1415613122576065915050610a5e565b8060ff1660ff1415613138576066915050610a5e565b50919050565b80356001600160e01b031981168114610a5e57600080fd5b60008083601f840112613167578182fd5b5081356001600160401b0381111561317d578182fd5b60208301915083602082850101111561319557600080fd5b9250929050565b600082601f8301126131ac578081fd5b81356001600160401b03808211156131c6576131c6613744565b604051601f8301601f19908116603f011681019082821181831017156131ee576131ee613744565b81604052838152866020858801011115613206578485fd5b8360208701602083013792830160200193909352509392505050565b600060208284031215613233578081fd5b8135611a798161375a565b60006020828403121561324f578081fd5b8151611a798161375a565b60006020828403121561326b578081fd5b81518015158114611a79578182fd5b60006020828403121561328b578081fd5b5035919050565b6000602082840312156132a3578081fd5b5051919050565b60008060008060008060008060008060006101008c8e0312156132cb578687fd5b6132d48c61313e565b9a506001600160401b038060208e013511156132ee578788fd5b6132fe8e60208f01358f0161319c565b9a508060408e01351115613310578788fd5b6133208e60408f01358f01613156565b909a50985061333160608e0161313e565b975060808d013596508060a08e0135111561334a578586fd5b61335a8e60a08f01358f01613156565b909650945060c08d0135935060e08d0135811015613376578283fd5b506133878d60e08e01358e01613156565b81935080925050509295989b509295989b9093969950565b600081518084526133b78160208601602086016136e3565b601f01601f19169290920160200192915050565b600063ffffffff60e01b808716835285516133ed816004860160208a016136e3565b855190840190613404816004840160208a016136e3565b919094169301600481019390935250506008019392505050565b6000828483379101908152919050565b600082516134408184602087016136e3565b9190910192915050565b6000835161345c8184602088016136e3565b6001600160f81b0319939093169190920190815260010192915050565b600085825284602083015260806040830152613498608083018561339f565b905082606083015295945050505050565b60006020825282602083015282846040840137818301604090810191909152601f909201601f19160101919050565b600060208252611a79602083018461339f565b6000606082526134fe606083018661339f565b6001600160a01b039490941660208301525060400152919050565b60208082526021908201527f43435472616e73666572526f757465723a2061646472657373206973207a65726040820152606f60f81b606082015260800190565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b600082198211156135a2576135a261372e565b500190565b60006001600160401b038083168185168083038211156135c9576135c961372e565b01949350505050565b600060ff821660ff84168060ff038211156135ef576135ef61372e565b019392505050565b60006001600160601b038083168185168083038211156135c9576135c961372e565b60008261363457634e487b7160e01b81526012600452602481fd5b500490565b60008160001904831182151516156136535761365361372e565b500290565b600060ff821660ff84168160ff04811182151516156136795761367961372e565b029392505050565b6000828210156136935761369361372e565b500390565b60006001600160401b03838116908316818110156136b8576136b861372e565b039392505050565b600060ff821660ff8416808210156136da576136da61372e565b90039392505050565b60005b838110156136fe5781810151838201526020016136e6565b8381111561370d576000848401525b50505050565b60006000198214156137275761372761372e565b5060010190565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6001600160a01b03811681146109d757600080fdfe416464726573733a206c6f772d6c6576656c2063616c6c20776974682076616c7565206661696c6564a26469706673582212201f4999e7228b4e35bcf177c77172c30dd5518228e6391f21ea67c5e7d00f2eca64736f6c63430008020033

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

000000000000000000000000000000000000000000000000000000000024ea9b000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000890000000000000000000000000000000000000000000000000000000000000001000000000000000000000000c7a7c6ab475138565fe0d2476ee4616862bf2467000000000000000000000000f5d6d369a7f4147f720aeadd4c4f903ae8046166000000000000000000000000515d720b9d219f1931205d5b8d842be1fe2febde0000000000000000000000005364e3557572bd5d5903c0e9c21be359f2eac1da

-----Decoded View---------------
Arg [0] : _startingBlockNumber (uint256): 2419355
Arg [1] : _protocolPercentageFee (uint256): 5
Arg [2] : _chainId (uint256): 137
Arg [3] : _appId (uint256): 1
Arg [4] : _relay (address): 0xc7a7C6AB475138565FE0D2476EE4616862Bf2467
Arg [5] : _lockers (address): 0xf5D6D369A7F4147F720AEAdd4C4f903aE8046166
Arg [6] : _teleBTC (address): 0x515D720B9D219f1931205D5B8D842bE1Fe2FeBDE
Arg [7] : _treasury (address): 0x5364E3557572bd5D5903C0e9C21BE359F2Eac1dA

-----Encoded View---------------
8 Constructor Arguments found :
Arg [0] : 000000000000000000000000000000000000000000000000000000000024ea9b
Arg [1] : 0000000000000000000000000000000000000000000000000000000000000005
Arg [2] : 0000000000000000000000000000000000000000000000000000000000000089
Arg [3] : 0000000000000000000000000000000000000000000000000000000000000001
Arg [4] : 000000000000000000000000c7a7c6ab475138565fe0d2476ee4616862bf2467
Arg [5] : 000000000000000000000000f5d6d369a7f4147f720aeadd4c4f903ae8046166
Arg [6] : 000000000000000000000000515d720b9d219f1931205d5b8d842be1fe2febde
Arg [7] : 0000000000000000000000005364e3557572bd5d5903c0e9c21be359f2eac1da


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

Txn Hash Block Value Eth2 PubKey Valid
View All Deposits
[ Download: CSV Export  ]
[ Download: CSV Export  ]

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