Contract Address Details

0x4C46b3Ef0Fada5e33A82c7Cc117C73840e72170f

Contract Name
CallistoBridge
Creator
0xb7971f–a51f91 at 0x043ec3–c0d216
Balance
0 CLO ( )
Tokens
Fetching tokens...
Transactions
16 Transactions
Transfers
0 Transfers
Gas Used
877,402
Last Balance Update
14463586
Contract name:
CallistoBridge




Optimization enabled
true
Compiler version
v0.8.7+commit.e28d00a7




Optimization runs
200
EVM Version
default




Verified at
2022-06-09T14:44:31.249408Z

Contract source code

// SPDX-License-Identifier: No License (None)
pragma solidity ^0.8.0;

interface IERC223TokenCloned {
    // initialize cloned token just for ERC223TokenCloned
    function initialize(address newOwner, string calldata name, string calldata symbol, uint8 decimals) external;
    function mint(address user, uint256 amount) external;
    function burnFrom(address account, uint256 amount) external returns(bool);
    function burn(uint256 amount) external returns(bool);
    function balanceOf(address account) external view returns (uint256);
    function allowance(address _owner, address spender) external view returns (uint256);
    function approve(address spender, uint256 amount) external returns (bool);
}

interface IContractCaller {
    function callContract(address user, address token, uint256 value, address toContract, bytes memory data) external payable;
}

/**
 * @dev https://eips.ethereum.org/EIPS/eip-1167[EIP 1167] is a standard for
 * deploying minimal proxy contracts, also known as "clones".
 *
 * > To simply and cheaply clone contract functionality in an immutable way, this standard specifies
 * > a minimal bytecode implementation that delegates all calls to a known, fixed address.
 *
 * The library includes functions to deploy a proxy using either `create` (traditional deployment) or `create2`
 * (salted deterministic deployment). It also includes functions to predict the addresses of clones deployed using the
 * deterministic method.
 *
 * _Available since v3.4._
 */
library Clones {
    /**
     * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
     *
     * This function uses the create opcode, which should never revert.
     */
    function clone(address implementation) internal returns (address instance) {
        assembly {
            let ptr := mload(0x40)
            mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
            mstore(add(ptr, 0x14), shl(0x60, implementation))
            mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)
            instance := create(0, ptr, 0x37)
        }
        require(instance != address(0), "ERC1167: create failed");
    }

    /**
     * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
     *
     * This function uses the create2 opcode and a `salt` to deterministically deploy
     * the clone. Using the same `implementation` and `salt` multiple time will revert, since
     * the clones cannot be deployed twice at the same address.
     */
    function cloneDeterministic(address implementation, bytes32 salt) internal returns (address instance) {
        assembly {
            let ptr := mload(0x40)
            mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
            mstore(add(ptr, 0x14), shl(0x60, implementation))
            mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)
            instance := create2(0, ptr, 0x37, salt)
        }
        require(instance != address(0), "ERC1167: create2 failed");
    }

    /**
     * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
     */
    function predictDeterministicAddress(
        address implementation,
        bytes32 salt,
        address deployer
    ) internal pure returns (address predicted) {
        assembly {
            let ptr := mload(0x40)
            mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
            mstore(add(ptr, 0x14), shl(0x60, implementation))
            mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf3ff00000000000000000000000000000000)
            mstore(add(ptr, 0x38), shl(0x60, deployer))
            mstore(add(ptr, 0x4c), salt)
            mstore(add(ptr, 0x6c), keccak256(ptr, 0x37))
            predicted := keccak256(add(ptr, 0x37), 0x55)
        }
    }

    /**
     * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
     */
    function predictDeterministicAddress(address implementation, bytes32 salt)
        internal
        view
        returns (address predicted)
    {
        return predictDeterministicAddress(implementation, salt, address(this));
    }
}

// helper methods for interacting with ERC20 tokens and sending ETH that do not consistently return true/false
library TransferHelper {
    function safeApprove(address token, address to, uint value) internal {
        // bytes4(keccak256(bytes('approve(address,uint256)')));
        (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x095ea7b3, to, value));
        require(success && (data.length == 0 || abi.decode(data, (bool))), 'TransferHelper: APPROVE_FAILED');
    }

    function safeTransfer(address token, address to, uint value) internal {
        // bytes4(keccak256(bytes('transfer(address,uint256)')));
        (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0xa9059cbb, to, value));
        require(success && (data.length == 0 || abi.decode(data, (bool))), 'TransferHelper: TRANSFER_FAILED');
    }

    function safeTransferFrom(address token, address from, address to, uint value) internal {
        // bytes4(keccak256(bytes('transferFrom(address,address,uint256)')));
        (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x23b872dd, from, to, value));
        require(success && (data.length == 0 || abi.decode(data, (bool))), 'TransferHelper: TRANSFER_FROM_FAILED');
    }

    function safeTransferETH(address to, uint value) internal {
        (bool success,) = to.call{value:value}(new bytes(0));
        require(success, 'TransferHelper: ETH_TRANSFER_FAILED');
    }
}

/**
 * @dev Library for managing
 * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
 * types.
 *
 * Sets have the following properties:
 *
 * - Elements are added, removed, and checked for existence in constant time
 * (O(1)).
 * - Elements are enumerated in O(n). No guarantees are made on the ordering.
 *
 * ```
 * contract Example {
 *     // Add the library methods
 *     using EnumerableSet for EnumerableSet.AddressSet;
 *
 *     // Declare a set state variable
 *     EnumerableSet.AddressSet private mySet;
 * }
 * ```
 *
 * As of v3.0.0, only sets of type `address` (`AddressSet`) and `uint256`
 * (`UintSet`) are supported.
 */
library EnumerableSet {

    struct AddressSet {
        // Storage of set values
        address[] _values;

        // Position of the value in the `values` array, plus 1 because index 0
        // means a value is not in the set.
        mapping (address => uint256) _indexes;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(AddressSet storage set, address value) internal returns (bool) {
        if (!contains(set, value)) {
            set._values.push(value);
            // The value is stored at length-1, but we add 1 to all indexes
            // and use 0 as a sentinel value
            set._indexes[value] = set._values.length;
            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(AddressSet storage set, address value) internal returns (bool) {
        // We read and store the value's index to prevent multiple reads from the same storage slot
        uint256 valueIndex = set._indexes[value];

        if (valueIndex != 0) { // Equivalent to contains(set, value)
            // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
            // the array, and then remove the last element (sometimes called as 'swap and pop').
            // This modifies the order of the array, as noted in {at}.

            uint256 toDeleteIndex = valueIndex - 1;
            uint256 lastIndex = set._values.length - 1;

            // When the value to delete is the last one, the swap operation is unnecessary. However, since this occurs
            // so rarely, we still do the swap anyway to avoid the gas cost of adding an 'if' statement.

            address lastvalue = set._values[lastIndex];

            // Move the last value to the index where the value to delete is
            set._values[toDeleteIndex] = lastvalue;
            // Update the index for the moved value
            set._indexes[lastvalue] = toDeleteIndex + 1; // All indexes are 1-based

            // Delete the slot where the moved value was stored
            set._values.pop();

            // Delete the index for the deleted slot
            delete set._indexes[value];

            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(AddressSet storage set, address value) internal view returns (bool) {
        return set._indexes[value] != 0;
    }

    /**
     * @dev Returns 1-based index of value in the set. O(1).
     */
    function indexOf(AddressSet storage set, address value) internal view returns (uint256) {
        return set._indexes[value];
    }


    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function length(AddressSet storage set) internal view returns (uint256) {
        return set._values.length;
    }

   /**
    * @dev Returns the value stored at position `index` in the set. O(1).
    *
    * Note that there are no guarantees on the ordering of values inside the
    * array, and it may change when more values are added or removed.
    *
    * Requirements:
    *
    * - `index` must be strictly less than {length}.
    */
    function at(AddressSet storage set, uint256 index) internal view returns (address) {
        require(set._values.length > index, "EnumerableSet: index out of bounds");
        return set._values[index];
    }
}

abstract contract Ownable {
    address internal _owner;

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

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    /* will use initialize instead
    constructor () {
        _owner = msg.sender;
        emit OwnershipTransferred(address(0), msg.sender);
    }
    */
    /**
     * @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() == msg.sender, "Ownable: caller is not the owner");
        _;
    }

}

contract CallistoBridge is Ownable {
    using TransferHelper for address;
    using EnumerableSet for EnumerableSet.AddressSet;
    EnumerableSet.AddressSet authorities; // authority has to sign claim transaction (message)

    address constant MAX_NATIVE_COINS = address(31); // addresses from address(1) to MAX_NATIVE_COINS are considered as native coins 
                                            // CLO = address(1)
    struct Token {
        address token;  // foreign token address
        bool isWrapped; // is native token wrapped of foreign
    }

    struct Upgrade {
        address newContract;
        uint64  validFrom;
    }

    uint256 public threshold;   // minimum number of signatures required to approve swap
    address public tokenImplementation;    // implementation of wrapped token
    address public feeTo; // send fee to this address
    bool public frozen; // if frozen - swap will not work
    uint256 public wrapNonce;   // the last nonce used to create wrapped token address begin with 0xCC.... 
    mapping(uint256 => mapping(bytes32 => bool)) public isTxProcessed;    // chainID => txID => isProcessed
    mapping(uint256 => mapping(address => Token)) public tokenPair;       // chainID => native token address => Token struct
    mapping(uint256 => mapping(address => address)) public tokenForeign;  // chainID => foreign token address => native token
    mapping(address => uint256) public tokenDeposits;  // amount of tokens were deposited by users
    mapping(address => bool) public isFreezer;  // addresses that have right to freeze contract 
    uint256 public setupMode;   // time when setup mode will start, 0 if disable
    Upgrade public upgradeData;
    address public founders;
    address public requiredAuthority;   // authority address that MUST sign swap transaction
    mapping(address => address) public migration;   // migration oldToken => newToken
    bool public migrationSetupForbidden;    // forbid adding migration tokens
    address public contractCaller; // intermediate contract that calls third-party contract functions (toContract)
    uint256 public functionMapping;    // bitmap of locked functions (one bit per function)

    event SetAuthority(address authority, bool isEnable);
    event SetFeeTo(address previousFeeTo, address newFeeTo);
    event SetThreshold(uint256 threshold);
    event SetContractCaller(address newContractCaller);
    event Deposit(address indexed token, address indexed sender, uint256 value, uint256 toChainId, address toToken);
    event Claim(address indexed token, address indexed to, uint256 value, bytes32 txId, uint256 fromChainId, address fromToken);
    event Fee(address indexed sender, uint256 fee);
    event CreatePair(address toToken, bool isWrapped, address fromToken, uint256 fromChainId);
    event Frozen(bool status);
    event RescuedERC20(address token, address to, uint256 value);
    event SetFreezer(address freezer, bool isActive);
    event SetupMode(uint time);
    event UpgradeRequest(address newContract, uint256 validFrom);
    event AddTokenMigration(address tokenFrom, address tokenTo);
    event BridgeToContract(address indexed token, address indexed sender, uint256 value, uint256 toChainId, address toToken, address toContract, bytes data);
    event ClaimToContract(address indexed token, address indexed to, uint256 value, bytes32 txId, uint256 fromChainId, address fromToken, address toContract);

    // run only once from proxy
    function initialize(address newOwner, address newFounders, address _tokenImplementation) external {
        require(newOwner != address(0) && newFounders != address(0) && founders == address(0)); // run only once
        _owner = newOwner;
        founders = newFounders;
        emit OwnershipTransferred(address(0), msg.sender);
        require(_tokenImplementation != address(0), "Wrong tokenImplementation");
        tokenImplementation = _tokenImplementation;
        feeTo = msg.sender;
        threshold = 1;
        setupMode = 1; // allow setup after deployment
    }
    /*
    constructor (address _tokenImplementation) {
        require(_tokenImplementation != address(0), "Wrong tokenImplementation");
        tokenImplementation = _tokenImplementation;
        feeTo = msg.sender;
        threshold = 1;
    }
    */
    modifier notFrozen() {
        require(!frozen, "Bridge is frozen");
        _;
    }

    // allowed only in setup mode
    modifier onlySetup() {
        uint256 mode = setupMode; //use local variable to save gas
        require(mode != 0 && mode < block.timestamp, "Not in setup mode");
        _;
    }

    function upgradeTo() external view returns(address newContract) {
        Upgrade memory upg = upgradeData;
        require(upg.validFrom < block.timestamp && upg.newContract != address(0), "Upgrade not allowed");
        newContract = upg.newContract;
    }

    function requestUpgrade(address newContract) external onlyOwner {
        require(newContract != address(0), "Zero address");
        uint256 validFrom = block.timestamp + 3 days;
        upgradeData = Upgrade(newContract, uint64(validFrom));
        emit UpgradeRequest(newContract, validFrom);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */

    function transferOwnership(address newOwner) public {
        require(founders == msg.sender, "Ownable: caller is not the founders");
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        emit OwnershipTransferred(_owner, newOwner);
        _owner = newOwner;
    }

    function ChangeFounder(address newFounders) public {
        require(founders == msg.sender, "caller is not the founders");
        require(newFounders != address(0), "new owner is the zero address");
        emit OwnershipTransferred(founders, newFounders);
        founders = newFounders;
    }

    // get number of authorities
    function getAuthoritiesNumber() external view returns(uint256) {
        return authorities.length();
    }

    // returns list of authorities addresses
    function getAuthorities() external view returns(address[] memory) {
        return authorities._values;
    }

    // Owner or Authority may freeze bridge in case of anomaly detection
    function freeze() external {
        require(msg.sender == owner() || authorities.contains(msg.sender) || isFreezer[msg.sender]);
        frozen = true;
        emit Frozen(true);
    }

    // Only owner can manually unfreeze contract
    function unfreeze() external onlyOwner onlySetup {
        frozen = false;
        emit Frozen(false);
    }

    function lockFunctions(uint256 _functionMapping) external onlyOwner {
        functionMapping = _functionMapping;
    }

    // add authority
    function setFreezer(address freezer, bool isActive) external onlyOwner {
        require(freezer != address(0), "Zero address");
        isFreezer[freezer] = isActive;
        emit SetFreezer(freezer, isActive);
    }

    // add authority
    function addAuthority(address authority) external onlyOwner onlySetup {
        require(authority != address(0), "Zero address");
        require(authorities.length() < 255, "Too many authorities");
        require(authorities.add(authority), "Authority already added");
        emit SetAuthority(authority, true);
    }

    // remove authority
    function removeAuthority(address authority) external onlyOwner {
        require(authorities.remove(authority), "Authority does not exist");
        emit SetAuthority(authority, false);
    }

    // set authority address that MUST sign claim request
    function setRequiredAuthority(address authority) external onlyOwner onlySetup {
        requiredAuthority = authority;
    }

    // set fee receiver address
    function setFeeTo(address newFeeTo) external onlyOwner onlySetup {
        require(newFeeTo != address(0), "Zero address");
        address previousFeeTo = feeTo;
        feeTo = newFeeTo;
        emit SetFeeTo(previousFeeTo, newFeeTo);
    }

    // set threshold - minimum number of signatures required to approve swap
    function setThreshold(uint256 _threshold) external onlyOwner onlySetup {
        require(threshold != 0 && threshold <= authorities.length(), "Wrong threshold");
        threshold = _threshold;
        emit SetThreshold(threshold);
    }

    // set contractCaller address
    function setContractCaller(address newContractCaller) external onlyOwner onlySetup {
        contractCaller = newContractCaller;
        emit SetContractCaller(newContractCaller);
    }

    function disableSetupMode() external onlyOwner {
        setupMode = 0;
        emit SetupMode(0);
    }

    function enableSetupMode() external onlyOwner {
        setupMode = block.timestamp + 1 days;
        emit SetupMode(setupMode);
    }

    // returns `nonce` to use in `createWrappedToken()` to create address starting with 0xCC.....
    function calculateNonce() external view returns(uint256 nonce, address addr) {
        nonce = wrapNonce;
        address implementation = tokenImplementation;
        while (true) {
            nonce++;
            addr = Clones.predictDeterministicAddress(implementation, bytes32(nonce));
            if (uint160(addr) & uint160(0xfF00000000000000000000000000000000000000) == uint160(0xCc00000000000000000000000000000000000000))
                break;
        }
    }

    function rescueERC20(address token, address to) external onlyOwner {
        uint256 value = IERC223TokenCloned(token).balanceOf(address(this)) - tokenDeposits[token];
        token.safeTransfer(to, value);
        emit RescuedERC20(token, to, value);
    }

    // Create wrapped token for foreign token
    function createWrappedToken(
        address fromToken,      // foreign token address
        uint256 fromChainId,    // foreign chain ID where token deployed
        string memory name,     // wrapped token name
        string memory symbol,   // wrapped token symbol
        uint8 decimals,         // wrapped token decimals (should be the same as in original token)
        uint256 nonce           // nonce to create wrapped token address begin with 0xCC.... 
    )
        external
        onlyOwner
        onlySetup
    {
        require(fromToken != address(0), "Wrong token address");
        require(tokenForeign[fromChainId][fromToken] == address(0), "This token already wrapped");
        require(nonce > wrapNonce, "Nonce must be higher then wrapNonce");
        wrapNonce = nonce;
        address wrappedToken = Clones.cloneDeterministic(tokenImplementation, bytes32(nonce));
        IERC223TokenCloned(wrappedToken).initialize(owner(), name, symbol, decimals);
        tokenPair[fromChainId][wrappedToken] = Token(fromToken, true);
        tokenForeign[fromChainId][fromToken] = wrappedToken;
        emit CreatePair(wrappedToken, true, fromToken, fromChainId); //wrappedToken - wrapped token contract address
    }

    /**
     * @dev Create pair between existing tokens on native and foreign chains
     * @param toToken token address on native chain
     * @param fromToken token address on foreign chain
     * @param fromChainId foreign chain ID
     * @param isWrapped `true` if `toToken` is our wrapped token otherwise `false`
     */
    function createPair(address toToken, address fromToken, uint256 fromChainId, bool isWrapped) external onlyOwner onlySetup {
        require(tokenPair[fromChainId][toToken].token == address(0), "Pair already exist");
        tokenPair[fromChainId][toToken] = Token(fromToken, isWrapped);
        tokenForeign[fromChainId][fromToken] = toToken;
        emit CreatePair(toToken, isWrapped, fromToken, fromChainId);
    }

    /**
     * @dev Delete unused pair
     * @param toToken token address on native chain
     * @param fromChainId foreign chain ID
     */
    function deletePair(address toToken, uint256 fromChainId) external onlyOwner onlySetup {
        delete tokenPair[fromChainId][toToken];
    }

    // Move tokens through the bridge and call the contract with 'data' parameters on the destination chain
    function bridgeToContract(
        address receiver,   // address of token receiver on destination chain
        address token,      // token that user send (if token address < 32, then send native coin)
        uint256 value,      // tokens value
        uint256 toChainId,  // destination chain Id where will be claimed tokens
        address toContract, // this contract will be called on destination chain
        bytes memory data   // this data will be passed to contract call (ABI encoded parameters)
    )
        external
        payable
        notFrozen
    {
        require(functionMapping & 2 == 0, "locked");    // check bit 1
        require(receiver != address(0), "Incorrect receiver address");
        address pair_token = _deposit(token, value, toChainId);
        if (token == address(0xbf6c50889d3a620eb42C0F188b65aDe90De958c4) && // BUSDT token on the Callisto chain
            pair_token == address(0xdAC17F958D2ee523a2206206994597C13D831ec7) && // USDT token on the ETH chain
            toChainId == 1 ) // destination is ETH chain
        {
            // Since USDT token on ETH chain has 6 decimals we have to convert 18 decimals of BUSDT to 6 decimals
            value = value / 10**12;
        }
        emit BridgeToContract(token, receiver, value, toChainId, pair_token, toContract, data);
    }

    // Claim tokens from the bridge and call the contract with 'data' parameters
    function claimToContract(
        address token,          // token to receive
        bytes32 txId,           // deposit transaction hash on fromChain 
        address to,             // user address
        uint256 value,          // value of tokens
        uint256 fromChainId,    // chain ID where user deposited
        address toContract,     // this contract will be called on destination chain
        bytes memory data,      // this data will be passed to contract call (ABI encoded parameters)
        bytes[] memory sig      // authority signatures
    ) 
        external
        payable
        notFrozen
    {
        require(functionMapping & 4 == 0, "locked");    // check bit 2
        require(!isTxProcessed[fromChainId][txId], "Transaction already processed");
        Token memory pair = tokenPair[fromChainId][token];
        require(pair.token != address(0), "There is no pair");
        {
        isTxProcessed[fromChainId][txId] = true;

        // Check signature
        address must = requiredAuthority;
        bytes32 messageHash = keccak256(abi.encodePacked(token, to, value, txId, fromChainId, block.chainid, toContract, data));
        messageHash = prefixed(messageHash);
        uint256 uniqSig;
        uint256 set;    // maximum number of authorities is 255
        for (uint i = 0; i < sig.length; i++) {
            address authority = recoverSigner(messageHash, sig[i]);
            if (authority == must) must = address(0);
            uint256 index = authorities.indexOf(authority);
            uint256 mask = 1 << index;
            if (index != 0 && (set & mask) == 0 ) {
                set |= mask;
                uniqSig++;
            }
        }
        require(threshold <= uniqSig, "Require more signatures");
        require(must == address(0), "The required authority does not sign");
        }
        // fix decimals for USDT on ETH
        if (token == address(0xbf6c50889d3a620eb42C0F188b65aDe90De958c4) && // BUSDT token on the Callisto chain
            pair.token == address(0xdAC17F958D2ee523a2206206994597C13D831ec7) && // USDT token on the ETH chain
            fromChainId == 1 ) // from ETH chain
        {
            // Since USDT token on ETH chain has 6 decimals we have to convert 6 decimals to 18 decimals of BUSDT 
            value = value * 10**12;
        }

        if (msg.value != 0) to.safeTransferETH(msg.value);  // send CLO to user as bonus

        // Call toContract
        if(isContract(toContract) && toContract != address(this)) {
            if (token <= MAX_NATIVE_COINS) {
                IContractCaller(contractCaller).callContract{value: value}(to, token, value, toContract, data);
            } else {
                if(pair.isWrapped) {
                    IERC223TokenCloned(token).mint(contractCaller, value);
                } else {
                    tokenDeposits[token] -= value;
                    token.safeTransfer(contractCaller, value);
                }
                IContractCaller(contractCaller).callContract(to, token, value, toContract, data);               
            }
        } else {    // if not contract
            if (token <= MAX_NATIVE_COINS) {
                to.safeTransferETH(value);
            } else {
                if(pair.isWrapped) {
                    IERC223TokenCloned(token).mint(to, value);
                } else {
                    tokenDeposits[token] -= value;
                    token.safeTransfer(to, value);
                }
            }
        }
        emit ClaimToContract(token, to, value, txId, fromChainId, pair.token, toContract);
    }

    // Due to issue in Callisto Explorer when createPair with isWrapped = 0 it sends transaction with isWrapped = 1
    // It makes this pair unusable without chance to fix it.
    // function reversIsWrapped allow to reverse isWrapped value
    function reversIsWrapped(address toToken, uint256 fromChainId) external onlyOwner onlySetup {
        bool isWrapped = tokenPair[fromChainId][toToken].isWrapped;
        tokenPair[fromChainId][toToken].isWrapped = !isWrapped;
    }

    function depositTokens(
        address receiver,   // address of token receiver on destination chain
        address token,      // token that user send (if token address < 32, then send native coin)
        uint256 value,      // tokens value
        uint256 toChainId   // destination chain Id where will be claimed tokens
    ) 
        external
        payable
        notFrozen
    {
        require(functionMapping & 1 == 0, "locked");    // check bit 0
        require(receiver != address(0), "Incorrect receiver address");
        address pair_token = _deposit(token, value, toChainId);
        if (token == address(0xbf6c50889d3a620eb42C0F188b65aDe90De958c4) && // BUSDT token on the Callisto chain
            pair_token == address(0xdAC17F958D2ee523a2206206994597C13D831ec7) && // USDT token on the ETH chain
            toChainId == 1 ) // destination is ETH chain
        {
            // Since USDT token on ETH chain has 6 decimals we have to convert 18 decimals of BUSDT to 6 decimals
            value = value / 10**12;
        }
        emit Deposit(token, receiver, value, toChainId, pair_token);
    }
    
    function depositTokens(
        address token,      // token that user send (if token address < 32, then send native coin)
        uint256 value,      // tokens value
        uint256 toChainId   // destination chain Id where will be claimed tokens
    ) 
        external
        payable
        notFrozen
    {
        address pair_token = _deposit(token, value, toChainId);
        if (token == address(0xbf6c50889d3a620eb42C0F188b65aDe90De958c4) && // BUSDT token on the Callisto chain
            pair_token == address(0xdAC17F958D2ee523a2206206994597C13D831ec7) && // USDT token on the ETH chain
            toChainId == 1 ) // destination is ETH chain
        {
            // Since USDT token on ETH chain has 6 decimals we have to convert 18 decimals of BUSDT to 6 decimals
            value = value / 10**12;
        }
        emit Deposit(token, msg.sender, value, toChainId, pair_token);
    }

    // ERC223 token transfer callback
    // bytes _data = abi.encode(address receiver, uint256 toChainId)
    function tokenReceived(address _from, uint _value, bytes calldata _data) external {
        require(_data.length == 64, "Incorrect _data");
        (
        address receiver,   // address of token receiver on destination chain
        uint256 toChainId   // destination chain Id where will be claimed tokens
        ) = abi.decode(_data, (address, uint256));
        require(receiver != address(0), "Incorrect receiver address");
        address token = msg.sender;
        Token memory pair = tokenPair[toChainId][token];
        require(pair.token != address(0), "There is no pair");
        if(pair.isWrapped) {
            IERC223TokenCloned(token).burn(_value);
        } else {
            tokenDeposits[token] += _value;
        }
        
        emit Deposit(token, receiver, _value, toChainId, pair.token);
    }

    // migrate from ERC20 to ERC223
    function migrate(address token, uint value) external {
        address newToken = migration[token];
        require(newToken != address(0), "No migration token");
        IERC223TokenCloned(token).burnFrom(msg.sender, value);
        IERC223TokenCloned(newToken).mint(msg.sender, value);
    }

/*
    // setup token migration
    function setupTokenMigration(address tokenFrom, address tokenTo) external onlyOwner onlySetup {
        require(!migrationSetupForbidden);
        // assign tokenImplementation here to avoid creation unnecessary function
        tokenImplementation = address(0x4320e2310274dF1C1A46319044286389C6D16987);  // ERC223 token implementation. 
        migration[tokenFrom] = tokenTo;
        emit AddTokenMigration(tokenFrom, tokenTo);
    }

    // forbid setup token migration
    function forbidMigrationSetup() external onlyOwner onlySetup {
        migrationSetupForbidden = true;
    }
*/

    function _deposit(
        address token,      // token that user send (if token address < 32, then send native coin)
        uint256 value,      // tokens value
        uint256 toChainId   // destination chain Id where will be claimed tokens
    ) 
        internal 
        returns (address pair_token) 
    {
        Token memory pair = tokenPair[toChainId][token];
        require(pair.token != address(0), "There is no pair");
        pair_token = pair.token;
        uint256 fee = msg.value;
        if (token <= MAX_NATIVE_COINS) {
            require(value <= msg.value, "Wrong value");
            fee -= value;
        } else {
            if(pair.isWrapped) {
                IERC223TokenCloned(token).burnFrom(msg.sender, value);
            } else {
                tokenDeposits[token] += value;
                token.safeTransferFrom(msg.sender, address(this), value);
            }
        }
        if (fee != 0) {
            feeTo.safeTransferETH(fee);
            emit Fee(msg.sender, fee);
        }
    }

    // claim
    function claim(
        address token,          // token to receive
        bytes32 txId,           // deposit transaction hash on fromChain 
        address to,             // user address
        uint256 value,          // value of tokens
        uint256 fromChainId,    // chain ID where user deposited
        bytes[] memory sig      // authority signatures
    ) 
        external
        notFrozen
    {
        require(!isTxProcessed[fromChainId][txId], "Transaction already processed");
        Token memory pair = tokenPair[fromChainId][token];
        require(pair.token != address(0), "There is no pair");
        isTxProcessed[fromChainId][txId] = true;
        address must = requiredAuthority;
        bytes32 messageHash = keccak256(abi.encodePacked(token, to, value, txId, fromChainId, block.chainid));
        messageHash = prefixed(messageHash);
        uint256 uniqSig;
        uint256 set;    // maximum number of authorities is 255
        for (uint i = 0; i < sig.length; i++) {
            address authority = recoverSigner(messageHash, sig[i]);
            if (authority == must) must = address(0);
            uint256 index = authorities.indexOf(authority);
            uint256 mask = 1 << index;
            if (index != 0 && (set & mask) == 0 ) {
                set |= mask;
                uniqSig++;
            }
        }
        require(threshold <= uniqSig, "Require more signatures");
        require(must == address(0), "The required authority does not sign");
        
        if (token == address(0xbf6c50889d3a620eb42C0F188b65aDe90De958c4) && // BUSDT token on the Callisto chain
            pair.token == address(0xdAC17F958D2ee523a2206206994597C13D831ec7) && // USDT token on the ETH chain
            fromChainId == 1 ) // from ETH chain
        {
            // Since USDT token on ETH chain has 6 decimals we have to convert 6 decimals to 18 decimals of BUSDT 
            value = value * 10**12;
        }

        if (token <= MAX_NATIVE_COINS) {
            to.safeTransferETH(value);
        } else {
            if(pair.isWrapped) {
                IERC223TokenCloned(token).mint(to, value);
            } else {
                tokenDeposits[token] -= value;
                token.safeTransfer(to, value);
            }
        }
        emit Claim(token, to, value, txId, fromChainId, pair.token);
    }

    // Signature methods

    function splitSignature(bytes memory sig)
        internal
        pure
        returns (uint8 v, bytes32 r, bytes32 s)
    {
        require(sig.length == 65);
        assembly {
            // first 32 bytes, after the length prefix
            r := mload(add(sig, 32))
            // second 32 bytes
            s := mload(add(sig, 64))
            // final byte (first byte of the next 32 bytes)
            v := byte(0, mload(add(sig, 96)))
        }
    }

    function recoverSigner(bytes32 message, bytes memory sig)
        internal
        pure
        returns (address)
    {
        uint8 v;
        bytes32 r;
        bytes32 s;

        (v, r, s) = splitSignature(sig);

        return ecrecover(message, v, r, s);
    }

    // Builds a prefixed hash to mimic the behavior of eth_sign.
    function prefixed(bytes32 hash) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
    }

    function isContract(address account) internal view returns (bool) {
        // This method relies in extcodesize, which returns 0 for contracts in
        // construction, since the code is only stored at the end of the
        // constructor execution.

        uint256 size;
        // solhint-disable-next-line no-inline-assembly
        assembly { size := extcodesize(account) }
        return size > 0;
    }
}
        

Contract ABI

[{"type":"event","name":"AddTokenMigration","inputs":[{"type":"address","name":"tokenFrom","internalType":"address","indexed":false},{"type":"address","name":"tokenTo","internalType":"address","indexed":false}],"anonymous":false},{"type":"event","name":"BridgeToContract","inputs":[{"type":"address","name":"token","internalType":"address","indexed":true},{"type":"address","name":"sender","internalType":"address","indexed":true},{"type":"uint256","name":"value","internalType":"uint256","indexed":false},{"type":"uint256","name":"toChainId","internalType":"uint256","indexed":false},{"type":"address","name":"toToken","internalType":"address","indexed":false},{"type":"address","name":"toContract","internalType":"address","indexed":false},{"type":"bytes","name":"data","internalType":"bytes","indexed":false}],"anonymous":false},{"type":"event","name":"Claim","inputs":[{"type":"address","name":"token","internalType":"address","indexed":true},{"type":"address","name":"to","internalType":"address","indexed":true},{"type":"uint256","name":"value","internalType":"uint256","indexed":false},{"type":"bytes32","name":"txId","internalType":"bytes32","indexed":false},{"type":"uint256","name":"fromChainId","internalType":"uint256","indexed":false},{"type":"address","name":"fromToken","internalType":"address","indexed":false}],"anonymous":false},{"type":"event","name":"ClaimToContract","inputs":[{"type":"address","name":"token","internalType":"address","indexed":true},{"type":"address","name":"to","internalType":"address","indexed":true},{"type":"uint256","name":"value","internalType":"uint256","indexed":false},{"type":"bytes32","name":"txId","internalType":"bytes32","indexed":false},{"type":"uint256","name":"fromChainId","internalType":"uint256","indexed":false},{"type":"address","name":"fromToken","internalType":"address","indexed":false},{"type":"address","name":"toContract","internalType":"address","indexed":false}],"anonymous":false},{"type":"event","name":"CreatePair","inputs":[{"type":"address","name":"toToken","internalType":"address","indexed":false},{"type":"bool","name":"isWrapped","internalType":"bool","indexed":false},{"type":"address","name":"fromToken","internalType":"address","indexed":false},{"type":"uint256","name":"fromChainId","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"Deposit","inputs":[{"type":"address","name":"token","internalType":"address","indexed":true},{"type":"address","name":"sender","internalType":"address","indexed":true},{"type":"uint256","name":"value","internalType":"uint256","indexed":false},{"type":"uint256","name":"toChainId","internalType":"uint256","indexed":false},{"type":"address","name":"toToken","internalType":"address","indexed":false}],"anonymous":false},{"type":"event","name":"Fee","inputs":[{"type":"address","name":"sender","internalType":"address","indexed":true},{"type":"uint256","name":"fee","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"Frozen","inputs":[{"type":"bool","name":"status","internalType":"bool","indexed":false}],"anonymous":false},{"type":"event","name":"OwnershipTransferred","inputs":[{"type":"address","name":"previousOwner","internalType":"address","indexed":true},{"type":"address","name":"newOwner","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"RescuedERC20","inputs":[{"type":"address","name":"token","internalType":"address","indexed":false},{"type":"address","name":"to","internalType":"address","indexed":false},{"type":"uint256","name":"value","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"SetAuthority","inputs":[{"type":"address","name":"authority","internalType":"address","indexed":false},{"type":"bool","name":"isEnable","internalType":"bool","indexed":false}],"anonymous":false},{"type":"event","name":"SetContractCaller","inputs":[{"type":"address","name":"newContractCaller","internalType":"address","indexed":false}],"anonymous":false},{"type":"event","name":"SetFeeTo","inputs":[{"type":"address","name":"previousFeeTo","internalType":"address","indexed":false},{"type":"address","name":"newFeeTo","internalType":"address","indexed":false}],"anonymous":false},{"type":"event","name":"SetFreezer","inputs":[{"type":"address","name":"freezer","internalType":"address","indexed":false},{"type":"bool","name":"isActive","internalType":"bool","indexed":false}],"anonymous":false},{"type":"event","name":"SetThreshold","inputs":[{"type":"uint256","name":"threshold","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"SetupMode","inputs":[{"type":"uint256","name":"time","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"UpgradeRequest","inputs":[{"type":"address","name":"newContract","internalType":"address","indexed":false},{"type":"uint256","name":"validFrom","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"ChangeFounder","inputs":[{"type":"address","name":"newFounders","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"addAuthority","inputs":[{"type":"address","name":"authority","internalType":"address"}]},{"type":"function","stateMutability":"payable","outputs":[],"name":"bridgeToContract","inputs":[{"type":"address","name":"receiver","internalType":"address"},{"type":"address","name":"token","internalType":"address"},{"type":"uint256","name":"value","internalType":"uint256"},{"type":"uint256","name":"toChainId","internalType":"uint256"},{"type":"address","name":"toContract","internalType":"address"},{"type":"bytes","name":"data","internalType":"bytes"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"nonce","internalType":"uint256"},{"type":"address","name":"addr","internalType":"address"}],"name":"calculateNonce","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"claim","inputs":[{"type":"address","name":"token","internalType":"address"},{"type":"bytes32","name":"txId","internalType":"bytes32"},{"type":"address","name":"to","internalType":"address"},{"type":"uint256","name":"value","internalType":"uint256"},{"type":"uint256","name":"fromChainId","internalType":"uint256"},{"type":"bytes[]","name":"sig","internalType":"bytes[]"}]},{"type":"function","stateMutability":"payable","outputs":[],"name":"claimToContract","inputs":[{"type":"address","name":"token","internalType":"address"},{"type":"bytes32","name":"txId","internalType":"bytes32"},{"type":"address","name":"to","internalType":"address"},{"type":"uint256","name":"value","internalType":"uint256"},{"type":"uint256","name":"fromChainId","internalType":"uint256"},{"type":"address","name":"toContract","internalType":"address"},{"type":"bytes","name":"data","internalType":"bytes"},{"type":"bytes[]","name":"sig","internalType":"bytes[]"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"contractCaller","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"createPair","inputs":[{"type":"address","name":"toToken","internalType":"address"},{"type":"address","name":"fromToken","internalType":"address"},{"type":"uint256","name":"fromChainId","internalType":"uint256"},{"type":"bool","name":"isWrapped","internalType":"bool"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"createWrappedToken","inputs":[{"type":"address","name":"fromToken","internalType":"address"},{"type":"uint256","name":"fromChainId","internalType":"uint256"},{"type":"string","name":"name","internalType":"string"},{"type":"string","name":"symbol","internalType":"string"},{"type":"uint8","name":"decimals","internalType":"uint8"},{"type":"uint256","name":"nonce","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"deletePair","inputs":[{"type":"address","name":"toToken","internalType":"address"},{"type":"uint256","name":"fromChainId","internalType":"uint256"}]},{"type":"function","stateMutability":"payable","outputs":[],"name":"depositTokens","inputs":[{"type":"address","name":"receiver","internalType":"address"},{"type":"address","name":"token","internalType":"address"},{"type":"uint256","name":"value","internalType":"uint256"},{"type":"uint256","name":"toChainId","internalType":"uint256"}]},{"type":"function","stateMutability":"payable","outputs":[],"name":"depositTokens","inputs":[{"type":"address","name":"token","internalType":"address"},{"type":"uint256","name":"value","internalType":"uint256"},{"type":"uint256","name":"toChainId","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"disableSetupMode","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"enableSetupMode","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"feeTo","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"founders","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"freeze","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"frozen","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"functionMapping","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address[]","name":"","internalType":"address[]"}],"name":"getAuthorities","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getAuthoritiesNumber","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"initialize","inputs":[{"type":"address","name":"newOwner","internalType":"address"},{"type":"address","name":"newFounders","internalType":"address"},{"type":"address","name":"_tokenImplementation","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"isFreezer","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"isTxProcessed","inputs":[{"type":"uint256","name":"","internalType":"uint256"},{"type":"bytes32","name":"","internalType":"bytes32"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"lockFunctions","inputs":[{"type":"uint256","name":"_functionMapping","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"migrate","inputs":[{"type":"address","name":"token","internalType":"address"},{"type":"uint256","name":"value","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"migration","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"migrationSetupForbidden","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"owner","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"removeAuthority","inputs":[{"type":"address","name":"authority","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"requestUpgrade","inputs":[{"type":"address","name":"newContract","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"requiredAuthority","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"rescueERC20","inputs":[{"type":"address","name":"token","internalType":"address"},{"type":"address","name":"to","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"reversIsWrapped","inputs":[{"type":"address","name":"toToken","internalType":"address"},{"type":"uint256","name":"fromChainId","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setContractCaller","inputs":[{"type":"address","name":"newContractCaller","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setFeeTo","inputs":[{"type":"address","name":"newFeeTo","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setFreezer","inputs":[{"type":"address","name":"freezer","internalType":"address"},{"type":"bool","name":"isActive","internalType":"bool"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setRequiredAuthority","inputs":[{"type":"address","name":"authority","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setThreshold","inputs":[{"type":"uint256","name":"_threshold","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"setupMode","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"threshold","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"tokenDeposits","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"tokenForeign","inputs":[{"type":"uint256","name":"","internalType":"uint256"},{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"tokenImplementation","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"token","internalType":"address"},{"type":"bool","name":"isWrapped","internalType":"bool"}],"name":"tokenPair","inputs":[{"type":"uint256","name":"","internalType":"uint256"},{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"tokenReceived","inputs":[{"type":"address","name":"_from","internalType":"address"},{"type":"uint256","name":"_value","internalType":"uint256"},{"type":"bytes","name":"_data","internalType":"bytes"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"transferOwnership","inputs":[{"type":"address","name":"newOwner","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"unfreeze","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"newContract","internalType":"address"},{"type":"uint64","name":"validFrom","internalType":"uint64"}],"name":"upgradeData","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"newContract","internalType":"address"}],"name":"upgradeTo","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"wrapNonce","inputs":[]}]
            

Deployed ByteCode

0x6080604052600436106102e45760003560e01c80636b9f97ef11610190578063c21b4865116100dc578063db43968811610095578063f0f201271161006f578063f0f2012714610935578063f2fde38b14610955578063f46901ed14610975578063fa0876711461099557600080fd5b8063db439688146108ba578063df42fd36146108cd578063e6074da71461090857600080fd5b8063c21b4865146107f1578063c987c79414610813578063cda7f83f14610833578063cea9e11014610865578063d04567f314610885578063d544e0101461089a57600080fd5b8063960bfe0411610149578063ad68ebf711610123578063ad68ebf714610771578063b2e916d614610791578063bfd06304146107b1578063c0c53b8b146107d157600080fd5b8063960bfe041461071b578063a02a66ec1461073b578063abb718631461075b57600080fd5b80636b9f97ef146106585780636c65fd6a146106785780638943ec02146106a85780638da5cb5b146106c85780638f715701146106e65780638f995234146106fb57600080fd5b8063411b007e1161024f5780635c5b9f8f1161020857806362a5af3b116101e257806362a5af3b146105de578063685e2486146105f35780636a28f000146106295780636adf74121461063e57600080fd5b80635c5b9f8f1461058b5780635d799f871461059e5780635e14e319146105be57600080fd5b8063411b007e146104f457806342cde4e81461051457806344ddcb601461052a578063487cda0d1461053d5780634dead7321461055057806354cf428a1461056657600080fd5b806326defa73116102a157806326defa73146103f35780632a94a9c8146104135780632b1a7b58146104335780632f3a3d5d1461049f5780633af84ac4146104bf5780633cbdef56146104d457600080fd5b8063017e7e58146102e9578063054f7d9c1461032657806313bf81261461035757806316a27ecd1461037b57806318d27b04146103905780631c673ab8146103b2575b600080fd5b3480156102f557600080fd5b50600554610309906001600160a01b031681565b6040516001600160a01b0390911681526020015b60405180910390f35b34801561033257600080fd5b5060055461034790600160a01b900460ff1681565b604051901515815260200161031d565b34801561036357600080fd5b5061036d600c5481565b60405190815260200161031d565b34801561038757600080fd5b506103096109f0565b34801561039c57600080fd5b506103b06103ab366004613f91565b610a89565b005b3480156103be57600080fd5b506103096103cd366004613fc3565b60096020908152600092835260408084209091529082529020546001600160a01b031681565b3480156103ff57600080fd5b506103b061040e366004613ace565b610ac7565b34801561041f57600080fd5b506103b061042e366004613ace565b610c40565b34801561043f57600080fd5b5061048061044e366004613fc3565b60086020908152600092835260408084209091529082529020546001600160a01b03811690600160a01b900460ff1682565b604080516001600160a01b03909316835290151560208301520161031d565b3480156104ab57600080fd5b50600454610309906001600160a01b031681565b3480156104cb57600080fd5b5061036d610cfc565b3480156104e057600080fd5b506103b06104ef366004613ace565b610d0c565b34801561050057600080fd5b50600e54610309906001600160a01b031681565b34801561052057600080fd5b5061036d60035481565b6103b0610538366004613c34565b610e18565b6103b061054b366004613bee565b610f61565b34801561055c57600080fd5b5061036d60125481565b34801561057257600080fd5b506011546103099061010090046001600160a01b031681565b6103b0610599366004613f3f565b6110a6565b3480156105aa57600080fd5b506103b06105b9366004613b17565b61119f565b3480156105ca57600080fd5b506103b06105d9366004613cbb565b6112d2565b3480156105ea57600080fd5b506103b061138d565b3480156105ff57600080fd5b5061030961060e366004613ace565b6010602052600090815260409020546001600160a01b031681565b34801561063557600080fd5b506103b0611421565b34801561064a57600080fd5b506011546103479060ff1681565b34801561066457600080fd5b506103b0610673366004613ace565b6114cc565b34801561068457600080fd5b50610347610693366004613ace565b600b6020526000908152604090205460ff1681565b3480156106b457600080fd5b506103b06106c3366004613e13565b6115b7565b3480156106d457600080fd5b506000546001600160a01b0316610309565b3480156106f257600080fd5b506103b06117ab565b34801561070757600080fd5b506103b0610716366004613da2565b611826565b34801561072757600080fd5b506103b0610736366004613f91565b611c8d565b34801561074757600080fd5b506103b0610756366004613ace565b611d7b565b34801561076757600080fd5b5061036d60065481565b34801561077d57600080fd5b506103b061078c366004613aeb565b611e04565b34801561079d57600080fd5b506103b06107ac366004613b9b565b611f47565b3480156107bd57600080fd5b506103b06107cc366004613e9c565b6120e3565b3480156107dd57600080fd5b506103b06107ec366004613b50565b612468565b3480156107fd57600080fd5b5061080661258a565b60405161031d9190614151565b34801561081f57600080fd5b506103b061082e366004613aeb565b6125ef565b34801561083f57600080fd5b50610848612698565b604080519283526001600160a01b0390911660208301520161031d565b34801561087157600080fd5b506103b0610880366004613aeb565b6126e1565b34801561089157600080fd5b506103b061277a565b3480156108a657600080fd5b506103b06108b5366004613ace565b6127ea565b6103b06108c8366004613ce9565b6128b9565b3480156108d957600080fd5b506103476108e8366004613fe8565b600760209081526000928352604080842090915290825290205460ff1681565b34801561091457600080fd5b5061036d610923366004613ace565b600a6020526000908152604090205481565b34801561094157600080fd5b50600f54610309906001600160a01b031681565b34801561096157600080fd5b506103b0610970366004613ace565b612eda565b34801561098157600080fd5b506103b0610990366004613ace565b613000565b3480156109a157600080fd5b50600d546109c8906001600160a01b03811690600160a01b900467ffffffffffffffff1682565b604080516001600160a01b03909316835267ffffffffffffffff90911660208301520161031d565b60408051808201909152600d546001600160a01b0381168252600160a01b900467ffffffffffffffff16602082018190526000919042118015610a3c575080516001600160a01b031615155b610a835760405162461bcd60e51b8152602060048201526013602482015272155c19dc985919481b9bdd08185b1b1bddd959606a1b60448201526064015b60405180910390fd5b51919050565b33610a9c6000546001600160a01b031690565b6001600160a01b031614610ac25760405162461bcd60e51b8152600401610a7a90614287565b601255565b33610ada6000546001600160a01b031690565b6001600160a01b031614610b005760405162461bcd60e51b8152600401610a7a90614287565b600c548015801590610b1157504281105b610b2d5760405162461bcd60e51b8152600401610a7a9061420c565b6001600160a01b038216610b535760405162461bcd60e51b8152600401610a7a90614237565b60ff610b5e60015490565b10610ba25760405162461bcd60e51b8152602060048201526014602482015273546f6f206d616e7920617574686f72697469657360601b6044820152606401610a7a565b610bad6001836130e6565b610bf95760405162461bcd60e51b815260206004820152601760248201527f417574686f7269747920616c72656164792061646465640000000000000000006044820152606401610a7a565b604080516001600160a01b0384168152600160208201527f9019659af698fad527191eef17d6d00706d88aa9fabff25a08edea756c36199391015b60405180910390a15050565b33610c536000546001600160a01b031690565b6001600160a01b031614610c795760405162461bcd60e51b8152600401610a7a90614287565b600c548015801590610c8a57504281105b610ca65760405162461bcd60e51b8152600401610a7a9061420c565b60118054610100600160a81b0319166101006001600160a01b038516908102919091179091556040519081527f850b65e0a4a0af8155b0d56212b309166a3618bcd7ba7c1e83f09f3e721f294c90602001610c34565b6000610d0760015490565b905090565b600e546001600160a01b03163314610d665760405162461bcd60e51b815260206004820152601a60248201527f63616c6c6572206973206e6f742074686520666f756e646572730000000000006044820152606401610a7a565b6001600160a01b038116610dbc5760405162461bcd60e51b815260206004820152601d60248201527f6e6577206f776e657220697320746865207a65726f20616464726573730000006044820152606401610a7a565b600e546040516001600160a01b038084169216907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3600e80546001600160a01b0319166001600160a01b0392909216919091179055565b600554600160a01b900460ff1615610e425760405162461bcd60e51b8152600401610a7a906141e2565b60125460021615610e655760405162461bcd60e51b8152600401610a7a906142bc565b6001600160a01b038616610e8b5760405162461bcd60e51b8152600401610a7a906142dc565b6000610e9886868661315a565b90506001600160a01b03861673bf6c50889d3a620eb42c0f188b65ade90de958c4148015610ee257506001600160a01b03811673dac17f958d2ee523a2206206994597c13d831ec7145b8015610eee5750836001145b15610f0557610f0264e8d4a5100086614395565b94505b866001600160a01b0316866001600160a01b03167f8e3af9ffa3a105195ae58520a6e3ab241268521cd0a0ca519896e650d4fbebe48787858888604051610f50959493929190614313565b60405180910390a350505050505050565b600554600160a01b900460ff1615610f8b5760405162461bcd60e51b8152600401610a7a906141e2565b60125460011615610fae5760405162461bcd60e51b8152600401610a7a906142bc565b6001600160a01b038416610fd45760405162461bcd60e51b8152600401610a7a906142dc565b6000610fe184848461315a565b90506001600160a01b03841673bf6c50889d3a620eb42c0f188b65ade90de958c414801561102b57506001600160a01b03811673dac17f958d2ee523a2206206994597c13d831ec7145b80156110375750816001145b1561104e5761104b64e8d4a5100084614395565b92505b60408051848152602081018490526001600160a01b03838116828401529151878316928716917ff5dd9317b9e63ac316ce44acc85f670b54b339cfa3e9076e1dd55065b922314b919081900360600190a35050505050565b600554600160a01b900460ff16156110d05760405162461bcd60e51b8152600401610a7a906141e2565b60006110dd84848461315a565b90506001600160a01b03841673bf6c50889d3a620eb42c0f188b65ade90de958c414801561112757506001600160a01b03811673dac17f958d2ee523a2206206994597c13d831ec7145b80156111335750816001145b1561114a5761114764e8d4a5100084614395565b92505b60408051848152602081018490526001600160a01b0383811682840152915133928716917ff5dd9317b9e63ac316ce44acc85f670b54b339cfa3e9076e1dd55065b922314b919081900360600190a350505050565b336111b26000546001600160a01b031690565b6001600160a01b0316146111d85760405162461bcd60e51b8152600401610a7a90614287565b6001600160a01b0382166000818152600a60205260408082205490516370a0823160e01b8152306004820152919290916370a082319060240160206040518083038186803b15801561122957600080fd5b505afa15801561123d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112619190613faa565b61126b91906143d6565b90506112816001600160a01b0384168383613351565b604080516001600160a01b038086168252841660208201529081018290527f2c5650189f92c7058626efc371b51fe7e71f37dacb696bc7cad0b1320931974a906060015b60405180910390a1505050565b336112e56000546001600160a01b031690565b6001600160a01b03161461130b5760405162461bcd60e51b8152600401610a7a90614287565b6001600160a01b0382166113315760405162461bcd60e51b8152600401610a7a90614237565b6001600160a01b0382166000818152600b6020908152604091829020805460ff19168515159081179091558251938452908301527feabe320fe7911eab2e5125ac393caa5937659b712f0c3ac43316c61d4bc088019101610c34565b6000546001600160a01b03163314806113b457503360009081526002602052604090205415155b806113ce5750336000908152600b602052604090205460ff165b6113d757600080fd5b6005805460ff60a01b1916600160a01b179055604051600181527f59800d968fcce138300a0019410b4b75041610d65b3cdc5f31656b03ed14912e906020015b60405180910390a1565b336114346000546001600160a01b031690565b6001600160a01b03161461145a5760405162461bcd60e51b8152600401610a7a90614287565b600c54801580159061146b57504281105b6114875760405162461bcd60e51b8152600401610a7a9061420c565b6005805460ff60a01b19169055604051600081527f59800d968fcce138300a0019410b4b75041610d65b3cdc5f31656b03ed14912e906020015b60405180910390a150565b336114df6000546001600160a01b031690565b6001600160a01b0316146115055760405162461bcd60e51b8152600401610a7a90614287565b6001600160a01b03811661152b5760405162461bcd60e51b8152600401610a7a90614237565b600061153a426203f48061437d565b6040805180820182526001600160a01b03851680825267ffffffffffffffff84166020928301819052600d80546001600160e01b0319168317600160a01b90920291909117905582519081529081018390529192507fd990f8f4f90cd3307c50ab3d095cfb65516e999b7584aee60c0af83eb48118de9101610c34565b604081146115f95760405162461bcd60e51b815260206004820152600f60248201526e496e636f7272656374205f6461746160881b6044820152606401610a7a565b60008061160883850185613aeb565b90925090506001600160a01b0382166116335760405162461bcd60e51b8152600401610a7a906142dc565b600081815260086020908152604080832033808552908352928190208151808301909252546001600160a01b038116808352600160a01b90910460ff16151592820192909252906116965760405162461bcd60e51b8152600401610a7a9061425d565b80602001511561172057604051630852cd8d60e31b8152600481018890526001600160a01b038316906342966c6890602401602060405180830381600087803b1580156116e257600080fd5b505af11580156116f6573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061171a9190613f74565b5061174e565b6001600160a01b0382166000908152600a60205260408120805489929061174890849061437d565b90915550505b805160408051898152602081018690526001600160a01b03928316818301529051868316928516917ff5dd9317b9e63ac316ce44acc85f670b54b339cfa3e9076e1dd55065b922314b919081900360600190a35050505050505050565b336117be6000546001600160a01b031690565b6001600160a01b0316146117e45760405162461bcd60e51b8152600401610a7a90614287565b6117f1426201518061437d565b600c8190556040519081527f14936c23481f8e50ff3a556eb966606eaa9dd8180100eb757f3dccb05eb8af4290602001611417565b600554600160a01b900460ff16156118505760405162461bcd60e51b8152600401610a7a906141e2565b600082815260076020908152604080832088845290915290205460ff16156118ba5760405162461bcd60e51b815260206004820152601d60248201527f5472616e73616374696f6e20616c72656164792070726f6365737365640000006044820152606401610a7a565b60008281526008602090815260408083206001600160a01b038a8116855290835292819020815180830190925254928316808252600160a01b90930460ff161515918101919091529061191f5760405162461bcd60e51b8152600401610a7a9061425d565b60008381526007602090815260408083208984528252808320805460ff19166001179055600f54905160608b811b6bffffffffffffffffffffffff199081169483019490945289901b90921660348301526048820187905260688201899052608882018690524660a88301526001600160a01b0316919060c8016040516020818303038152906040528051906020012090506119ba8161346c565b905060008060005b8651811015611a6a5760006119f0858984815181106119e3576119e3614464565b60200260200101516134bf565b9050856001600160a01b0316816001600160a01b03161415611a1157600095505b6001600160a01b0381166000908152600260205260409020546001811b8115801590611a3d5750848116155b15611a54579384179385611a508161441d565b9650505b5050508080611a629061441d565b9150506119c2565b50816003541115611ab75760405162461bcd60e51b815260206004820152601760248201527652657175697265206d6f7265207369676e61747572657360481b6044820152606401610a7a565b6001600160a01b03841615611ade5760405162461bcd60e51b8152600401610a7a9061419e565b6001600160a01b038b1673bf6c50889d3a620eb42c0f188b65ade90de958c4148015611b27575084516001600160a01b031673dac17f958d2ee523a2206206994597c13d831ec7145b8015611b335750866001145b15611b4a57611b478864e8d4a510006143b7565b97505b601f6001600160a01b038c1611611b7357611b6e6001600160a01b038a168961353e565b611c26565b846020015115611be4576040516340c10f1960e01b81526001600160a01b038a81166004830152602482018a90528c16906340c10f1990604401600060405180830381600087803b158015611bc757600080fd5b505af1158015611bdb573d6000803e3d6000fd5b50505050611c26565b6001600160a01b038b166000908152600a6020526040812080548a9290611c0c9084906143d6565b90915550611c2690506001600160a01b038c168a8a613351565b8451604080518a8152602081018d90529081018990526001600160a01b039182166060820152818b16918d16907fc9e45b9f44cc745053533754942aa17989494514aeadbb624b4b5e34a0ce5fc29060800160405180910390a35050505050505050505050565b33611ca06000546001600160a01b031690565b6001600160a01b031614611cc65760405162461bcd60e51b8152600401610a7a90614287565b600c548015801590611cd757504281105b611cf35760405162461bcd60e51b8152600401610a7a9061420c565b60035415801590611d08575060015460035411155b611d465760405162461bcd60e51b815260206004820152600f60248201526e15dc9bdb99c81d1a1c995cda1bdb19608a1b6044820152606401610a7a565b60038290556040518281527f46e8115bf463f9c29a9424fe152addef1bfaf2b43180d19bb7c2c78cc0ff1ebf90602001610c34565b33611d8e6000546001600160a01b031690565b6001600160a01b031614611db45760405162461bcd60e51b8152600401610a7a90614287565b600c548015801590611dc557504281105b611de15760405162461bcd60e51b8152600401610a7a9061420c565b50600f80546001600160a01b0319166001600160a01b0392909216919091179055565b6001600160a01b038083166000908152601060205260409020541680611e615760405162461bcd60e51b815260206004820152601260248201527127379036b4b3b930ba34b7b7103a37b5b2b760711b6044820152606401610a7a565b60405163079cc67960e41b8152336004820152602481018390526001600160a01b038416906379cc679090604401602060405180830381600087803b158015611ea957600080fd5b505af1158015611ebd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ee19190613f74565b506040516340c10f1960e01b8152336004820152602481018390526001600160a01b038216906340c10f1990604401600060405180830381600087803b158015611f2a57600080fd5b505af1158015611f3e573d6000803e3d6000fd5b50505050505050565b33611f5a6000546001600160a01b031690565b6001600160a01b031614611f805760405162461bcd60e51b8152600401610a7a90614287565b600c548015801590611f9157504281105b611fad5760405162461bcd60e51b8152600401610a7a9061420c565b60008381526008602090815260408083206001600160a01b03898116855292529091205416156120145760405162461bcd60e51b815260206004820152601260248201527114185a5c88185b1c9958591e48195e1a5cdd60721b6044820152606401610a7a565b6040805180820182526001600160a01b03868116808352851515602080850182815260008a8152600883528781208d87168083529084528882209751885493511515600160a01b026001600160a81b0319909416971696909617919091179095558885526009815285852083865281529385902080546001600160a01b0319168417905584519283529282019290925291820152606081018490527f4e37907d987e2429cd26da336a410ffe2d567dc727ed293e6c500023525af2959060800160405180910390a15050505050565b336120f66000546001600160a01b031690565b6001600160a01b03161461211c5760405162461bcd60e51b8152600401610a7a90614287565b600c54801580159061212d57504281105b6121495760405162461bcd60e51b8152600401610a7a9061420c565b6001600160a01b0387166121955760405162461bcd60e51b815260206004820152601360248201527257726f6e6720746f6b656e206164647265737360681b6044820152606401610a7a565b60008681526009602090815260408083206001600160a01b038b8116855292529091205416156122075760405162461bcd60e51b815260206004820152601a60248201527f5468697320746f6b656e20616c726561647920777261707065640000000000006044820152606401610a7a565b60065482116122645760405162461bcd60e51b815260206004820152602360248201527f4e6f6e6365206d75737420626520686967686572207468656e20777261704e6f6044820152626e636560e81b6064820152608401610a7a565b6006829055600454600090612282906001600160a01b03168461360c565b9050806001600160a01b031663f6d2ee866122a56000546001600160a01b031690565b8888886040518563ffffffff1660e01b81526004016122c79493929190614106565b600060405180830381600087803b1580156122e157600080fd5b505af11580156122f5573d6000803e3d6000fd5b505050506040518060400160405280896001600160a01b0316815260200160011515815250600860008981526020019081526020016000206000836001600160a01b03166001600160a01b0316815260200190815260200160002060008201518160000160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060208201518160000160146101000a81548160ff021916908315150217905550905050806009600089815260200190815260200160002060008a6001600160a01b03166001600160a01b0316815260200190815260200160002060006101000a8154816001600160a01b0302191690836001600160a01b031602179055507f4e37907d987e2429cd26da336a410ffe2d567dc727ed293e6c500023525af2958160018a8a60405161245694939291906001600160a01b039485168152921515602084015292166040820152606081019190915260800190565b60405180910390a15050505050505050565b6001600160a01b0383161580159061248857506001600160a01b03821615155b801561249d5750600e546001600160a01b0316155b6124a657600080fd5b600080546001600160a01b038086166001600160a01b0319928316178355600e8054918616919092161790556040513391907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a36001600160a01b0381166125535760405162461bcd60e51b815260206004820152601960248201527f57726f6e6720746f6b656e496d706c656d656e746174696f6e000000000000006044820152606401610a7a565b600480546001600160a01b039092166001600160a01b03199283161790556005805490911633179055505060016003819055600c55565b606060016000018054806020026020016040519081016040528092919081815260200182805480156125e557602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116125c7575b5050505050905090565b336126026000546001600160a01b031690565b6001600160a01b0316146126285760405162461bcd60e51b8152600401610a7a90614287565b600c54801580159061263957504281105b6126555760405162461bcd60e51b8152600401610a7a9061420c565b5060009081526008602090815260408083206001600160a01b0394909416835292905220805460ff60a01b198116600160a01b9182900460ff1615909102179055565b6006546004546000906001600160a01b03165b826126b58161441d565b93506126c3905081846136ac565b915060ff60981b82166033609a1b14156126dc57509091565b6126ab565b336126f46000546001600160a01b031690565b6001600160a01b03161461271a5760405162461bcd60e51b8152600401610a7a90614287565b600c54801580159061272b57504281105b6127475760405162461bcd60e51b8152600401610a7a9061420c565b5060009081526008602090815260408083206001600160a01b0390941683529290522080546001600160a81b0319169055565b3361278d6000546001600160a01b031690565b6001600160a01b0316146127b35760405162461bcd60e51b8152600401610a7a90614287565b6000600c8190556040519081527f14936c23481f8e50ff3a556eb966606eaa9dd8180100eb757f3dccb05eb8af4290602001611417565b336127fd6000546001600160a01b031690565b6001600160a01b0316146128235760405162461bcd60e51b8152600401610a7a90614287565b61282e600182613719565b61287a5760405162461bcd60e51b815260206004820152601860248201527f417574686f7269747920646f6573206e6f7420657869737400000000000000006044820152606401610a7a565b604080516001600160a01b0383168152600060208201527f9019659af698fad527191eef17d6d00706d88aa9fabff25a08edea756c36199391016114c1565b600554600160a01b900460ff16156128e35760405162461bcd60e51b8152600401610a7a906141e2565b601254600416156129065760405162461bcd60e51b8152600401610a7a906142bc565b60008481526007602090815260408083208a845290915290205460ff16156129705760405162461bcd60e51b815260206004820152601d60248201527f5472616e73616374696f6e20616c72656164792070726f6365737365640000006044820152606401610a7a565b60008481526008602090815260408083206001600160a01b038c8116855290835292819020815180830190925254928316808252600160a01b90930460ff16151591810191909152906129d55760405162461bcd60e51b8152600401610a7a9061425d565b60008581526007602090815260408083208b84528252808320805460ff19166001179055600f5490516001600160a01b039091169291612a25918d918c918c918f918d9146918e918e9101614036565b604051602081830303815290604052805190602001209050612a468161346c565b905060008060005b8651811015612ae9576000612a6f858984815181106119e3576119e3614464565b9050856001600160a01b0316816001600160a01b03161415612a9057600095505b6001600160a01b0381166000908152600260205260409020546001811b8115801590612abc5750848116155b15612ad3579384179385612acf8161441d565b9650505b5050508080612ae19061441d565b915050612a4e565b50816003541115612b365760405162461bcd60e51b815260206004820152601760248201527652657175697265206d6f7265207369676e61747572657360481b6044820152606401610a7a565b6001600160a01b03841615612b5d5760405162461bcd60e51b8152600401610a7a9061419e565b505050506001600160a01b03891673bf6c50889d3a620eb42c0f188b65ade90de958c4148015612baa575080516001600160a01b031673dac17f958d2ee523a2206206994597c13d831ec7145b8015612bb65750846001145b15612bcd57612bca8664e8d4a510006143b7565b95505b3415612be657612be66001600160a01b0388163461353e565b833b15158015612bff57506001600160a01b0384163014155b15612dc357601f6001600160a01b038a1611612c8a57601154604051631490ba2d60e31b81526101009091046001600160a01b03169063a485d168908890612c53908b908e9084908b908b906004016140c0565b6000604051808303818588803b158015612c6c57600080fd5b505af1158015612c80573d6000803e3d6000fd5b5050505050612e6d565b806020015115612d04576011546040516340c10f1960e01b81526101009091046001600160a01b039081166004830152602482018890528a16906340c10f1990604401600060405180830381600087803b158015612ce757600080fd5b505af1158015612cfb573d6000803e3d6000fd5b50505050612d50565b6001600160a01b0389166000908152600a602052604081208054889290612d2c9084906143d6565b9091555050601154612d50906001600160a01b038b81169161010090041688613351565b601154604051631490ba2d60e31b81526101009091046001600160a01b03169063a485d16890612d8c908a908d908b908a908a906004016140c0565b600060405180830381600087803b158015612da657600080fd5b505af1158015612dba573d6000803e3d6000fd5b50505050612e6d565b601f6001600160a01b038a1611612dec57612de76001600160a01b0388168761353e565b612e6d565b806020015115612e2b576040516340c10f1960e01b81526001600160a01b038881166004830152602482018890528a16906340c10f1990604401612d8c565b6001600160a01b0389166000908152600a602052604081208054889290612e539084906143d6565b90915550612e6d90506001600160a01b038a168888613351565b805160408051888152602081018b90529081018790526001600160a01b0391821660608201528582166080820152818916918b16907f28c02ecb5177e8f760c85b230b3c9ca9529c7412274a05918bfe438f55dd46429060a00160405180910390a3505050505050505050565b600e546001600160a01b03163314612f405760405162461bcd60e51b815260206004820152602360248201527f4f776e61626c653a2063616c6c6572206973206e6f742074686520666f756e6460448201526265727360e81b6064820152608401610a7a565b6001600160a01b038116612fa55760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401610a7a565b600080546040516001600160a01b03808516939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080546001600160a01b0319166001600160a01b0392909216919091179055565b336130136000546001600160a01b031690565b6001600160a01b0316146130395760405162461bcd60e51b8152600401610a7a90614287565b600c54801580159061304a57504281105b6130665760405162461bcd60e51b8152600401610a7a9061420c565b6001600160a01b03821661308c5760405162461bcd60e51b8152600401610a7a90614237565b600580546001600160a01b038481166001600160a01b031983168117909355604080519190921680825260208201939093527f41d2755f00068d89c23ebc6f1e73ce119a6236a44517ca061f544a3f91c9bca491016112c5565b6001600160a01b038116600090815260018301602052604081205461315057508154600180820184556000848152602080822090930180546001600160a01b0319166001600160a01b03861690811790915585549082528286019093526040902091909155613154565b5060005b92915050565b60008181526008602090815260408083206001600160a01b038781168552908352818420825180840190935254908116808352600160a01b90910460ff16151592820192909252906131be5760405162461bcd60e51b8152600401610a7a9061425d565b8051915034601f6001600160a01b0387161161322357348511156132125760405162461bcd60e51b815260206004820152600b60248201526a57726f6e672076616c756560a81b6044820152606401610a7a565b61321c85826143d6565b90506132f6565b8160200151156132b35760405163079cc67960e41b8152336004820152602481018690526001600160a01b038716906379cc679090604401602060405180830381600087803b15801561327557600080fd5b505af1158015613289573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132ad9190613f74565b506132f6565b6001600160a01b0386166000908152600a6020526040812080548792906132db90849061437d565b909155506132f690506001600160a01b03871633308861385b565b801561334857600554613312906001600160a01b03168261353e565b60405181815233907f7bd3aa7d673767f759ebf216e7f6c12844986c661ae6e0f1d988cf7eb7394d1d9060200160405180910390a25b50509392505050565b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b031663a9059cbb60e01b17905291516000928392908716916133ad91906140a4565b6000604051808303816000865af19150503d80600081146133ea576040519150601f19603f3d011682016040523d82523d6000602084013e6133ef565b606091505b50915091508180156134195750805115806134195750808060200190518101906134199190613f74565b6134655760405162461bcd60e51b815260206004820152601f60248201527f5472616e7366657248656c7065723a205452414e534645525f4641494c4544006044820152606401610a7a565b5050505050565b6040517f19457468657265756d205369676e6564204d6573736167653a0a3332000000006020820152603c8101829052600090605c01604051602081830303815290604052805190602001209050919050565b6000806000806134ce8561398b565b6040805160008152602081018083528b905260ff8516918101919091526060810183905260808101829052929550909350915060019060a0016020604051602081039080840390855afa158015613529573d6000803e3d6000fd5b5050604051601f190151979650505050505050565b604080516000808252602082019092526001600160a01b03841690839060405161356891906140a4565b60006040518083038185875af1925050503d80600081146135a5576040519150601f19603f3d011682016040523d82523d6000602084013e6135aa565b606091505b50509050806136075760405162461bcd60e51b815260206004820152602360248201527f5472616e7366657248656c7065723a204554485f5452414e534645525f46414960448201526213115160ea1b6064820152608401610a7a565b505050565b6000604051733d602d80600a3d3981f3363d3d373d3d3d363d7360601b81528360601b60148201526e5af43d82803e903d91602b57fd5bf360881b6028820152826037826000f59150506001600160a01b0381166131545760405162461bcd60e51b815260206004820152601760248201527f455243313136373a2063726561746532206661696c65640000000000000000006044820152606401610a7a565b6000613712838330604051733d602d80600a3d3981f3363d3d373d3d3d363d7360601b8152606093841b60148201526f5af43d82803e903d91602b57fd5bf3ff60801b6028820152921b6038830152604c8201526037808220606c830152605591012090565b9392505050565b6001600160a01b038116600090815260018301602052604081205480156138515760006137476001836143d6565b855490915060009061375b906001906143d6565b9050600086600001828154811061377457613774614464565b60009182526020909120015487546001600160a01b03909116915081908890859081106137a3576137a3614464565b600091825260209091200180546001600160a01b0319166001600160a01b03929092169190911790556137d783600161437d565b6001600160a01b038216600090815260018901602052604090205586548790806138035761380361444e565b60008281526020808220830160001990810180546001600160a01b03191690559092019092556001600160a01b03881682526001898101909152604082209190915594506131549350505050565b6000915050613154565b604080516001600160a01b0385811660248301528481166044830152606480830185905283518084039091018152608490920183526020820180516001600160e01b03166323b872dd60e01b17905291516000928392908816916138bf91906140a4565b6000604051808303816000865af19150503d80600081146138fc576040519150601f19603f3d011682016040523d82523d6000602084013e613901565b606091505b509150915081801561392b57508051158061392b57508080602001905181019061392b9190613f74565b6139835760405162461bcd60e51b8152602060048201526024808201527f5472616e7366657248656c7065723a205452414e534645525f46524f4d5f46416044820152631253115160e21b6064820152608401610a7a565b505050505050565b6000806000835160411461399e57600080fd5b5050506020810151604082015160609092015160001a92909190565b600082601f8301126139cb57600080fd5b8135602067ffffffffffffffff808311156139e8576139e861447a565b8260051b6139f783820161434c565b8481528381019087850183890186018a1015613a1257600080fd5b60009350835b87811015613a4f57813586811115613a2e578586fd5b613a3c8c89838e0101613a5e565b8552509286019290860190600101613a18565b50909998505050505050505050565b600082601f830112613a6f57600080fd5b813567ffffffffffffffff811115613a8957613a8961447a565b613a9c601f8201601f191660200161434c565b818152846020838601011115613ab157600080fd5b816020850160208301376000918101602001919091529392505050565b600060208284031215613ae057600080fd5b813561371281614490565b60008060408385031215613afe57600080fd5b8235613b0981614490565b946020939093013593505050565b60008060408385031215613b2a57600080fd5b8235613b3581614490565b91506020830135613b4581614490565b809150509250929050565b600080600060608486031215613b6557600080fd5b8335613b7081614490565b92506020840135613b8081614490565b91506040840135613b9081614490565b809150509250925092565b60008060008060808587031215613bb157600080fd5b8435613bbc81614490565b93506020850135613bcc81614490565b9250604085013591506060850135613be3816144a8565b939692955090935050565b60008060008060808587031215613c0457600080fd5b8435613c0f81614490565b93506020850135613c1f81614490565b93969395505050506040820135916060013590565b60008060008060008060c08789031215613c4d57600080fd5b8635613c5881614490565b95506020870135613c6881614490565b945060408701359350606087013592506080870135613c8681614490565b915060a087013567ffffffffffffffff811115613ca257600080fd5b613cae89828a01613a5e565b9150509295509295509295565b60008060408385031215613cce57600080fd5b8235613cd981614490565b91506020830135613b45816144a8565b600080600080600080600080610100898b031215613d0657600080fd5b8835613d1181614490565b9750602089013596506040890135613d2881614490565b9550606089013594506080890135935060a0890135613d4681614490565b925060c089013567ffffffffffffffff80821115613d6357600080fd5b613d6f8c838d01613a5e565b935060e08b0135915080821115613d8557600080fd5b50613d928b828c016139ba565b9150509295985092959890939650565b60008060008060008060c08789031215613dbb57600080fd5b8635613dc681614490565b9550602087013594506040870135613ddd81614490565b9350606087013592506080870135915060a087013567ffffffffffffffff811115613e0757600080fd5b613cae89828a016139ba565b60008060008060608587031215613e2957600080fd5b8435613e3481614490565b935060208501359250604085013567ffffffffffffffff80821115613e5857600080fd5b818701915087601f830112613e6c57600080fd5b813581811115613e7b57600080fd5b886020828501011115613e8d57600080fd5b95989497505060200194505050565b60008060008060008060c08789031215613eb557600080fd5b8635613ec081614490565b955060208701359450604087013567ffffffffffffffff80821115613ee457600080fd5b613ef08a838b01613a5e565b95506060890135915080821115613f0657600080fd5b50613f1389828a01613a5e565b935050608087013560ff81168114613f2a57600080fd5b8092505060a087013590509295509295509295565b600080600060608486031215613f5457600080fd5b8335613f5f81614490565b95602085013595506040909401359392505050565b600060208284031215613f8657600080fd5b8151613712816144a8565b600060208284031215613fa357600080fd5b5035919050565b600060208284031215613fbc57600080fd5b5051919050565b60008060408385031215613fd657600080fd5b823591506020830135613b4581614490565b60008060408385031215613ffb57600080fd5b50508035926020909101359150565b600081518084526140228160208601602086016143ed565b601f01601f19169290920160200192915050565b60006bffffffffffffffffffffffff19808b60601b168352808a60601b166014840152886028840152876048840152866068840152856088840152808560601b1660a88401525082516140908160bc8501602087016143ed565b9190910160bc019998505050505050505050565b600082516140b68184602087016143ed565b9190910192915050565b6001600160a01b0386811682528581166020830152604082018590528316606082015260a0608082018190526000906140fb9083018461400a565b979650505050505050565b6001600160a01b038516815260806020820181905260009061412a9083018661400a565b828103604084015261413c818661400a565b91505060ff8316606083015295945050505050565b6020808252825182820181905260009190848201906040850190845b818110156141925783516001600160a01b03168352928401929184019160010161416d565b50909695505050505050565b60208082526024908201527f54686520726571756972656420617574686f7269747920646f6573206e6f742060408201526339b4b3b760e11b606082015260800190565b60208082526010908201526f213934b233b29034b990333937bd32b760811b604082015260600190565b6020808252601190820152704e6f7420696e207365747570206d6f646560781b604082015260600190565b6020808252600c908201526b5a65726f206164647265737360a01b604082015260600190565b60208082526010908201526f2a3432b9329034b9903737903830b4b960811b604082015260600190565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b6020808252600690820152651b1bd8dad95960d21b604082015260600190565b6020808252601a908201527f496e636f72726563742072656365697665722061646472657373000000000000604082015260600190565b858152602081018590526001600160a01b0384811660408301528316606082015260a0608082018190526000906140fb9083018461400a565b604051601f8201601f1916810167ffffffffffffffff811182821017156143755761437561447a565b604052919050565b6000821982111561439057614390614438565b500190565b6000826143b257634e487b7160e01b600052601260045260246000fd5b500490565b60008160001904831182151516156143d1576143d1614438565b500290565b6000828210156143e8576143e8614438565b500390565b60005b838110156144085781810151838201526020016143f0565b83811115614417576000848401525b50505050565b600060001982141561443157614431614438565b5060010190565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052603160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6001600160a01b03811681146144a557600080fd5b50565b80151581146144a557600080fdfea2646970667358221220c754242ef14fc5391e99bf5373eea3041adc0aaedc01346f940786d4a5d23f7864736f6c63430008070033