ETH Price: $2,853.98 (-2.97%)

Contract

0x51b206899BcFCBf4530412a43ed5C2336f91c90C

Overview

ETH Balance

0 ETH

ETH Value

$0.00

More Info

Private Name Tags

Transaction Hash
Block
From
To
Resend Transfer376995622026-01-15 3:32:0111 days ago1768447921IN
0x51b20689...36f91c90C
0.000005 ETH00.00000033

Latest 25 internal transactions (View All)

Advanced mode:
Parent Transaction Hash Block From To
386491322026-01-26 3:18:111 hr ago1769397491
0x51b20689...36f91c90C
0.000005 ETH
386491322026-01-26 3:18:111 hr ago1769397491
0x51b20689...36f91c90C
0.000005 ETH
386489252026-01-26 3:14:441 hr ago1769397284
0x51b20689...36f91c90C
0.000005 ETH
386489252026-01-26 3:14:441 hr ago1769397284
0x51b20689...36f91c90C
0.000005 ETH
386487332026-01-26 3:11:321 hr ago1769397092
0x51b20689...36f91c90C
0.000005 ETH
386487332026-01-26 3:11:321 hr ago1769397092
0x51b20689...36f91c90C
0.000005 ETH
385974902026-01-25 12:57:2915 hrs ago1769345849
0x51b20689...36f91c90C
0.000005 ETH
385974902026-01-25 12:57:2915 hrs ago1769345849
0x51b20689...36f91c90C
0.000005 ETH
385969072026-01-25 12:47:4615 hrs ago1769345266
0x51b20689...36f91c90C
0.000005 ETH
385969072026-01-25 12:47:4615 hrs ago1769345266
0x51b20689...36f91c90C
0.000005 ETH
385954622026-01-25 12:23:4115 hrs ago1769343821
0x51b20689...36f91c90C
0.000005 ETH
385954622026-01-25 12:23:4115 hrs ago1769343821
0x51b20689...36f91c90C
0.000005 ETH
385953432026-01-25 12:21:4215 hrs ago1769343702
0x51b20689...36f91c90C
0.000005 ETH
385953432026-01-25 12:21:4215 hrs ago1769343702
0x51b20689...36f91c90C
0.000005 ETH
385442992026-01-24 22:10:5830 hrs ago1769292658
0x51b20689...36f91c90C
0.000005 ETH
385442992026-01-24 22:10:5830 hrs ago1769292658
0x51b20689...36f91c90C
0.000005 ETH
385434382026-01-24 21:56:3730 hrs ago1769291797
0x51b20689...36f91c90C
0.000005 ETH
385434382026-01-24 21:56:3730 hrs ago1769291797
0x51b20689...36f91c90C
0.000005 ETH
385354442026-01-24 19:43:2332 hrs ago1769283803
0x51b20689...36f91c90C
0.000005 ETH
385354442026-01-24 19:43:2332 hrs ago1769283803
0x51b20689...36f91c90C
0.000005 ETH
384832272026-01-24 5:13:0647 hrs ago1769231586
0x51b20689...36f91c90C
0.000005 ETH
384832272026-01-24 5:13:0647 hrs ago1769231586
0x51b20689...36f91c90C
0.000005 ETH
384831032026-01-24 5:11:0247 hrs ago1769231462
0x51b20689...36f91c90C
0.000005 ETH
384831032026-01-24 5:11:0247 hrs ago1769231462
0x51b20689...36f91c90C
0.000005 ETH
384313172026-01-23 14:47:562 days ago1769179676
0x51b20689...36f91c90C
0.000005 ETH
View All Internal Transactions

Cross-Chain Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
AssetController

Compiler Version
v0.8.19+commit.7dd6d404

Optimization Enabled:
Yes with 200 runs

Other Settings:
london EvmVersion
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.19;

import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import {Context} from "@openzeppelin/contracts/utils/Context.sol";
import {BaseAssetBridge} from "./BaseAssetBridge.sol";
import {IBaseAdapter} from "./adapters/interfaces/IBaseAdapter.sol";
import {IController} from "./interfaces/IController.sol";
import {IFeeCollector} from "./interfaces/IFeeCollector.sol";
import {SafeERC20, IERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

/**
 * @title AssetController
 * @notice This contract is responsible for managing the minting and burning of a specified token across different chains, using a single or multiple bridge adapters.
 */
contract AssetController is Context, BaseAssetBridge, ReentrancyGuard, IController {
    using SafeERC20 for IERC20;

    /// @notice Event emitted when the token unwrapping setting is changed.
    event AllowTokenUnwrappingSet(bool allowUnwrapping);

    /// @notice Event emitted when an asset mint message is sent to another chain.
    /// @param transferId The unique identifier of the transfer.
    /// @param destChainId The destination chain ID.
    /// @param threshold The number of bridges required to relay the asset.
    /// @param sender The address of the sender.
    /// @param recipient The address of the recipient.
    /// @param amount The amount of the asset.
    event TransferCreated(
        bytes32 indexed transferId,
        uint256 indexed destChainId,
        uint256 threshold,
        address indexed sender,
        address recipient,
        uint256 amount,
        bool unwrap
    );

    /// @notice Event emitted when a transfer can now be executed.
    /// @param transferId The unique identifier of the transfer.
    event TransferExecutable(bytes32 indexed transferId);

    /// @notice Event emitted when an asset is minted on the current chain.
    /// @param transferId The unique identifier of the transfer.
    event TransferExecuted(bytes32 indexed transferId);

    /// @notice Event emitted when transfers to a specific chain are paused or unpaused.
    event TransfersPausedToChain(uint256 indexed chainId, bool paused);

    /// @notice Event emitted when a transfer is resent.
    /// @param transferId The unique identifier of the transfer.
    event TransferResent(bytes32 indexed transferId);

    /// @notice Event emitted when an asset mint message is sent to another chain via a bridgeAdapter.
    /// @param transferId The unique identifier of the transfer.
    /// @param bridgeAdapter The address of the bridge adapter.
    event TransferRelayed(bytes32 indexed transferId, address bridgeAdapter);

    /// @notice Event emitted when an asset is received from another chain.
    /// @param transferId The unique identifier of the transfer.
    /// @param originChainId The chain id of the origin chain.
    /// @param bridgeAdapter The address of the bridge adapter that sent the message.
    event TransferReceived(bytes32 indexed transferId, uint256 originChainId, address bridgeAdapter);

    /// @notice Event emitted when the minimum number of bridges required to relay an asset for multi-bridge transfers is set.
    /// @param minBridges The minimum number of bridges required.
    event MinBridgesSet(uint256 minBridges);

    /// @notice Event emitted when bridge adapters that can be used for multi-bridge transfers, bypassing the limits have been set
    /// @param adapter The address of the bridge adapter.
    /// @param enabled The status of the adapter.
    event MultiBridgeAdapterSet(address indexed adapter, bool enabled);

    /// @notice Event emitted when the controller address for a chain is set.
    /// @param controller The address of the controller.
    /// @param chainId The chain ID.
    event ControllerForChainSet(address indexed controller, uint256 chainId);

    /// @notice Error thrown when an asset controller is not deployed/supported on the other chain
    error Controller_Chain_Not_Supported();

    /// @notice Error thrown when a multibridge adapter specified is not whitelisted
    error Controller_AdapterNotSupported();

    /// @notice Error thrown when a transfer is not executable
    error Controller_TransferNotExecutable();

    /// @notice Error thrown when the transferId is not recognized
    error Controller_UnknownTransfer();

    /// @notice Error thrown when multi-bridge transfers are disabled
    error Controller_MultiBridgeTransfersDisabled();

    /// @notice Error thrown when the threshold is not met for execution
    error Controller_ThresholdNotMet();

    /// @notice Error thrown when the msg.value and the sum of fees[] don't match
    error Controller_FeesSumMismatch();

    /// @notice Error thrown when the adapter is a duplicate in the adapters array
    error Controller_DuplicateAdapter();

    /// @notice Error thrown when the ether transfer fails
    error Controller_EtherTransferFailed();

    /// @notice Error thrown when the amount passed is zero
    error Controller_ZeroAmount();

    /// @notice Error thrown when an adapter resends a transfer that has already been delivered
    error Controller_TransferResentByAadapter();

    /// @notice Error thrown when transfers to the destination chain are paused
    error Controller_TransfersPausedToDestination();

    /// @notice Error thrown when the mint function call on the token fails
    error Controller_TokenMintFailed();

    /// @notice Error thrown when the burn function call on the token fails
    error Controller_TokenBurnFailed();

    /// @notice Struct representing a bridged asset.
    /// @dev This struct holds the details of a message to be relayed to another chain.
    struct Transfer {
        address recipient;
        uint256 amount;
        bool unwrap;
        uint256 threshold;
        bytes32 transferId;
    }

    /// @notice Struct representing a received transfer.
    /// @dev This struct holds the details of a message received from another chain.
    struct ReceivedTransfer {
        address recipient;
        uint256 amount;
        bool unwrap;
        uint256 receivedSoFar;
        uint256 threshold;
        uint256 originChainId;
        bool executed;
    }

    /// @dev The fee collector contract address.
    IFeeCollector public immutable feeCollector;

    /// @dev The local token address that is being bridged.
    address public token;

    /// @dev Allow XERC20 incoming token transfers to be unwrapped into the native token using the lockbox.
    bool public allowTokenUnwrapping;

    /// @notice The function selector for the burn(uint256) function on the token.
    bytes4 public constant BURN_SELECTOR_SINGLE = bytes4(keccak256(bytes("burn(uint256)"))); // 0x42966c68

    /// @notice The function selector for the mint function on the token.
    bytes4 public immutable MINT_SELECTOR;

    /// @notice The function selector for the burn function on the token.
    bytes4 public immutable BURN_SELECTOR;

    /// @dev The minimum number of bridges required to relay an asset for multi-bridge transfers.
    uint256 public minBridges;

    /// @notice Nonce used in transferId calculation, increments after each calculation.
    uint256 public nonce;

    /// @dev Mapping of chain IDs to their respective controller addresses. This contract knows all the controllers in supported chains.
    mapping(uint256 => address) private _controllerForChain;

    /// @dev Mapping of whitelisted bridge adapters that can be used for multi-bridge transfers. Used for both sending and receiving messages.
    mapping(address => bool) public multiBridgeAdapters;

    /// @dev Indicates whether transfers to a given chain ID are currently paused
    mapping(uint256 => bool) public transfersPausedTo;

    /// @dev Mapping of transfer id to received messages
    mapping(bytes32 => ReceivedTransfer) public receivedTransfers;

    /// @dev Mapping of transfers identified by their transfer ID.
    mapping(bytes32 => Transfer) public relayedTransfers;

    /// @dev Mapping of transfer ID to destination chain ID.
    mapping(bytes32 => uint256) public destChainForMessage;

    /// @dev Mapping of transfer ID to the bridge adapter that has delivered the transfer.
    mapping(bytes32 => mapping(address => bool)) public deliveredBy;

    /* ========== CONSTRUCTOR ========== */

    /**
     * @notice Initializes the contract with the given parameters.
     * @notice To configure multibridge limits, use the zero address as a bridge in `_bridges` and set the limits accordingly.
     * @param _addresses An array with four elements, containing the token address, the user that gets DEFAULT_ADMIN_ROLE and PAUSE_ROLE, the user getting only PAUSE_ROLE,
     *          the fee collector contract, the controller address in other chains for the given chain IDs (if deployed with create3).
     * @param _duration The duration it takes for the limits to fully replenish.
     * @param _minBridges The minimum number of bridges required to relay an asset for multi-bridge transfers. Setting to 0 will disable multi-bridge transfers.
     * @param _multiBridgeAdapters The addresses of the initial bridge adapters that can be used for multi-bridge transfers, bypassing the limits.
     * @param _chainId The list of chain IDs to set the controller addresses for.
     * @param _bridges The list of bridge adapter addresses that have limits set for minting and burning.
     * @param _mintingLimits The list of minting limits for the bridge adapters. It must correspond to the mint() function of the token, otherwise tokens cannot be minted
     * @param _burningLimits The list of burning limits for the bridge adapters. It must correspond to the burn() function of the token, otherwise tokens cannot be burned
     * @param _selectors Bytes4 array of mint and burn function selectors.
     */
    constructor(
        address[5] memory _addresses, //token, initialOwner, pauser, feeCollector, controllerAddress
        uint256 _duration,
        uint256 _minBridges,
        address[] memory _multiBridgeAdapters,
        uint256[] memory _chainId,
        address[] memory _bridges,
        uint256[] memory _mintingLimits,
        uint256[] memory _burningLimits,
        bytes4[2] memory _selectors
    ) BaseAssetBridge(_addresses[1], _addresses[2], _duration, _bridges, _mintingLimits, _burningLimits) {
        if ((_addresses[0] == address(0)) || (_addresses[3] == address(0))) revert Controller_Invalid_Params();
        token = _addresses[0];
        feeCollector = IFeeCollector(_addresses[3]);
        minBridges = _minBridges;
        emit MinBridgesSet(_minBridges);
        if (_multiBridgeAdapters.length > 0) {
            for (uint256 i = 0; i < _multiBridgeAdapters.length; i++) {
                multiBridgeAdapters[_multiBridgeAdapters[i]] = true;
                emit MultiBridgeAdapterSet(_multiBridgeAdapters[i], true);
            }
        }

        if (_addresses[4] != address(0)) {
            for (uint256 i = 0; i < _chainId.length; i++) {
                _controllerForChain[_chainId[i]] = _addresses[4];
                emit ControllerForChainSet(_addresses[4], _chainId[i]);
            }
        }
        allowTokenUnwrapping = false;
        emit AllowTokenUnwrappingSet(false);

        MINT_SELECTOR = _selectors[0];
        BURN_SELECTOR = _selectors[1];
    }

    /* ========== PUBLIC ========== */

    /**
     * @notice Sends a message to another chain via a bridgeAdapter to mint the asset.
     * @dev msg.value should contain the bridge adapter fee
     * @param recipient The address of the recipient. Could be the same as msg.sender.
     * @param amount The amount of the asset to mint.
     * @param unwrap Applicable only to XERC20 transfers. Used to unwrap the native asset using the lockbox on destination, if enabled on the destination.
     * @param destChainId The destination chain ID.
     * @param bridgeAdapter The address of the bridge adapter.
     * @param bridgeOptions Additional params to be used by the adapter.
     */
    function transferTo(
        address recipient,
        uint256 amount,
        bool unwrap,
        uint256 destChainId,
        address bridgeAdapter,
        bytes memory bridgeOptions
    ) public payable nonReentrant whenNotPaused {
        if (amount == 0) revert Controller_ZeroAmount();
        if (burningCurrentLimitOf(bridgeAdapter) < amount) revert Controller_NotHighEnoughLimits();
        _useBurnerLimits(bridgeAdapter, amount);
        _burn(_msgSender(), amount);

        if (recipient == address(0)) revert Controller_Invalid_Params();
        if (getControllerForChain(destChainId) == address(0)) revert Controller_Chain_Not_Supported();
        if (transfersPausedTo[destChainId]) revert Controller_TransfersPausedToDestination();
        bytes32 transferId = calculateTransferId(destChainId);
        // Increment nonce used to create transfer id
        nonce++;

        // Store transfer data
        destChainForMessage[transferId] = destChainId;
        Transfer memory transfer = Transfer(recipient, amount, unwrap, 1, transferId);
        relayedTransfers[transferId] = transfer;

        IBaseAdapter(bridgeAdapter).relayMessage{value: msg.value}(
            destChainId,
            getControllerForChain(destChainId),
            bridgeOptions,
            abi.encode(transfer)
        );
        emit TransferCreated(transferId, destChainId, 1, _msgSender(), recipient, amount, unwrap);
        emit TransferRelayed(transferId, bridgeAdapter);
    }

    /**
     * @notice Resends a single-bridge transfer message to another chain via a bridgeAdapter
     * @notice Msg.sender will receive any refunds from excess fees paid by the bridge, if the bridge supports it.
     * @dev msg.value should contain the bridge adapter fee
     * @param transferId The unique identifier of the transfer.
     * @param adapter The address of the bridge adapter.
     * @param options Additional params to be used by the adapter.
     */
    function resendTransfer(bytes32 transferId, address adapter, bytes memory options) public payable nonReentrant whenNotPaused {
        uint256 destChainId = destChainForMessage[transferId];
        if (destChainId == 0) revert Controller_UnknownTransfer();
        Transfer memory transfer = relayedTransfers[transferId];
        if (transfer.threshold == 1) {
            uint256 _currentLimit = burningCurrentLimitOf(adapter);
            if (_currentLimit < transfer.amount) revert Controller_NotHighEnoughLimits();
            // Resend doesn't consume burn limits, since the asset is already burned, but it checks if the bridge adapter is enabled

            IBaseAdapter(adapter).relayMessage{value: msg.value}(destChainId, getControllerForChain(destChainId), options, abi.encode(transfer));
            emit TransferResent(transferId);
            emit TransferRelayed(transferId, adapter);
        } else {
            revert Controller_Invalid_Params();
        }
    }

    /**
     * @notice Sends a message to another chain via multiple bridgeAdapter to mint the asset, bypassing the individual bridge limits.
     * @notice This function uses instead higher limits, since execution goes through a minimum number of bridges.
     * @notice Msg.sender will receive any refunds from excess fees paid by the bridge, if the bridge supports it.
     * @dev Token allowance must be given before calling this function, which should include the multi-bridge fee, if any.
     * @param recipient The address of the recipient. Could be the same as msg.sender.
     * @param amount The amount of the asset to mint.
     * @param unwrap Applicable only to XERC20 transfers. Used to unwrap the native asset using the lockbox on destination, if enabled on the destination.
     * @param destChainId The destination chain ID.
     * @param adapters The addresses of the bridge adapters.
     * @param fees The fees to be paid to the bridge adapters.
     * @param options Additional params to be used by the adapter.
     */
    function transferTo(
        address recipient,
        uint256 amount,
        bool unwrap,
        uint256 destChainId,
        address[] memory adapters,
        uint256[] memory fees,
        bytes[] memory options
    ) public payable nonReentrant whenNotPaused {
        if (amount == 0) revert Controller_ZeroAmount();
        // Fee collection for multi-bridge transfers
        uint256 fee = feeCollector.quote(amount);
        if (fee > 0) {
            IERC20(token).safeTransferFrom(_msgSender(), address(this), fee);
            IERC20(token).safeApprove(address(feeCollector), fee);
            feeCollector.collect(token, fee);
        }
        _burn(_msgSender(), amount);

        uint256 _currentLimit = burningCurrentLimitOf(address(0));
        if (_currentLimit < amount) revert Controller_NotHighEnoughLimits();
        _useBurnerLimits(address(0), amount);

        _checkUniqueness(adapters);

        // Revert if threshold is higher than the number of adapters that will execute the message
        if (adapters.length < minBridges) revert Controller_Invalid_Params();
        if (recipient == address(0)) revert Controller_Invalid_Params();
        if (minBridges == 0) revert Controller_MultiBridgeTransfersDisabled();
        if (getControllerForChain(destChainId) == address(0)) revert Controller_Chain_Not_Supported();
        if (transfersPausedTo[destChainId]) revert Controller_TransfersPausedToDestination();
        // Create transfer id
        bytes32 transferId = calculateTransferId(destChainId);
        // Increment nonce used to create transfer id
        nonce++;
        Transfer memory transfer = Transfer(recipient, amount, unwrap, minBridges, transferId);

        // Store transfer data
        destChainForMessage[transferId] = destChainId;
        relayedTransfers[transferId] = transfer;

        _relayTransfer(transfer, destChainId, adapters, fees, options, msg.value);
        emit TransferCreated(transferId, destChainId, minBridges, _msgSender(), recipient, amount, unwrap);
    }

    /**
     * @notice Resends a multi-bridge transfer message to another chain via one or more multibridge whitelested bridge Adapters
     * @dev msg.value should contain the total bridge adapter fees
     * @param transferId The unique identifier of the transfer.
     * @param adapters The addresses of the bridge adapters.
     * @param fees The fees to be paid to the bridge adapters.
     * @param options Additional params to be used by the adapter.
     */
    function resendTransfer(
        bytes32 transferId,
        address[] memory adapters,
        uint256[] memory fees,
        bytes[] memory options
    ) public payable nonReentrant whenNotPaused {
        uint256 destChainId = destChainForMessage[transferId];
        if (destChainId == 0) revert Controller_UnknownTransfer();
        if (minBridges == 0) revert Controller_MultiBridgeTransfersDisabled();
        _checkUniqueness(adapters);
        Transfer memory transfer = relayedTransfers[transferId];
        // Resend doesn't uses the burn limits, since the asset is already burned and limits are global for all whitelisted multibridge adapters
        if (transfer.threshold > 1) {
            if (adapters.length != fees.length) revert Controller_Invalid_Params();

            _relayTransfer(transfer, destChainId, adapters, fees, options, msg.value);
            emit TransferResent(transferId);
        } else {
            revert Controller_Invalid_Params();
        }
    }

    /**
     * @notice Registers a received message.
     * @dev Can be called by an adapter contract only
     * @param receivedMsg The received message data in bytes.
     * @param originChain The origin chain ID.
     * @param originSender The address of the origin sender. (controller in origin chain)
     */
    function receiveMessage(bytes calldata receivedMsg, uint256 originChain, address originSender) public override nonReentrant whenNotPaused {
        // OriginSender must be a controller on another chain
        if (getControllerForChain(originChain) != originSender) revert Controller_Invalid_Params();

        // Decode message
        Transfer memory transfer = abi.decode(receivedMsg, (Transfer));

        if (transfer.threshold == 1) {
            // Instant transfer using the bridge limits
            // Check that transfer hasn't been replayed
            if (receivedTransfers[transfer.transferId].amount != 0) revert Controller_TransferNotExecutable();
            receivedTransfers[transfer.transferId] = ReceivedTransfer({
                recipient: transfer.recipient,
                amount: transfer.amount,
                unwrap: transfer.unwrap,
                receivedSoFar: 1,
                threshold: 1,
                originChainId: originChain,
                executed: true
            });

            // Get limit of bridge
            uint256 _currentLimit = mintingCurrentLimitOf(msg.sender);
            if (_currentLimit < transfer.amount) revert Controller_NotHighEnoughLimits();
            _useMinterLimits(msg.sender, transfer.amount);

            if (transfer.unwrap && allowTokenUnwrapping) {
                _unwrapAndMint(transfer.recipient, transfer.amount);
            } else {
                _mint(transfer.recipient, transfer.amount);
            }
            emit TransferExecuted(transfer.transferId);
        } else {
            // Msg.sender needs to be a multibridge adapter
            if (!multiBridgeAdapters[msg.sender]) revert Controller_AdapterNotSupported();
            if (deliveredBy[transfer.transferId][msg.sender] == true) revert Controller_TransferResentByAadapter();
            deliveredBy[transfer.transferId][msg.sender] = true;

            ReceivedTransfer memory receivedTransfer = receivedTransfers[transfer.transferId];
            // Multi-bridge transfer
            if (receivedTransfer.receivedSoFar == 0) {
                receivedTransfer = ReceivedTransfer({
                    recipient: transfer.recipient,
                    amount: transfer.amount,
                    unwrap: transfer.unwrap,
                    receivedSoFar: 1,
                    threshold: transfer.threshold,
                    originChainId: originChain,
                    executed: false
                });
            } else {
                receivedTransfer.receivedSoFar++;
            }
            // Check if the transfer can be executed
            if (receivedTransfer.receivedSoFar >= receivedTransfer.threshold) {
                emit TransferExecutable(transfer.transferId);
            }
            receivedTransfers[transfer.transferId] = receivedTransfer;
        }
        emit TransferReceived(transfer.transferId, originChain, msg.sender);
    }

    /**
     * @notice Executes a received multibridge transfer. Anyone can execute a transfer
     * @param transferId The unique identifier of the transfer.
     */
    function execute(bytes32 transferId) public nonReentrant whenNotPaused {
        ReceivedTransfer storage transfer = receivedTransfers[transferId];
        if (transfer.amount == 0) revert Controller_UnknownTransfer();
        if (transfer.executed) revert Controller_TransferNotExecutable();
        if (transfer.receivedSoFar < transfer.threshold) revert Controller_ThresholdNotMet();
        uint256 _currentLimit = mintingCurrentLimitOf(address(0));
        if (_currentLimit < transfer.amount) revert Controller_NotHighEnoughLimits();
        _useMinterLimits(address(0), transfer.amount);
        transfer.executed = true;

        if (transfer.unwrap && allowTokenUnwrapping) {
            _unwrapAndMint(transfer.recipient, transfer.amount);
        } else {
            _mint(transfer.recipient, transfer.amount);
        }

        emit TransferExecuted(transferId);
    }

    /* ========== VIEW ========== */

    /**
     * @notice Returns the controller address for a given chain ID.
     * @param chainId The chain ID.
     * @return The controller address.
     */
    function getControllerForChain(uint256 chainId) public view override returns (address) {
        return _controllerForChain[chainId];
    }

    /**
     * @notice Calculates the transfer ID based on the provided parameters.
     * @param destChainId The destination chain ID.
     * @return The calculated transfer ID.
     */
    function calculateTransferId(uint256 destChainId) public view returns (bytes32) {
        return keccak256(abi.encode(destChainId, block.chainid, nonce));
    }

    /* ========== ADMIN ========== */

    function setTokenUnwrapping(bool _allowUnwrapping) public virtual onlyRole(DEFAULT_ADMIN_ROLE) {
        allowTokenUnwrapping = _allowUnwrapping;
        emit AllowTokenUnwrappingSet(_allowUnwrapping);
    }

    /**
     * @notice Sets the controller addresses for the given chain IDs.
     * @dev This also serves -in a way- as the token mappers for the token according to xERC20
     * @param chainId The list of chain IDs.
     * @param controller The list of controller addresses.
     */
    function setControllerForChain(uint256[] memory chainId, address[] memory controller) public onlyRole(DEFAULT_ADMIN_ROLE) {
        _setControllerForChain(chainId, controller);
    }

    /**
     * @notice Pauses or unpauses the initiation of new transfers to a specific chain.
     * @dev Only the owner can call this function.
     * @param _chainId The chain ID to pause or unpause transfers to.
     * @param _pause True to pause transfers, false to unpause.
     */
    function pauseTransfersToChain(uint256 _chainId, bool _pause) public onlyRole(DEFAULT_ADMIN_ROLE) {
        transfersPausedTo[_chainId] = _pause;
        emit TransfersPausedToChain(_chainId, _pause);
    }

    /**
     * @notice Sets the minimum number of bridges required to relay an asset for multi-bridge transfers.
     * @dev Setting to 0 will disable multi-bridge transfers.
     * @param _minBridges The minimum number of bridges required.
     */
    function setMinBridges(uint256 _minBridges) public onlyRole(DEFAULT_ADMIN_ROLE) {
        minBridges = _minBridges;
        emit MinBridgesSet(_minBridges);
    }

    /**
     * @notice Adds a bridge adapter to the whitelist for multibridge transfers.
     * @dev adapter and enabledmust have the same length.
     * @param adapter An array of adapter addresses of the bridge adapters.
     * @param enabled An array of the status of the adapters. True to enable, false to disable.
     */
    function setMultiBridgeAdapters(address[] memory adapter, bool[] memory enabled) public onlyRole(DEFAULT_ADMIN_ROLE) {
        if (adapter.length != enabled.length) revert Controller_Invalid_Params();
        for (uint256 i = 0; i < adapter.length; i++) {
            multiBridgeAdapters[adapter[i]] = enabled[i];
            emit MultiBridgeAdapterSet(adapter[i], enabled[i]);
        }
    }

    /**
     *@notice Withdraws the contract balance to the recipient address.
     * @dev Only the owner can call this function.
     * @param recipient The address to which the contract balance will be transferred.
     */
    function withdraw(address payable recipient) public onlyRole(DEFAULT_ADMIN_ROLE) {
        if (recipient == address(0)) revert Controller_Invalid_Params();

        (bool success, ) = recipient.call{value: address(this).balance}("");
        if (!success) revert Controller_EtherTransferFailed();
    }

    /* ========== INTERNAL ========== */

    /**
     * @notice Mints the asset to the recipient address, using the MINT_SELECTOR.
     * @param recipient The address of the recipient.
     * @param amount The amount of the asset to mint.
     */
    function _mint(address recipient, uint256 amount) internal virtual {
        (bool success, ) = token.call(abi.encodeWithSelector(MINT_SELECTOR, recipient, amount));
        if (!success) revert Controller_TokenMintFailed();
    }

    /**
     * @notice Burns the asset from the account address, using the burnSelector.
     * @param account The address of the account to burn from.
     * @param amount The amount of the asset to burn.
     */
    function _burn(address account, uint256 amount) internal virtual {
        uint256 balance = IERC20(token).balanceOf(account);
        bool success;
        if (BURN_SELECTOR == BURN_SELECTOR_SINGLE) {
            // burn(uint256) implementations expect msg.sender to hold the tokens
            IERC20(token).safeTransferFrom(account, address(this), amount);
            (success, ) = token.call(abi.encodeWithSelector(BURN_SELECTOR, amount));
        } else {
            (success, ) = token.call(abi.encodeWithSelector(BURN_SELECTOR, account, amount));
        }
        uint256 newBalance = IERC20(token).balanceOf(account);
        if (!success || (newBalance != balance - amount)) revert Controller_TokenBurnFailed();
    }

    /**
     * @notice Relays a message to another chain.
     * @notice Msg.sender will receive any refunds from excess fees paid by the bridge, if the bridge supports it.
     * @param transfer The Transfer struct with the transfer data.
     * @param destChainId The destination chain ID.
     * @param adapters The list of adapter addresses.
     * @param fees The list of fees for each adapter.
     * @param totalFees The msg.value passed to the function that should cover the sum of all the fees. Will revert if sum of fees is not equal to totalFees.
     */
    function _relayTransfer(
        Transfer memory transfer,
        uint256 destChainId,
        address[] memory adapters,
        uint256[] memory fees,
        bytes[] memory options,
        uint256 totalFees
    ) internal {
        if ((adapters.length != fees.length) || (adapters.length != options.length)) revert Controller_Invalid_Params();
        uint256 fee;
        for (uint256 i = 0; i < adapters.length; i++) {
            // Check that provided bridges are whitelisted
            if (multiBridgeAdapters[adapters[i]] == false) revert Controller_AdapterNotSupported();
            IBaseAdapter(adapters[i]).relayMessage{value: fees[i]}(destChainId, getControllerForChain(destChainId), options[i], abi.encode(transfer));
            emit TransferRelayed(transfer.transferId, adapters[i]);
            fee += fees[i];
        }
        if (fee != totalFees) revert Controller_FeesSumMismatch();
    }

    /**
     * @notice Sets the controller addresses for the given chain IDs.
     * @param chainId The list of chain IDs.
     * @param controller The list of controller addresses.
     */
    function _setControllerForChain(uint256[] memory chainId, address[] memory controller) internal {
        if (chainId.length != controller.length) revert Controller_Invalid_Params();
        for (uint256 i = 0; i < chainId.length; i++) {
            _controllerForChain[chainId[i]] = controller[i];
            emit ControllerForChainSet(controller[i], chainId[i]);
        }
    }

    /**
     * @notice If there is a XERC20Lockbox set in the token, unwrap the asset and send it to the recipient. If not, mint the tokens directly.
     * @dev Checks that all external calls will succeed, otherwise it returns the tokens to the user.
     * @param recipient The address of the recipient.
     * @param amount The amount of the asset to unwrap and mint.
     */
    function _unwrapAndMint(address recipient, uint256 amount) internal virtual {
        // Try to get the lockbox address from the token
        (bool success, bytes memory data) = token.call(abi.encodeWithSignature("lockbox()"));

        // Check if lockbox call was successful and returned data
        if (success && data.length != 0) {
            address lockbox = abi.decode(data, (address));

            // Only attempt unwrapping if lockbox exists
            if (lockbox != address(0)) {
                // Store initial balance to calculate exact amount after minting
                uint256 balanceBefore = IERC20(token).balanceOf(address(this));

                // Mint tokens to this contract
                _mint(address(this), amount);

                // Calculate exact amount received (accounts for potential taxes/fees)
                uint256 amountToUnwrap = IERC20(token).balanceOf(address(this)) - balanceBefore;

                // Get the underlying token address from lockbox
                (bool addrRetrSuccess, bytes memory underlying) = lockbox.call(abi.encodeWithSignature("ERC20()"));

                // Approve tokens for unwrapping
                (success, ) = token.call(abi.encodeWithSignature("approve(address,uint256)", lockbox, amountToUnwrap));

                // If approval failed or underlying token retrieval failed or allowance is insufficient
                if (!success || !addrRetrSuccess || IERC20(token).allowance(address(this), lockbox) < amountToUnwrap) {
                    // Transfer tokens directly to recipient without unwrapping
                    IERC20(token).safeTransfer(recipient, amountToUnwrap);
                    return;
                }

                // Decode underlying token address
                address underlyingToken = abi.decode(underlying, (address));

                // If underlying token is address(0), can't unwrap
                if (underlyingToken == address(0)) {
                    IERC20(token).safeTransfer(recipient, amountToUnwrap);
                    return;
                }

                // Try to withdraw/unwrap tokens
                (success, ) = lockbox.call(abi.encodeWithSignature("withdraw(uint256)", amountToUnwrap));

                // If withdrawal failed, send the wrapped tokens directly
                if (!success) {
                    IERC20(token).safeTransfer(recipient, amountToUnwrap);
                    return;
                }

                // Transfer unwrapped/underlying tokens to recipient
                IERC20(underlyingToken).safeTransfer(recipient, amountToUnwrap);
                return;
            }
        }

        // If lockbox doesn't exist or can't be used, mint tokens directly to recipient
        _mint(recipient, amount);
    }

    /// @dev Execution will revert if there is a duplicate adapter in the array
    function _checkUniqueness(address[] memory adapters) internal pure {
        uint256 length = adapters.length;
        for (uint256 i = 0; i < length - 1; i++) {
            for (uint256 j = i + 1; j < length; j++) {
                // Verify that the adapter is not a duplicate
                if (adapters[i] == adapters[j]) revert Controller_DuplicateAdapter();
            }
        }
    }

    ///@dev Fallback function to receive ether from bridge refunds
    receive() external payable {}
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/AccessControl.sol)

pragma solidity ^0.8.0;

import "./IAccessControl.sol";
import "../utils/Context.sol";
import "../utils/Strings.sol";
import "../utils/introspection/ERC165.sol";

/**
 * @dev Contract module that allows children to implement role-based access
 * control mechanisms. This is a lightweight version that doesn't allow enumerating role
 * members except through off-chain means by accessing the contract event logs. Some
 * applications may benefit from on-chain enumerability, for those cases see
 * {AccessControlEnumerable}.
 *
 * Roles are referred to by their `bytes32` identifier. These should be exposed
 * in the external API and be unique. The best way to achieve this is by
 * using `public constant` hash digests:
 *
 * ```solidity
 * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
 * ```
 *
 * Roles can be used to represent a set of permissions. To restrict access to a
 * function call, use {hasRole}:
 *
 * ```solidity
 * function foo() public {
 *     require(hasRole(MY_ROLE, msg.sender));
 *     ...
 * }
 * ```
 *
 * Roles can be granted and revoked dynamically via the {grantRole} and
 * {revokeRole} functions. Each role has an associated admin role, and only
 * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
 *
 * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
 * that only accounts with this role will be able to grant or revoke other
 * roles. More complex role relationships can be created by using
 * {_setRoleAdmin}.
 *
 * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
 * grant and revoke this role. Extra precautions should be taken to secure
 * accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}
 * to enforce additional security measures for this role.
 */
abstract contract AccessControl is Context, IAccessControl, ERC165 {
    struct RoleData {
        mapping(address => bool) members;
        bytes32 adminRole;
    }

    mapping(bytes32 => RoleData) private _roles;

    bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;

    /**
     * @dev Modifier that checks that an account has a specific role. Reverts
     * with a standardized message including the required role.
     *
     * The format of the revert reason is given by the following regular expression:
     *
     *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
     *
     * _Available since v4.1._
     */
    modifier onlyRole(bytes32 role) {
        _checkRole(role);
        _;
    }

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
    }

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) public view virtual override returns (bool) {
        return _roles[role].members[account];
    }

    /**
     * @dev Revert with a standard message if `_msgSender()` is missing `role`.
     * Overriding this function changes the behavior of the {onlyRole} modifier.
     *
     * Format of the revert message is described in {_checkRole}.
     *
     * _Available since v4.6._
     */
    function _checkRole(bytes32 role) internal view virtual {
        _checkRole(role, _msgSender());
    }

    /**
     * @dev Revert with a standard message if `account` is missing `role`.
     *
     * The format of the revert reason is given by the following regular expression:
     *
     *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
     */
    function _checkRole(bytes32 role, address account) internal view virtual {
        if (!hasRole(role, account)) {
            revert(
                string(
                    abi.encodePacked(
                        "AccessControl: account ",
                        Strings.toHexString(account),
                        " is missing role ",
                        Strings.toHexString(uint256(role), 32)
                    )
                )
            );
        }
    }

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) {
        return _roles[role].adminRole;
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     *
     * May emit a {RoleGranted} event.
     */
    function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
        _grantRole(role, account);
    }

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     *
     * May emit a {RoleRevoked} event.
     */
    function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
        _revokeRole(role, account);
    }

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been revoked `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `account`.
     *
     * May emit a {RoleRevoked} event.
     */
    function renounceRole(bytes32 role, address account) public virtual override {
        require(account == _msgSender(), "AccessControl: can only renounce roles for self");

        _revokeRole(role, account);
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event. Note that unlike {grantRole}, this function doesn't perform any
     * checks on the calling account.
     *
     * May emit a {RoleGranted} event.
     *
     * [WARNING]
     * ====
     * This function should only be called from the constructor when setting
     * up the initial roles for the system.
     *
     * Using this function in any other way is effectively circumventing the admin
     * system imposed by {AccessControl}.
     * ====
     *
     * NOTE: This function is deprecated in favor of {_grantRole}.
     */
    function _setupRole(bytes32 role, address account) internal virtual {
        _grantRole(role, account);
    }

    /**
     * @dev Sets `adminRole` as ``role``'s admin role.
     *
     * Emits a {RoleAdminChanged} event.
     */
    function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
        bytes32 previousAdminRole = getRoleAdmin(role);
        _roles[role].adminRole = adminRole;
        emit RoleAdminChanged(role, previousAdminRole, adminRole);
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * Internal function without access restriction.
     *
     * May emit a {RoleGranted} event.
     */
    function _grantRole(bytes32 role, address account) internal virtual {
        if (!hasRole(role, account)) {
            _roles[role].members[account] = true;
            emit RoleGranted(role, account, _msgSender());
        }
    }

    /**
     * @dev Revokes `role` from `account`.
     *
     * Internal function without access restriction.
     *
     * May emit a {RoleRevoked} event.
     */
    function _revokeRole(bytes32 role, address account) internal virtual {
        if (hasRole(role, account)) {
            _roles[role].members[account] = false;
            emit RoleRevoked(role, account, _msgSender());
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)

pragma solidity ^0.8.0;

/**
 * @dev External interface of AccessControl declared to support ERC165 detection.
 */
interface IAccessControl {
    /**
     * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
     *
     * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
     * {RoleAdminChanged} not being emitted signaling this.
     *
     * _Available since v3.1._
     */
    event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);

    /**
     * @dev Emitted when `account` is granted `role`.
     *
     * `sender` is the account that originated the contract call, an admin role
     * bearer except when using {AccessControl-_setupRole}.
     */
    event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Emitted when `account` is revoked `role`.
     *
     * `sender` is the account that originated the contract call:
     *   - if using `revokeRole`, it is the admin role bearer
     *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
     */
    event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) external view returns (bool);

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {AccessControl-_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) external view returns (bytes32);

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function grantRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function revokeRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been granted `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `account`.
     */
    function renounceRole(bytes32 role, address account) external;
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol)

pragma solidity ^0.8.0;

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

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

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

    bool private _paused;

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

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

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

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

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

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

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

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

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

pragma solidity ^0.8.0;

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

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

    uint256 private _status;

    constructor() {
        _status = _NOT_ENTERED;
    }

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

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

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

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

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

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

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 */
interface IERC20Permit {
    /**
     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
     * given ``owner``'s signed approval.
     *
     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
     * ordering also apply here.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
     * over the EIP712-formatted function arguments.
     * - the signature must use ``owner``'s current nonce (see {nonces}).
     *
     * For more information on the signature format, see the
     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
     * section].
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

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

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

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

pragma solidity ^0.8.0;

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

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

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

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

pragma solidity ^0.8.0;

import "../IERC20.sol";
import "../extensions/IERC20Permit.sol";
import "../../../utils/Address.sol";

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

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

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

    /**
     * @dev Deprecated. This function has issues similar to the ones found in
     * {IERC20-approve}, and its usage is discouraged.
     *
     * Whenever possible, use {safeIncreaseAllowance} and
     * {safeDecreaseAllowance} instead.
     */
    function safeApprove(IERC20 token, address spender, uint256 value) internal {
        // safeApprove should only be called when setting an initial allowance,
        // or when resetting it to zero. To increase and decrease it, use
        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
        require(
            (value == 0) || (token.allowance(address(this), spender) == 0),
            "SafeERC20: approve from non-zero to non-zero allowance"
        );
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
    }

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

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        unchecked {
            uint256 oldAllowance = token.allowance(address(this), spender);
            require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
        }
    }

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

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

    /**
     * @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
     * Revert on invalid signature.
     */
    function safePermit(
        IERC20Permit token,
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal {
        uint256 nonceBefore = token.nonces(owner);
        token.permit(owner, spender, value, deadline, v, r, s);
        uint256 nonceAfter = token.nonces(owner);
        require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
    }

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

        bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
        require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
    }

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

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

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

pragma solidity ^0.8.1;

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

        return account.code.length > 0;
    }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

pragma solidity ^0.8.0;

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

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

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)

pragma solidity ^0.8.0;

import "./IERC165.sol";

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
 * for the additional interface id that will be supported. For example:
 *
 * ```solidity
 * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
 *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
 * }
 * ```
 *
 * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
 */
abstract contract ERC165 is IERC165 {
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IERC165).interfaceId;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

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

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

pragma solidity ^0.8.0;

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

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

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

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

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

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.8.19 <=0.8.20;

interface IBaseAdapter {
    /// @notice Struct used by the adapter to relay messages
    struct BridgedMessage {
        bytes message;
        address originController;
        address destController;
    }

    /// @param destChainId The destination chain ID.
    /// @param destination The destination address.
    /// @param options Additional options to be used by the adapter.
    /// @param message The message data to be relayed.
    /// @return transferId The transfer ID of the relayed message.
    function relayMessage(
        uint256 destChainId,
        address destination,
        bytes memory options,
        bytes calldata message
    ) external payable returns (bytes32 transferId);

    /// @param chainId The chain ID to check.
    /// @return bool True if the chain ID is supported, false otherwise.
    function isChainIdSupported(uint256 chainId) external view returns (bool);
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.19;

import {Pausable} from "@openzeppelin/contracts/security/Pausable.sol";
import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol";

/**
 * @title BaseAssetBridge
 */
abstract contract BaseAssetBridge is AccessControl, Pausable {
    /**
     * @notice Error thrown when invalid parameters are provided.
     */
    error Controller_Invalid_Params();

    /**
     * @notice Error thrown when new limits are too high.
     */
    error Controller_LimitsTooHigh();

    /**
     * @notice Emits when a limit is set
     *
     * @param _mintingLimit The updated minting limit we are setting to the bridge
     * @param _burningLimit The updated burning limit we are setting to the bridge
     * @param _bridge The address of the bridge we are setting the limit too
     */
    event BridgeLimitsSet(uint256 _mintingLimit, uint256 _burningLimit, address indexed _bridge);

    /**
     * @notice Reverts when a user with too low of a limit tries to call mint/burn
     */
    error Controller_NotHighEnoughLimits();

    /**
     * @notice Contains the full minting and burning data for a particular bridge
     *
     * @param minterParams The minting parameters for the bridge
     * @param burnerParams The burning parameters for the bridge
     */
    struct Bridge {
        BridgeParameters minterParams;
        BridgeParameters burnerParams;
    }

    /**
     * @notice Contains the mint or burn parameters for a bridge
     *
     * @param timestamp The timestamp of the last mint/burn
     * @param ratePerSecond The rate per second of the bridge
     * @param maxLimit The max limit of the bridge
     * @param currentLimit The current limit of the bridge
     */
    struct BridgeParameters {
        uint256 timestamp;
        uint256 ratePerSecond;
        uint256 maxLimit;
        uint256 currentLimit;
    }

    /**
     * @notice The role that allows an address to pause the contract
     */
    bytes32 public constant PAUSE_ROLE = keccak256("PAUSE_ROLE");

    /**
     * @notice The duration it takes for the limits to fully replenish
     */
    uint256 public duration;

    /**
     * @notice Maps bridge address to bridge configurations
     */
    mapping(address => Bridge) public bridges;

    /**
     * @notice The constructor for the BaseAssetBridge
     *
     * @param _owner The user getting the DEFAULT_ADMIN_ROLE and PAUSE_ROLE.
     * @param _pauser The user getting the PAUSE_ROLE.
     * @param _duration The duration it takes for the limits to fully replenish.
     * @param _bridges The list of bridge adapter addresses that have limits set for minting and burning.
     * @param _mintingLimits The list of minting limits for the bridge adapters.
     * @param _burningLimits The list of burning limits for the bridge adapters.
     */
    constructor(
        address _owner,
        address _pauser,
        uint256 _duration,
        address[] memory _bridges,
        uint256[] memory _mintingLimits,
        uint256[] memory _burningLimits
    ) {
        _setupRole(DEFAULT_ADMIN_ROLE, _owner);
        _setupRole(PAUSE_ROLE, _owner);
        _grantRole(PAUSE_ROLE, _pauser);
        if (_duration == 0) revert Controller_Invalid_Params();
        duration = _duration;
        if ((_bridges.length != _mintingLimits.length) || (_bridges.length != _burningLimits.length)) revert Controller_Invalid_Params();
        if (_bridges.length > 0) {
            for (uint256 i = 0; i < _bridges.length; i++) {
                _setLimits(_bridges[i], _mintingLimits[i], _burningLimits[i]);
            }
        }
    }

    /**
     * @notice Updates the limits of any bridge
     * @dev Can only be called by the owner
     * @param _mintingLimit The updated minting limit we are setting to the bridge
     * @param _burningLimit The updated burning limit we are setting to the bridge
     * @param _bridge The address of the bridge we are setting the limits too
     */
    function setLimits(address _bridge, uint256 _mintingLimit, uint256 _burningLimit) external onlyRole(DEFAULT_ADMIN_ROLE) {
        _setLimits(_bridge, _mintingLimit, _burningLimit);
    }

    /**
     * @notice Returns the max limit of a bridge
     *
     * @param _bridge the bridge we are viewing the limits of
     * @return _limit The limit the bridge has
     */

    function mintingMaxLimitOf(address _bridge) public view returns (uint256 _limit) {
        _limit = bridges[_bridge].minterParams.maxLimit;
    }

    /**
     * @notice Returns the max limit of a bridge
     *
     * @param _bridge the bridge we are viewing the limits of
     * @return _limit The limit the bridge has
     */

    function burningMaxLimitOf(address _bridge) public view returns (uint256 _limit) {
        _limit = bridges[_bridge].burnerParams.maxLimit;
    }

    /**
     * @notice Returns the current limit of a bridge
     *
     * @param _bridge the bridge we are viewing the limits of
     * @return _limit The limit the bridge has
     */

    function mintingCurrentLimitOf(address _bridge) public view returns (uint256 _limit) {
        _limit = _getCurrentLimit(
            bridges[_bridge].minterParams.currentLimit,
            bridges[_bridge].minterParams.maxLimit,
            bridges[_bridge].minterParams.timestamp,
            bridges[_bridge].minterParams.ratePerSecond
        );
    }

    /**
     * @notice Returns the current limit of a bridge
     *
     * @param _bridge the bridge we are viewing the limits of
     * @return _limit The limit the bridge has
     */

    function burningCurrentLimitOf(address _bridge) public view returns (uint256 _limit) {
        _limit = _getCurrentLimit(
            bridges[_bridge].burnerParams.currentLimit,
            bridges[_bridge].burnerParams.maxLimit,
            bridges[_bridge].burnerParams.timestamp,
            bridges[_bridge].burnerParams.ratePerSecond
        );
    }

    /**
     * @notice Pauses the contract
     * @dev Only the owner can call this function.
     */
    function pause() external onlyRole(PAUSE_ROLE) {
        _pause();
    }

    /**
     * @notice Unpauses the contract
     * @dev Only the owner can call this function.
     */
    function unpause() external onlyRole(PAUSE_ROLE) {
        _unpause();
    }

    /**
     * @notice Updates the limits of any bridge
     * @param _mintingLimit The updated minting limit we are setting to the bridge
     * @param _burningLimit The updated burning limit we are setting to the bridge
     * @param _bridge The address of the bridge we are setting the limits too
     */
    function _setLimits(address _bridge, uint256 _mintingLimit, uint256 _burningLimit) internal {
        // Ensure new limits do not cause overflows
        if (_mintingLimit > (type(uint256).max / 2) || _burningLimit > (type(uint256).max / 2)) {
            revert Controller_LimitsTooHigh();
        }

        _changeMinterLimit(_bridge, _mintingLimit);
        _changeBurnerLimit(_bridge, _burningLimit);
        emit BridgeLimitsSet(_mintingLimit, _burningLimit, _bridge);
    }

    /**
     * @notice Uses the limit of any bridge
     * @param _bridge The address of the bridge who is being changed
     * @param _change The change in the limit
     */

    function _useMinterLimits(address _bridge, uint256 _change) internal {
        uint256 _currentLimit = mintingCurrentLimitOf(_bridge);
        bridges[_bridge].minterParams.timestamp = block.timestamp;
        bridges[_bridge].minterParams.currentLimit = _currentLimit - _change;
    }

    /**
     * @notice Uses the limit of any bridge
     * @param _bridge The address of the bridge who is being changed
     * @param _change The change in the limit
     */

    function _useBurnerLimits(address _bridge, uint256 _change) internal {
        uint256 _currentLimit = burningCurrentLimitOf(_bridge);
        bridges[_bridge].burnerParams.timestamp = block.timestamp;
        bridges[_bridge].burnerParams.currentLimit = _currentLimit - _change;
    }

    /**
     * @notice Updates the limit of any bridge
     * @dev Can only be called by the owner
     * @param _bridge The address of the bridge we are setting the limit too
     * @param _limit The updated limit we are setting to the bridge
     */

    function _changeMinterLimit(address _bridge, uint256 _limit) internal {
        uint256 _oldLimit = bridges[_bridge].minterParams.maxLimit;
        uint256 _currentLimit = mintingCurrentLimitOf(_bridge);
        bridges[_bridge].minterParams.maxLimit = _limit;

        bridges[_bridge].minterParams.currentLimit = _calculateNewCurrentLimit(_limit, _oldLimit, _currentLimit);

        bridges[_bridge].minterParams.ratePerSecond = _limit / duration;
        bridges[_bridge].minterParams.timestamp = block.timestamp;
    }

    function _changeBurnerLimit(address _bridge, uint256 _limit) internal {
        uint256 _oldLimit = bridges[_bridge].burnerParams.maxLimit;
        uint256 _currentLimit = burningCurrentLimitOf(_bridge);
        bridges[_bridge].burnerParams.maxLimit = _limit;

        bridges[_bridge].burnerParams.currentLimit = _calculateNewCurrentLimit(_limit, _oldLimit, _currentLimit);

        bridges[_bridge].burnerParams.ratePerSecond = _limit / duration;
        bridges[_bridge].burnerParams.timestamp = block.timestamp;
    }

    /**
     * @notice Updates the current limit
     *
     * @param _limit The new limit
     * @param _oldLimit The old limit
     * @param _currentLimit The current limit
     * @return _newCurrentLimit The new current limit
     */

    function _calculateNewCurrentLimit(uint256 _limit, uint256 _oldLimit, uint256 _currentLimit) internal pure returns (uint256 _newCurrentLimit) {
        uint256 _difference;

        if (_oldLimit > _limit) {
            _difference = _oldLimit - _limit;
            _newCurrentLimit = _currentLimit > _difference ? _currentLimit - _difference : 0;
        } else {
            _difference = _limit - _oldLimit;
            _newCurrentLimit = _currentLimit + _difference;
        }
    }

    /**
     * @notice Gets the current limit
     *
     * @param _currentLimit The current limit
     * @param _maxLimit The max limit
     * @param _timestamp The timestamp of the last update
     * @param _ratePerSecond The rate per second
     * @return _limit The current limit
     */

    function _getCurrentLimit(
        uint256 _currentLimit,
        uint256 _maxLimit,
        uint256 _timestamp,
        uint256 _ratePerSecond
    ) internal view returns (uint256 _limit) {
        _limit = _currentLimit;
        if (_limit == _maxLimit) {
            return _limit;
        } else if (_timestamp + duration <= block.timestamp) {
            _limit = _maxLimit;
        } else if (_timestamp + duration > block.timestamp) {
            uint256 _timePassed = block.timestamp - _timestamp;
            uint256 _calculatedLimit = _limit + (_timePassed * _ratePerSecond);
            _limit = _calculatedLimit > _maxLimit ? _maxLimit : _calculatedLimit;
        }
    }
}

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19 <=0.8.20;

interface IController {
    /**
     * @notice Registers a received message.
     * @param message The received message data in bytes.
     * @param originChain The origin chain ID.
     * @param originSender The address of the origin sender. (controller in origin chain)
     */
    function receiveMessage(bytes calldata message, uint256 originChain, address originSender) external;

    /**
     * @notice Returns the controller address for a given chain ID.
     * @param chainId The chain ID.
     * @return The controller address.
     */
    function getControllerForChain(uint256 chainId) external view returns (address);
}

// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;

interface IFeeCollector {
    function collect(address token, uint256 amount) external;

    function quote(uint256 amount) external view returns (uint256 fee);
}

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

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"address[5]","name":"_addresses","type":"address[5]"},{"internalType":"uint256","name":"_duration","type":"uint256"},{"internalType":"uint256","name":"_minBridges","type":"uint256"},{"internalType":"address[]","name":"_multiBridgeAdapters","type":"address[]"},{"internalType":"uint256[]","name":"_chainId","type":"uint256[]"},{"internalType":"address[]","name":"_bridges","type":"address[]"},{"internalType":"uint256[]","name":"_mintingLimits","type":"uint256[]"},{"internalType":"uint256[]","name":"_burningLimits","type":"uint256[]"},{"internalType":"bytes4[2]","name":"_selectors","type":"bytes4[2]"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"Controller_AdapterNotSupported","type":"error"},{"inputs":[],"name":"Controller_Chain_Not_Supported","type":"error"},{"inputs":[],"name":"Controller_DuplicateAdapter","type":"error"},{"inputs":[],"name":"Controller_EtherTransferFailed","type":"error"},{"inputs":[],"name":"Controller_FeesSumMismatch","type":"error"},{"inputs":[],"name":"Controller_Invalid_Params","type":"error"},{"inputs":[],"name":"Controller_LimitsTooHigh","type":"error"},{"inputs":[],"name":"Controller_MultiBridgeTransfersDisabled","type":"error"},{"inputs":[],"name":"Controller_NotHighEnoughLimits","type":"error"},{"inputs":[],"name":"Controller_ThresholdNotMet","type":"error"},{"inputs":[],"name":"Controller_TokenBurnFailed","type":"error"},{"inputs":[],"name":"Controller_TokenMintFailed","type":"error"},{"inputs":[],"name":"Controller_TransferNotExecutable","type":"error"},{"inputs":[],"name":"Controller_TransferResentByAadapter","type":"error"},{"inputs":[],"name":"Controller_TransfersPausedToDestination","type":"error"},{"inputs":[],"name":"Controller_UnknownTransfer","type":"error"},{"inputs":[],"name":"Controller_ZeroAmount","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"allowUnwrapping","type":"bool"}],"name":"AllowTokenUnwrappingSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_mintingLimit","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_burningLimit","type":"uint256"},{"indexed":true,"internalType":"address","name":"_bridge","type":"address"}],"name":"BridgeLimitsSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"controller","type":"address"},{"indexed":false,"internalType":"uint256","name":"chainId","type":"uint256"}],"name":"ControllerForChainSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"minBridges","type":"uint256"}],"name":"MinBridgesSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"adapter","type":"address"},{"indexed":false,"internalType":"bool","name":"enabled","type":"bool"}],"name":"MultiBridgeAdapterSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"transferId","type":"bytes32"},{"indexed":true,"internalType":"uint256","name":"destChainId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"threshold","type":"uint256"},{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"bool","name":"unwrap","type":"bool"}],"name":"TransferCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"transferId","type":"bytes32"}],"name":"TransferExecutable","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"transferId","type":"bytes32"}],"name":"TransferExecuted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"transferId","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"originChainId","type":"uint256"},{"indexed":false,"internalType":"address","name":"bridgeAdapter","type":"address"}],"name":"TransferReceived","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"transferId","type":"bytes32"},{"indexed":false,"internalType":"address","name":"bridgeAdapter","type":"address"}],"name":"TransferRelayed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"transferId","type":"bytes32"}],"name":"TransferResent","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"chainId","type":"uint256"},{"indexed":false,"internalType":"bool","name":"paused","type":"bool"}],"name":"TransfersPausedToChain","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"inputs":[],"name":"BURN_SELECTOR","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"BURN_SELECTOR_SINGLE","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MINT_SELECTOR","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PAUSE_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"allowTokenUnwrapping","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"bridges","outputs":[{"components":[{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"uint256","name":"ratePerSecond","type":"uint256"},{"internalType":"uint256","name":"maxLimit","type":"uint256"},{"internalType":"uint256","name":"currentLimit","type":"uint256"}],"internalType":"struct BaseAssetBridge.BridgeParameters","name":"minterParams","type":"tuple"},{"components":[{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"uint256","name":"ratePerSecond","type":"uint256"},{"internalType":"uint256","name":"maxLimit","type":"uint256"},{"internalType":"uint256","name":"currentLimit","type":"uint256"}],"internalType":"struct BaseAssetBridge.BridgeParameters","name":"burnerParams","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_bridge","type":"address"}],"name":"burningCurrentLimitOf","outputs":[{"internalType":"uint256","name":"_limit","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_bridge","type":"address"}],"name":"burningMaxLimitOf","outputs":[{"internalType":"uint256","name":"_limit","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"destChainId","type":"uint256"}],"name":"calculateTransferId","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"},{"internalType":"address","name":"","type":"address"}],"name":"deliveredBy","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"destChainForMessage","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"duration","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"transferId","type":"bytes32"}],"name":"execute","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"feeCollector","outputs":[{"internalType":"contract IFeeCollector","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"chainId","type":"uint256"}],"name":"getControllerForChain","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minBridges","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_bridge","type":"address"}],"name":"mintingCurrentLimitOf","outputs":[{"internalType":"uint256","name":"_limit","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_bridge","type":"address"}],"name":"mintingMaxLimitOf","outputs":[{"internalType":"uint256","name":"_limit","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"multiBridgeAdapters","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nonce","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_chainId","type":"uint256"},{"internalType":"bool","name":"_pause","type":"bool"}],"name":"pauseTransfersToChain","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"receivedMsg","type":"bytes"},{"internalType":"uint256","name":"originChain","type":"uint256"},{"internalType":"address","name":"originSender","type":"address"}],"name":"receiveMessage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"receivedTransfers","outputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bool","name":"unwrap","type":"bool"},{"internalType":"uint256","name":"receivedSoFar","type":"uint256"},{"internalType":"uint256","name":"threshold","type":"uint256"},{"internalType":"uint256","name":"originChainId","type":"uint256"},{"internalType":"bool","name":"executed","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"relayedTransfers","outputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bool","name":"unwrap","type":"bool"},{"internalType":"uint256","name":"threshold","type":"uint256"},{"internalType":"bytes32","name":"transferId","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"transferId","type":"bytes32"},{"internalType":"address","name":"adapter","type":"address"},{"internalType":"bytes","name":"options","type":"bytes"}],"name":"resendTransfer","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"transferId","type":"bytes32"},{"internalType":"address[]","name":"adapters","type":"address[]"},{"internalType":"uint256[]","name":"fees","type":"uint256[]"},{"internalType":"bytes[]","name":"options","type":"bytes[]"}],"name":"resendTransfer","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"chainId","type":"uint256[]"},{"internalType":"address[]","name":"controller","type":"address[]"}],"name":"setControllerForChain","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_bridge","type":"address"},{"internalType":"uint256","name":"_mintingLimit","type":"uint256"},{"internalType":"uint256","name":"_burningLimit","type":"uint256"}],"name":"setLimits","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_minBridges","type":"uint256"}],"name":"setMinBridges","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"adapter","type":"address[]"},{"internalType":"bool[]","name":"enabled","type":"bool[]"}],"name":"setMultiBridgeAdapters","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_allowUnwrapping","type":"bool"}],"name":"setTokenUnwrapping","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"token","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bool","name":"unwrap","type":"bool"},{"internalType":"uint256","name":"destChainId","type":"uint256"},{"internalType":"address[]","name":"adapters","type":"address[]"},{"internalType":"uint256[]","name":"fees","type":"uint256[]"},{"internalType":"bytes[]","name":"options","type":"bytes[]"}],"name":"transferTo","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bool","name":"unwrap","type":"bool"},{"internalType":"uint256","name":"destChainId","type":"uint256"},{"internalType":"address","name":"bridgeAdapter","type":"address"},{"internalType":"bytes","name":"bridgeOptions","type":"bytes"}],"name":"transferTo","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"transfersPausedTo","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address payable","name":"recipient","type":"address"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]

60e06040523480156200001157600080fd5b506040516200517738038062005177833981016040819052620000349162000b15565b602089015160408a01516001805460ff1916905589868686620000596000876200048b565b6200007460008051602062005157833981519152876200048b565b6200008f60008051602062005157833981519152866200049b565b83600003620000b157604051633f4e2f2960e21b815260040160405180910390fd5b600284905581518351141580620000ca57508051835114155b15620000e957604051633f4e2f2960e21b815260040160405180910390fd5b8251156200017c5760005b83518110156200017a576200016584828151811062000117576200011762000c37565b602002602001015184838151811062000134576200013462000c37565b602002602001015184848151811062000151576200015162000c37565b60200260200101516200052360201b60201c565b80620001718162000c63565b915050620000f4565b505b5050600160045550508a516001600160a01b0316159150819050620001ac575060608901516001600160a01b0316155b15620001cb57604051633f4e2f2960e21b815260040160405180910390fd5b8851600580546001600160a01b0319166001600160a01b0392831617905560608a01511660805260068790556040518781527fe125065bd51f3e795dd066e6f64e43cedd27dc38adf0062c7be9eaa209ae11479060200160405180910390a1855115620003135760005b865181101562000311576001600960008984815181106200025a576200025a62000c37565b60200260200101516001600160a01b03166001600160a01b0316815260200190815260200160002060006101000a81548160ff021916908315150217905550868181518110620002ae57620002ae62000c37565b60200260200101516001600160a01b03167fd3577f4d8ca054ac85e150e2a592220c54234d070e9f8ed0199869b7a17b25206001604051620002f4911515815260200190565b60405180910390a280620003088162000c63565b91505062000235565b505b60808901516001600160a01b031615620004205760005b85518110156200041e5789600460200201516008600088848151811062000355576200035562000c37565b6020026020010151815260200190815260200160002060006101000a8154816001600160a01b0302191690836001600160a01b0316021790555089600460058110620003a557620003a562000c37565b60200201516001600160a01b03167f3083b3b95a46df5c4a6cdd1fa242b76f968ed75bf364ec15de70e5b043f62831878381518110620003e957620003e962000c37565b60200260200101516040516200040191815260200190565b60405180910390a280620004158162000c63565b9150506200032a565b505b6005805460ff60a01b19169055604051600081527f2b777c07ea46054410835bc43625d87926c44d9a6b4ec49a7e3248d434cfe5b59060200160405180910390a180516001600160e01b031990811660a0526020909101511660c0525062000ce89650505050505050565b6200049782826200049b565b5050565b620004a78282620005cd565b62000497576000828152602081815260408083206001600160a01b03851684529091529020805460ff19166001179055620004df3390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b62000532600260001962000c7f565b8211806200054d57506200054a600260001962000c7f565b81115b156200056c576040516301e8cafb60e71b815260040160405180910390fd5b620005788383620005f8565b620005848382620006a3565b60408051838152602081018390526001600160a01b038516917f93f3bbfe8cfb354ec059175107653f49f6eb479a8622a7d83866ea015435c944910160405180910390a2505050565b6000828152602081815260408083206001600160a01b038516845290915290205460ff165b92915050565b6001600160a01b03821660009081526003602052604081206002015490620006208462000753565b6001600160a01b038516600090815260036020526040902060020184905590506200064d8383836200078a565b6001600160a01b0385166000908152600360208190526040909120015560025462000679908462000c7f565b6001600160a01b039094166000908152600360205260409020600181019490945550504290915550565b6001600160a01b03821660009081526003602052604081206006015490620006cb84620007ed565b6001600160a01b03851660009081526003602052604090206006018490559050620006f88383836200078a565b6001600160a01b03851660009081526003602052604090206007015560025462000723908462000c7f565b6001600160a01b039094166000908152600360205260409020600581019490945550504260049092019190915550565b6001600160a01b038116600090815260036020819052604082209081015460028201548254600190930154620005f2939062000821565b60008084841115620007c857620007a2858562000ca2565b9050808311620007b4576000620007c0565b620007c0818462000ca2565b9150620007e5565b620007d4848662000ca2565b9050620007e2818462000cb8565b91505b509392505050565b6001600160a01b0381166000908152600360205260408120600781015460068201546004830154600590930154620005f293905b83838114620008a35742600254846200083b919062000cb8565b1162000849575082620008a3565b42600254846200085a919062000cb8565b1115620008a35760006200086f844262000ca2565b905060006200087f848362000cce565b6200088b908462000cb8565b90508581116200089c57806200089e565b855b925050505b949350505050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b0381118282101715620008ec57620008ec620008ab565b604052919050565b80516001600160a01b03811681146200090c57600080fd5b919050565b600082601f8301126200092357600080fd5b60405160a081016001600160401b0381118282101715620009485762000948620008ab565b6040528060a08401858111156200095e57600080fd5b845b8181101562000983576200097481620008f4565b83526020928301920162000960565b509195945050505050565b60006001600160401b03821115620009aa57620009aa620008ab565b5060051b60200190565b600082601f830112620009c657600080fd5b81516020620009df620009d9836200098e565b620008c1565b82815260059290921b84018101918181019086841115620009ff57600080fd5b8286015b8481101562000a255762000a1781620008f4565b835291830191830162000a03565b509695505050505050565b600082601f83011262000a4257600080fd5b8151602062000a55620009d9836200098e565b82815260059290921b8401810191818101908684111562000a7557600080fd5b8286015b8481101562000a25578051835291830191830162000a79565b600082601f83011262000aa457600080fd5b604080519081016001600160401b038111828210171562000ac95762000ac9620008ab565b806040525080604084018581111562000ae157600080fd5b845b81811015620009835780516001600160e01b03198116811462000b065760008081fd5b83526020928301920162000ae3565b60008060008060008060008060006101c08a8c03121562000b3557600080fd5b62000b418b8b62000911565b60a08b015160c08c015160e08d0151929b5090995097506001600160401b038082111562000b6e57600080fd5b62000b7c8d838e01620009b4565b97506101008c015191508082111562000b9457600080fd5b62000ba28d838e0162000a30565b96506101208c015191508082111562000bba57600080fd5b62000bc88d838e01620009b4565b95506101408c015191508082111562000be057600080fd5b62000bee8d838e0162000a30565b94506101608c015191508082111562000c0657600080fd5b5062000c158c828d0162000a30565b92505062000c288b6101808c0162000a92565b90509295985092959850929598565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b60006001820162000c785762000c7862000c4d565b5060010190565b60008262000c9d57634e487b7160e01b600052601260045260246000fd5b500490565b81810381811115620005f257620005f262000c4d565b80820180821115620005f257620005f262000c4d565b8082028115828204841417620005f257620005f262000c4d565b60805160a05160c05161440e62000d496000396000818161071c01528181612459015281816124d001526125a20152600081816105b901526130260152600081816108e301528181610dde01528181610e830152610ed1015261440e6000f3fe6080604052600436106102805760003560e01c80637385e7fb1161014f578063c1eb7137116100c1578063d3a8b6311161007a578063d3a8b63114610a08578063d547741f14610a1b578063d6991b5b14610a3b578063e751f27114610a5b578063eab6995814610a7b578063fc0c546a14610a9157600080fd5b8063c1eb713714610898578063c415b95c146108d1578063cdf0366f1461091d578063ced67f0c1461093d578063d236ad68146109d5578063d30a41c8146109f557600080fd5b806391d148541161011357806391d148541461075f578063968638861461077f578063998955d31461082d578063a08d56541461084d578063a217fddf1461086d578063affed0e01461088257600080fd5b80637385e7fb1461066c5780637e0a8c6a1461069c5780638456cb59146106f557806387a131bb1461070a57806389a3b4fb1461073e57600080fd5b806336568abe116101f357806351cff8d9116101ac57806351cff8d914610587578063544fe464146105a75780635c975abb146105f4578063651fd2681461060c5780637256d8721461062c578063734afa2b1461064c57600080fd5b806336568abe146104d8578063365fc213146104f8578063389ed2671461051857806339c82de61461054c5780633f4ba83a1461055f5780634995c3e71461057457600080fd5b806315af3b671161024557806315af3b671461036d5780631f9bb777146103a8578063215e73e114610438578063248a9ca31461046857806327a65d3f146104985780632f2ff15d146104b857600080fd5b80625856cb1461028c57806301ffc9a7146102cc5780630c05f82c146102fc5780630fb5a6b41461033557806312ca7dbb1461034b57600080fd5b3661028757005b600080fd5b34801561029857600080fd5b506102b96102a7366004613956565b600d6020526000908152604090205481565b6040519081526020015b60405180910390f35b3480156102d857600080fd5b506102ec6102e736600461396f565b610ab1565b60405190151581526020016102c3565b34801561030857600080fd5b506102b96103173660046139ae565b6001600160a01b031660009081526003602052604090206002015490565b34801561034157600080fd5b506102b960025481565b34801561035757600080fd5b5061036b6103663660046139d9565b610ae8565b005b34801561037957600080fd5b506102ec610388366004613a09565b600e60209081526000928352604080842090915290825290205460ff1681565b3480156103b457600080fd5b506104006103c3366004613956565b600c60205260009081526040902080546001820154600283015460038401546004909401546001600160a01b0390931693919260ff909116919085565b604080516001600160a01b0390961686526020860194909452911515928401929092526060830191909152608082015260a0016102c3565b34801561044457600080fd5b506102ec6104533660046139ae565b60096020526000908152604090205460ff1681565b34801561047457600080fd5b506102b9610483366004613956565b60009081526020819052604090206001015490565b3480156104a457600080fd5b5061036b6104b3366004613b0b565b610b4c565b3480156104c457600080fd5b5061036b6104d3366004613a09565b610c83565b3480156104e457600080fd5b5061036b6104f3366004613a09565b610cad565b34801561050457600080fd5b5061036b610513366004613bce565b610d30565b34801561052457600080fd5b506102b97f139c2898040ef16910dc9f44dc697df79363da767d8bc92f2e310312b816e46d81565b61036b61055a366004613d34565b610d94565b34801561056b57600080fd5b5061036b6111bf565b61036b610582366004613df4565b6111f4565b34801561059357600080fd5b5061036b6105a23660046139ae565b6113fa565b3480156105b357600080fd5b506105db7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160e01b031990911681526020016102c3565b34801561060057600080fd5b5060015460ff166102ec565b34801561061857600080fd5b506102b96106273660046139ae565b6114a0565b34801561063857600080fd5b506102b9610647366004613956565b6114d5565b34801561065857600080fd5b5061036b610667366004613e4c565b61151c565b34801561067857600080fd5b506102ec610687366004613956565b600a6020526000908152604090205460ff1681565b3480156106a857600080fd5b5060408051808201909152600d81526c6275726e2875696e743235362960981b6020909101526105db7f42966c689b5afe9b9b3f8a7103b2a19980d59629bfd6a20a60972312ed41d83681565b34801561070157600080fd5b5061036b6119ca565b34801561071657600080fd5b506105db7f000000000000000000000000000000000000000000000000000000000000000081565b34801561074a57600080fd5b506005546102ec90600160a01b900460ff1681565b34801561076b57600080fd5b506102ec61077a366004613a09565b6119fc565b34801561078b57600080fd5b506107e661079a366004613956565b600b6020526000908152604090208054600182015460028301546003840154600485015460058601546006909601546001600160a01b0390951695939460ff9384169492939192911687565b604080516001600160a01b0390981688526020880196909652931515948601949094526060850191909152608084015260a0830191909152151560c082015260e0016102c3565b34801561083957600080fd5b506102b96108483660046139ae565b611a25565b34801561085957600080fd5b5061036b610868366004613ed6565b611a5c565b34801561087957600080fd5b506102b9600081565b34801561088e57600080fd5b506102b960075481565b3480156108a457600080fd5b506102b96108b33660046139ae565b6001600160a01b031660009081526003602052604090206006015490565b3480156108dd57600080fd5b506109057f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020016102c3565b34801561092957600080fd5b5061036b610938366004613f0b565b611a72565b34801561094957600080fd5b506109c76109583660046139ae565b60036020818152600092835260409283902083516080808201865282548252600183015482850152600283015482870152938201546060808301919091528551948501865260048301548552600583015493850193909352600682015494840194909452600701549082015282565b6040516102c3929190613f6e565b3480156109e157600080fd5b5061036b6109f0366004613956565b611a87565b61036b610a03366004613fba565b611ac7565b61036b610a16366004614040565b611db9565b348015610a2757600080fd5b5061036b610a36366004613a09565b611eee565b348015610a4757600080fd5b50610905610a56366004613956565b611f13565b348015610a6757600080fd5b5061036b610a76366004613956565b611f2e565b348015610a8757600080fd5b506102b960065481565b348015610a9d57600080fd5b50600554610905906001600160a01b031681565b60006001600160e01b03198216637965db0b60e01b1480610ae257506301ffc9a760e01b6001600160e01b03198316145b92915050565b6000610af3816120a3565b6000838152600a6020908152604091829020805460ff1916851515908117909155915191825284917f8670d56066b58d6b5eff6e9ff63fb7bc28c6d4ec9676c45cf2360314e9e9c24a91015b60405180910390a2505050565b6000610b57816120a3565b8151835114610b7957604051633f4e2f2960e21b815260040160405180910390fd5b60005b8351811015610c7d57828181518110610b9757610b976140d1565b602002602001015160096000868481518110610bb557610bb56140d1565b60200260200101516001600160a01b03166001600160a01b0316815260200190815260200160002060006101000a81548160ff021916908315150217905550838181518110610c0657610c066140d1565b60200260200101516001600160a01b03167fd3577f4d8ca054ac85e150e2a592220c54234d070e9f8ed0199869b7a17b2520848381518110610c4a57610c4a6140d1565b6020026020010151604051610c63911515815260200190565b60405180910390a280610c75816140fd565b915050610b7c565b50505050565b600082815260208190526040902060010154610c9e816120a3565b610ca883836120ad565b505050565b6001600160a01b0381163314610d225760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201526e103937b632b9903337b91039b2b63360891b60648201526084015b60405180910390fd5b610d2c8282612131565b5050565b6000610d3b816120a3565b60058054831515600160a01b0260ff60a01b199091161790556040517f2b777c07ea46054410835bc43625d87926c44d9a6b4ec49a7e3248d434cfe5b590610d8890841515815260200190565b60405180910390a15050565b610d9c612196565b610da46121ef565b85600003610dc557604051636c13c71b60e01b815260040160405180910390fd5b604051633b46f5db60e21b8152600481018790526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063ed1bd76c90602401602060405180830381865afa158015610e2d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e519190614116565b90508015610f3057610e71336005546001600160a01b0316903084612237565b600554610ea8906001600160a01b03167f0000000000000000000000000000000000000000000000000000000000000000836122a2565b600554604051631ae0d5eb60e21b81526001600160a01b039182166004820152602481018390527f000000000000000000000000000000000000000000000000000000000000000090911690636b8357ac90604401600060405180830381600087803b158015610f1757600080fd5b505af1158015610f2b573d6000803e3d6000fd5b505050505b610f3a33886123b7565b6000610f466000611a25565b905087811015610f6957604051633ebc3cb560e11b815260040160405180910390fd5b610f746000896126d2565b610f7d8561272c565b60065485511015610fa157604051633f4e2f2960e21b815260040160405180910390fd5b6001600160a01b038916610fc857604051633f4e2f2960e21b815260040160405180910390fd5b600654600003610feb5760405163d123a59d60e01b815260040160405180910390fd5b6000610ff687611f13565b6001600160a01b03160361101d57604051634ea9399d60e01b815260040160405180910390fd5b6000868152600a602052604090205460ff161561104d5760405163e5d48ceb60e01b815260040160405180910390fd5b6000611058876114d5565b60078054919250600061106a836140fd565b919050555060006040518060a001604052808c6001600160a01b031681526020018b81526020018a15158152602001600654815260200183815250905087600d60008481526020019081526020016000208190555080600c600084815260200190815260200160002060008201518160000160006101000a8154816001600160a01b0302191690836001600160a01b031602179055506020820151816001015560408201518160020160006101000a81548160ff021916908315150217905550606082015181600301556080820151816004015590505061114f8189898989346127e4565b600654604080519182526001600160a01b038d16602083015281018b905289151560608201523390899084907f7214d2a3fc41a2bc94a7a69749a3d127afd8d8e79e0573dbe7892d84ee6fdd749060800160405180910390a4505050506111b66001600455565b50505050505050565b7f139c2898040ef16910dc9f44dc697df79363da767d8bc92f2e310312b816e46d6111e9816120a3565b6111f1612a29565b50565b6111fc612196565b6112046121ef565b6000838152600d6020526040812054908190036112345760405163789d685360e01b815260040160405180910390fd5b6000848152600c6020908152604091829020825160a08101845281546001600160a01b0316815260018083015493820193909352600282015460ff16151593810193909352600381015460608401819052600490910154608084015290036113d55760006112a185611a25565b905081602001518110156112c857604051633ebc3cb560e11b815260040160405180910390fd5b846001600160a01b031663a4cbec7334856112e287611f13565b88876040516020016112f4919061412f565b6040516020818303038152906040526040518663ffffffff1660e01b815260040161132294939291906141bf565b60206040518083038185885af1158015611340573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052508101906113659190614116565b5060405186907fdc2df65ad98a3640553c4a6fa9e8b28b7cac67002cff20bfa132ad69b51b9e6d90600090a26040516001600160a01b038616815286907f5a94fe36b9c96bbd30dafe9eb364e4d1ac0751c6f8c114bd0977952694149c079060200160405180910390a2506113ee565b604051633f4e2f2960e21b815260040160405180910390fd5b5050610ca86001600455565b6000611405816120a3565b6001600160a01b03821661142c57604051633f4e2f2960e21b815260040160405180910390fd5b6000826001600160a01b03164760405160006040518083038185875af1925050503d8060008114611479576040519150601f19603f3d011682016040523d82523d6000602084013e61147e565b606091505b5050905080610ca857604051631bc19cc560e31b815260040160405180910390fd5b6001600160a01b038116600090815260036020819052604082209081015460028201548254600190930154610ae29390612a7b565b600081466007546040516020016114ff939291909283526020830191909152604082015260600190565b604051602081830303815290604052805190602001209050919050565b611524612196565b61152c6121ef565b806001600160a01b031661153f83611f13565b6001600160a01b03161461156657604051633f4e2f2960e21b815260040160405180910390fd5b600061157484860186614206565b905080606001516001036117325760808101516000908152600b6020526040902060010154156115b757604051632da0ab1960e01b815260040160405180910390fd5b6040805160e08101825282516001600160a01b039081168252602080850151818401908152858501511515848601908152600160608601818152608080880183815260a089018d815260c08a01858152928c01516000908152600b9098529987209851895498166001600160a01b0319909816979097178855935191870191909155905160028601805491151560ff1992831617905590516003860155925160048501559351600584015592516006909201805492151592909116919091179055611681336114a0565b905081602001518110156116a857604051633ebc3cb560e11b815260040160405180910390fd5b6116b6338360200151612af5565b816040015180156116d05750600554600160a01b900460ff165b156116ec576116e782600001518360200151612b4c565b6116fe565b6116fe82600001518360200151613002565b60808201516040517fe843a2101c5af088cd2648db06f117411c38047d50a9f499f99cd99adb41490a90600090a250611982565b3360009081526009602052604090205460ff1661176257604051635cfb665160e01b815260040160405180910390fd5b60808101516000908152600e6020908152604080832033845290915290205460ff1615156001036117a65760405163c7d1b4c160e01b815260040160405180910390fd5b608080820180516000908152600e602090815260408083203384528252808320805460ff1916600190811790915593518352600b8252808320815160e08101835281546001600160a01b031681529481015492850192909252600282015460ff908116151591850191909152600382015460608501819052600483015495850195909552600582015460a085015260069091015416151560c0830152909190036118a3576040518060e0016040528083600001516001600160a01b031681526020018360200151815260200183604001511515815260200160018152602001836060015181526020018581526020016000151581525090506118b7565b606081018051906118b3826140fd565b9052505b80608001518160600151106118f55760808201516040517fb23e240f843ae6fd7bc9d43aef3333ff0333d69a51e80b0d869df7546162622590600090a25b6080808301516000908152600b6020908152604091829020845181546001600160a01b039091166001600160a01b03199091161781559084015160018201559083015160028201805491151560ff199283161790556060840151600383015591830151600482015560a0830151600582015560c09092015160069092018054921515929091169190911790555b6080810151604080518581523360208201527fb56eee7198c2db26c3593517941abaa02608050011d356b4f29e8a5c6a269aed910160405180910390a250610c7d6001600455565b7f139c2898040ef16910dc9f44dc697df79363da767d8bc92f2e310312b816e46d6119f4816120a3565b6111f16130eb565b6000918252602082815260408084206001600160a01b0393909316845291905290205460ff1690565b6001600160a01b0381166000908152600360205260408120600781015460068201546004830154600590930154610ae29390612a7b565b6000611a67816120a3565b610c7d848484613126565b6000611a7d816120a3565b610ca883836131be565b6000611a92816120a3565b60068290556040518281527fe125065bd51f3e795dd066e6f64e43cedd27dc38adf0062c7be9eaa209ae114790602001610d88565b611acf612196565b611ad76121ef565b84600003611af857604051636c13c71b60e01b815260040160405180910390fd5b84611b0283611a25565b1015611b2157604051633ebc3cb560e11b815260040160405180910390fd5b611b2b82866126d2565b611b3533866123b7565b6001600160a01b038616611b5c57604051633f4e2f2960e21b815260040160405180910390fd5b6000611b6784611f13565b6001600160a01b031603611b8e57604051634ea9399d60e01b815260040160405180910390fd5b6000838152600a602052604090205460ff1615611bbe5760405163e5d48ceb60e01b815260040160405180910390fd5b6000611bc9846114d5565b600780549192506000611bdb836140fd565b90915550506000818152600d60209081526040808320879055805160a0810182526001600160a01b038b811682528184018b81528a1515838501908152600160608501818152608086018a81528a8a52600c90985295909720845181546001600160a01b031916908516178155915196820196909655945160028601805460ff191691151591909117905591516003850155915160049093019290925590841663a4cbec733487611c8b81611f13565b8786604051602001611c9d919061412f565b6040516020818303038152906040526040518663ffffffff1660e01b8152600401611ccb94939291906141bf565b60206040518083038185885af1158015611ce9573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190611d0e9190614116565b5060408051600181526001600160a01b038a166020820152808201899052871515606082015290513391879185917f7214d2a3fc41a2bc94a7a69749a3d127afd8d8e79e0573dbe7892d84ee6fdd74919081900360800190a46040516001600160a01b038516815282907f5a94fe36b9c96bbd30dafe9eb364e4d1ac0751c6f8c114bd0977952694149c079060200160405180910390a25050611db16001600455565b505050505050565b611dc1612196565b611dc96121ef565b6000848152600d602052604081205490819003611df95760405163789d685360e01b815260040160405180910390fd5b600654600003611e1c5760405163d123a59d60e01b815260040160405180910390fd5b611e258461272c565b6000858152600c6020908152604091829020825160a08101845281546001600160a01b0316815260018083015493820193909352600282015460ff16151593810193909352600381015460608401819052600490910154608084015211156113d5578351855114611ea957604051633f4e2f2960e21b815260040160405180910390fd5b611eb78183878787346127e4565b60405186907fdc2df65ad98a3640553c4a6fa9e8b28b7cac67002cff20bfa132ad69b51b9e6d90600090a25050610c7d6001600455565b600082815260208190526040902060010154611f09816120a3565b610ca88383612131565b6000908152600860205260409020546001600160a01b031690565b611f36612196565b611f3e6121ef565b6000818152600b602052604081206001810154909103611f715760405163789d685360e01b815260040160405180910390fd5b600681015460ff1615611f9757604051632da0ab1960e01b815260040160405180910390fd5b806004015481600301541015611fc057604051631b8d5dc160e01b815260040160405180910390fd5b6000611fcc60006114a0565b90508160010154811015611ff357604051633ebc3cb560e11b815260040160405180910390fd5b61200260008360010154612af5565b60068201805460ff19166001179055600282015460ff16801561202e5750600554600160a01b900460ff165b15612052578154600183015461204d916001600160a01b031690612b4c565b61206c565b8154600183015461206c916001600160a01b031690613002565b60405183907fe843a2101c5af088cd2648db06f117411c38047d50a9f499f99cd99adb41490a90600090a250506111f16001600455565b6111f181336132dd565b6120b782826119fc565b610d2c576000828152602081815260408083206001600160a01b03851684529091529020805460ff191660011790556120ed3390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b61213b82826119fc565b15610d2c576000828152602081815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b6002600454036121e85760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610d19565b6002600455565b60015460ff16156122355760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b6044820152606401610d19565b565b6040516001600160a01b0380851660248301528316604482015260648101829052610c7d9085906323b872dd60e01b906084015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152613336565b80158061231c5750604051636eb1769f60e11b81523060048201526001600160a01b03838116602483015284169063dd62ed3e90604401602060405180830381865afa1580156122f6573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061231a9190614116565b155b6123875760405162461bcd60e51b815260206004820152603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527520746f206e6f6e2d7a65726f20616c6c6f77616e636560501b6064820152608401610d19565b6040516001600160a01b038316602482015260448101829052610ca890849063095ea7b360e01b9060640161226b565b6005546040516370a0823160e01b81526001600160a01b03848116600483015260009216906370a0823190602401602060405180830381865afa158015612402573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124269190614116565b60408051808201909152600d81526c6275726e2875696e743235362960981b602090910152905060006317ad327360e31b7f00000000000000000000000000000000000000000000000000000000000000006001600160e01b031916016125615760055461249f906001600160a01b0316853086612237565b60055460408051602480820187905282518083039091018152604490910182526020810180516001600160e01b03167f00000000000000000000000000000000000000000000000000000000000000006001600160e01b03191617905290516001600160a01b03909216916125149190614284565b6000604051808303816000865af19150503d8060008114612551576040519150601f19603f3d011682016040523d82523d6000602084013e612556565b606091505b505080915050612626565b600554604080516001600160a01b038781166024830152604480830188905283518084039091018152606490920183526020820180516001600160e01b03167f00000000000000000000000000000000000000000000000000000000000000006001600160e01b031916179052915191909216916125de91614284565b6000604051808303816000865af19150503d806000811461261b576040519150601f19603f3d011682016040523d82523d6000602084013e612620565b606091505b50909150505b6005546040516370a0823160e01b81526001600160a01b03868116600483015260009216906370a0823190602401602060405180830381865afa158015612671573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126959190614116565b90508115806126ad57506126a984846142a0565b8114155b156126cb5760405163da67ce5960e01b815260040160405180910390fd5b5050505050565b60006126dd83611a25565b6001600160a01b038416600090815260036020526040902042600490910155905061270882826142a0565b6001600160a01b039093166000908152600360205260409020600701929092555050565b805160005b61273c6001836142a0565b811015610ca85760006127508260016142b3565b90505b828110156127d15783818151811061276d5761276d6140d1565b60200260200101516001600160a01b0316848381518110612790576127906140d1565b60200260200101516001600160a01b0316036127bf57604051636e7c990560e01b815260040160405180910390fd5b806127c9816140fd565b915050612753565b50806127dc816140fd565b915050612731565b825184511415806127f757508151845114155b1561281557604051633f4e2f2960e21b815260040160405180910390fd5b6000805b8551811015612a085760096000878381518110612838576128386140d1565b6020908102919091018101516001600160a01b0316825281019190915260400160009081205460ff161515900361288257604051635cfb665160e01b815260040160405180910390fd5b858181518110612894576128946140d1565b60200260200101516001600160a01b031663a4cbec738683815181106128bc576128bc6140d1565b6020026020010151896128ce8b611f13565b8886815181106128e0576128e06140d1565b60200260200101518d6040516020016128f9919061412f565b6040516020818303038152906040526040518663ffffffff1660e01b815260040161292794939291906141bf565b60206040518083038185885af1158015612945573d6000803e3d6000fd5b50505050506040513d601f19601f8201168201806040525081019061296a9190614116565b5087608001517f5a94fe36b9c96bbd30dafe9eb364e4d1ac0751c6f8c114bd0977952694149c078783815181106129a3576129a36140d1565b60200260200101516040516129c791906001600160a01b0391909116815260200190565b60405180910390a28481815181106129e1576129e16140d1565b6020026020010151826129f491906142b3565b915080612a00816140fd565b915050612819565b508181146111b65760405163c0a890d760e01b815260040160405180910390fd5b612a3161340b565b6001805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a1565b83838114612aed574260025484612a9291906142b3565b11612a9e575082612aed565b4260025484612aad91906142b3565b1115612aed576000612abf84426142a0565b90506000612acd84836142c6565b612ad790846142b3565b9050858111612ae65780612ae8565b855b925050505b949350505050565b6000612b00836114a0565b6001600160a01b03841660009081526003602052604090204290559050612b2782826142a0565b6001600160a01b03909316600090815260036020819052604090912001929092555050565b60055460408051600481526024810182526020810180516001600160e01b03166333662b8160e11b179052905160009283926001600160a01b0390911691612b949190614284565b6000604051808303816000865af19150503d8060008114612bd1576040519150601f19603f3d011682016040523d82523d6000602084013e612bd6565b606091505b5091509150818015612be85750805115155b15612ffc57600081806020019051810190612c0391906142dd565b90506001600160a01b03811615612ffa576005546040516370a0823160e01b81523060048201526000916001600160a01b0316906370a0823190602401602060405180830381865afa158015612c5d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c819190614116565b9050612c8d3086613002565b6005546040516370a0823160e01b815230600482015260009183916001600160a01b03909116906370a0823190602401602060405180830381865afa158015612cda573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612cfe9190614116565b612d0891906142a0565b60408051600481526024810182526020810180516001600160e01b0316633312a88160e21b179052905191925060009182916001600160a01b03871691612d4f9190614284565b6000604051808303816000865af19150503d8060008114612d8c576040519150601f19603f3d011682016040523d82523d6000602084013e612d91565b606091505b506005546040516001600160a01b03898116602483015260448201889052939550919350919091169060640160408051601f198184030181529181526020820180516001600160e01b031663095ea7b360e01b17905251612df29190614284565b6000604051808303816000865af19150503d8060008114612e2f576040519150601f19603f3d011682016040523d82523d6000602084013e612e34565b606091505b5090975050861580612e44575081155b80612ec05750600554604051636eb1769f60e11b81523060048201526001600160a01b0387811660248301528592169063dd62ed3e90604401602060405180830381865afa158015612e9a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ebe9190614116565b105b15612ee757600554612edc906001600160a01b03168a85613454565b505050505050505050565b600081806020019051810190612efd91906142dd565b90506001600160a01b038116612f3057600554612f24906001600160a01b03168b86613454565b50505050505050505050565b856001600160a01b031684604051602401612f4d91815260200190565b60408051601f198184030181529181526020820180516001600160e01b0316632e1a7d4d60e01b17905251612f829190614284565b6000604051808303816000865af19150503d8060008114612fbf576040519150601f19603f3d011682016040523d82523d6000602084013e612fc4565b606091505b50508098505087612fe657600554612f24906001600160a01b03168b86613454565b612f246001600160a01b0382168b86613454565b505b610c7d84845b6005546040516001600160a01b0384811660248301526044820184905260009216907f00000000000000000000000000000000000000000000000000000000000000009060640160408051601f198184030181529181526020820180516001600160e01b03166001600160e01b03199094169390931790925290516130879190614284565b6000604051808303816000865af19150503d80600081146130c4576040519150601f19603f3d011682016040523d82523d6000602084013e6130c9565b606091505b5050905080610ca857604051638d938d7b60e01b815260040160405180910390fd5b6130f36121ef565b6001805460ff1916811790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a25833612a5e565b61313360026000196142fa565b82118061314b575061314860026000196142fa565b81115b15613169576040516301e8cafb60e71b815260040160405180910390fd5b6131738383613484565b61317d8382613529565b60408051838152602081018390526001600160a01b038516917f93f3bbfe8cfb354ec059175107653f49f6eb479a8622a7d83866ea015435c9449101610b3f565b80518251146131e057604051633f4e2f2960e21b815260040160405180910390fd5b60005b8251811015610ca8578181815181106131fe576131fe6140d1565b60200260200101516008600085848151811061321c5761321c6140d1565b6020026020010151815260200190815260200160002060006101000a8154816001600160a01b0302191690836001600160a01b03160217905550818181518110613268576132686140d1565b60200260200101516001600160a01b03167f3083b3b95a46df5c4a6cdd1fa242b76f968ed75bf364ec15de70e5b043f628318483815181106132ac576132ac6140d1565b60200260200101516040516132c391815260200190565b60405180910390a2806132d5816140fd565b9150506131e3565b6132e782826119fc565b610d2c576132f4816135d3565b6132ff8360206135e5565b60405160200161331092919061431c565b60408051601f198184030181529082905262461bcd60e51b8252610d1991600401614391565b600061338b826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166137879092919063ffffffff16565b90508051600014806133ac5750808060200190518101906133ac91906143a4565b610ca85760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610d19565b60015460ff166122355760405162461bcd60e51b815260206004820152601460248201527314185d5cd8589b194e881b9bdd081c185d5cd95960621b6044820152606401610d19565b6040516001600160a01b038316602482015260448101829052610ca890849063a9059cbb60e01b9060640161226b565b6001600160a01b038216600090815260036020526040812060020154906134aa846114a0565b6001600160a01b038516600090815260036020526040902060020184905590506134d5838383613796565b6001600160a01b038516600090815260036020819052604090912001556002546134ff90846142fa565b6001600160a01b039094166000908152600360205260409020600181019490945550504290915550565b6001600160a01b0382166000908152600360205260408120600601549061354f84611a25565b6001600160a01b0385166000908152600360205260409020600601849055905061357a838383613796565b6001600160a01b0385166000908152600360205260409020600701556002546135a390846142fa565b6001600160a01b039094166000908152600360205260409020600581019490945550504260049092019190915550565b6060610ae26001600160a01b03831660145b606060006135f48360026142c6565b6135ff9060026142b3565b6001600160401b0381111561361657613616613a2e565b6040519080825280601f01601f191660200182016040528015613640576020820181803683370190505b509050600360fc1b8160008151811061365b5761365b6140d1565b60200101906001600160f81b031916908160001a905350600f60fb1b8160018151811061368a5761368a6140d1565b60200101906001600160f81b031916908160001a90535060006136ae8460026142c6565b6136b99060016142b3565b90505b6001811115613731576f181899199a1a9b1b9c1cb0b131b232b360811b85600f16601081106136ed576136ed6140d1565b1a60f81b828281518110613703576137036140d1565b60200101906001600160f81b031916908160001a90535060049490941c9361372a816143c1565b90506136bc565b5083156137805760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401610d19565b9392505050565b6060612aed84846000856137ed565b600080848411156137cc576137ab85856142a0565b90508083116137bb5760006137c5565b6137c581846142a0565b91506137e5565b6137d684866142a0565b90506137e281846142b3565b91505b509392505050565b60608247101561384e5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b6064820152608401610d19565b600080866001600160a01b0316858760405161386a9190614284565b60006040518083038185875af1925050503d80600081146138a7576040519150601f19603f3d011682016040523d82523d6000602084013e6138ac565b606091505b5091509150612ae88783838760608315613927578251600003613920576001600160a01b0385163b6139205760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610d19565b5081612aed565b612aed838381511561393c5781518083602001fd5b8060405162461bcd60e51b8152600401610d199190614391565b60006020828403121561396857600080fd5b5035919050565b60006020828403121561398157600080fd5b81356001600160e01b03198116811461378057600080fd5b6001600160a01b03811681146111f157600080fd5b6000602082840312156139c057600080fd5b813561378081613999565b80151581146111f157600080fd5b600080604083850312156139ec57600080fd5b8235915060208301356139fe816139cb565b809150509250929050565b60008060408385031215613a1c57600080fd5b8235915060208301356139fe81613999565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b0381118282101715613a6c57613a6c613a2e565b604052919050565b60006001600160401b03821115613a8d57613a8d613a2e565b5060051b60200190565b600082601f830112613aa857600080fd5b81356020613abd613ab883613a74565b613a44565b82815260059290921b84018101918181019086841115613adc57600080fd5b8286015b84811015613b00578035613af381613999565b8352918301918301613ae0565b509695505050505050565b60008060408385031215613b1e57600080fd5b82356001600160401b0380821115613b3557600080fd5b613b4186838701613a97565b9350602091508185013581811115613b5857600080fd5b85019050601f81018613613b6b57600080fd5b8035613b79613ab882613a74565b81815260059190911b82018301908381019088831115613b9857600080fd5b928401925b82841015613bbf578335613bb0816139cb565b82529284019290840190613b9d565b80955050505050509250929050565b600060208284031215613be057600080fd5b8135613780816139cb565b600082601f830112613bfc57600080fd5b81356020613c0c613ab883613a74565b82815260059290921b84018101918181019086841115613c2b57600080fd5b8286015b84811015613b005780358352918301918301613c2f565b600082601f830112613c5757600080fd5b81356001600160401b03811115613c7057613c70613a2e565b613c83601f8201601f1916602001613a44565b818152846020838601011115613c9857600080fd5b816020850160208301376000918101602001919091529392505050565b600082601f830112613cc657600080fd5b81356020613cd6613ab883613a74565b82815260059290921b84018101918181019086841115613cf557600080fd5b8286015b84811015613b005780356001600160401b03811115613d185760008081fd5b613d268986838b0101613c46565b845250918301918301613cf9565b600080600080600080600060e0888a031215613d4f57600080fd5b8735613d5a81613999565b9650602088013595506040880135613d71816139cb565b94506060880135935060808801356001600160401b0380821115613d9457600080fd5b613da08b838c01613a97565b945060a08a0135915080821115613db657600080fd5b613dc28b838c01613beb565b935060c08a0135915080821115613dd857600080fd5b50613de58a828b01613cb5565b91505092959891949750929550565b600080600060608486031215613e0957600080fd5b833592506020840135613e1b81613999565b915060408401356001600160401b03811115613e3657600080fd5b613e4286828701613c46565b9150509250925092565b60008060008060608587031215613e6257600080fd5b84356001600160401b0380821115613e7957600080fd5b818701915087601f830112613e8d57600080fd5b813581811115613e9c57600080fd5b886020828501011115613eae57600080fd5b6020928301965094505085013591506040850135613ecb81613999565b939692955090935050565b600080600060608486031215613eeb57600080fd5b8335613ef681613999565b95602085013595506040909401359392505050565b60008060408385031215613f1e57600080fd5b82356001600160401b0380821115613f3557600080fd5b613f4186838701613beb565b93506020850135915080821115613f5757600080fd5b50613f6485828601613a97565b9150509250929050565b82518152602080840151818301526040808501518184015260608086015181850152845160808501529184015160a084015283015160c083015282015160e08201526101008101613780565b60008060008060008060c08789031215613fd357600080fd5b8635613fde81613999565b9550602087013594506040870135613ff5816139cb565b935060608701359250608087013561400c81613999565b915060a08701356001600160401b0381111561402757600080fd5b61403389828a01613c46565b9150509295509295509295565b6000806000806080858703121561405657600080fd5b8435935060208501356001600160401b038082111561407457600080fd5b61408088838901613a97565b9450604087013591508082111561409657600080fd5b6140a288838901613beb565b935060608701359150808211156140b857600080fd5b506140c587828801613cb5565b91505092959194509250565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b60006001820161410f5761410f6140e7565b5060010190565b60006020828403121561412857600080fd5b5051919050565b81516001600160a01b0316815260208083015190820152604080830151151590820152606080830151908201526080918201519181019190915260a00190565b60005b8381101561418a578181015183820152602001614172565b50506000910152565b600081518084526141ab81602086016020860161416f565b601f01601f19169290920160200192915050565b8481526001600160a01b03841660208201526080604082018190526000906141e990830185614193565b82810360608401526141fb8185614193565b979650505050505050565b600060a0828403121561421857600080fd5b60405160a081018181106001600160401b038211171561423a5761423a613a2e565b604052823561424881613999565b8152602083810135908201526040830135614262816139cb565b6040820152606083810135908201526080928301359281019290925250919050565b6000825161429681846020870161416f565b9190910192915050565b81810381811115610ae257610ae26140e7565b80820180821115610ae257610ae26140e7565b8082028115828204841417610ae257610ae26140e7565b6000602082840312156142ef57600080fd5b815161378081613999565b60008261431757634e487b7160e01b600052601260045260246000fd5b500490565b7f416363657373436f6e74726f6c3a206163636f756e742000000000000000000081526000835161435481601785016020880161416f565b7001034b99036b4b9b9b4b733903937b6329607d1b601791840191820152835161438581602884016020880161416f565b01602801949350505050565b6020815260006137806020830184614193565b6000602082840312156143b657600080fd5b8151613780816139cb565b6000816143d0576143d06140e7565b50600019019056fea264697066735822122014555f41f27d588b1fdd35163b5e30a50cb4cb9721ea9ab185fb14f5b2e8b1c464736f6c63430008130033139c2898040ef16910dc9f44dc697df79363da767d8bc92f2e310312b816e46d000000000000000000000000909583f64948e4c6a9c7a9285c292e9d9302fb960000000000000000000000005db7ad07d4903579a0342f7332bd4e3ed480d1af0000000000000000000000005db7ad07d4903579a0342f7332bd4e3ed480d1af0000000000000000000000002ff4e5ff77e49eba45bffea2f75814d114f9c90400000000000000000000000051b206899bcfcbf4530412a43ed5c2336f91c90c0000000000000000000000000000000000000000000000000000000000015180000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000042000000000000000000000000000000000000000000000000000000000000004e000000000000000000000000000000000000000000000000000000000000005a040c10f190000000000000000000000000000000000000000000000000000000042966c6800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000a4b1000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000002105000000000000000000000000000000000000000000000000000000000000e7080000000000000000000000000000000000000000000000000000000000013e3100000000000000000000000000000000000000000000000000000000000000890000000000000000000000000000000000000000000000000000000000000d0a000000000000000000000000000000000000000000000000000000000000008200000000000000000000000000000000000000000000000000000000000138de0000000000000000000000000000000000000000000000000000000000001388000000000000000000000000000000000000000000000000000000000000009200000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000038000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000000df00000000000000000000000000000000000000000000000000000000000182320000000000000000000000000000000000000000000000000000000000000005000000000000000000000000cb8530f2b20f3c62602f01d3a78bcc58e9aea8530000000000000000000000005ef37628d45c80740fb6db7ed9c0a753b4f85263000000000000000000000000a9bd5559531fe372e866ad4337599962d5810e010000000000000000000000000ea1c09b884dc5758ed1d9bd006dceb20d31c439000000000000000000000000dea0243b5ee5200d1feba615eaa7385598417b8600000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000033b2e3c9fd0803ce80000000000000000000000000000000000000000000000033b2e3c9fd0803ce80000000000000000000000000000000000000000000000033b2e3c9fd0803ce80000000000000000000000000000000000000000000000033b2e3c9fd0803ce80000000000000000000000000000000000000000000000033b2e3c9fd0803ce800000000000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000033b2e3c9fd0803ce80000000000000000000000000000000000000000000000033b2e3c9fd0803ce80000000000000000000000000000000000000000000000033b2e3c9fd0803ce80000000000000000000000000000000000000000000000033b2e3c9fd0803ce80000000000000000000000000000000000000000000000033b2e3c9fd0803ce8000000

Deployed Bytecode

0x6080604052600436106102805760003560e01c80637385e7fb1161014f578063c1eb7137116100c1578063d3a8b6311161007a578063d3a8b63114610a08578063d547741f14610a1b578063d6991b5b14610a3b578063e751f27114610a5b578063eab6995814610a7b578063fc0c546a14610a9157600080fd5b8063c1eb713714610898578063c415b95c146108d1578063cdf0366f1461091d578063ced67f0c1461093d578063d236ad68146109d5578063d30a41c8146109f557600080fd5b806391d148541161011357806391d148541461075f578063968638861461077f578063998955d31461082d578063a08d56541461084d578063a217fddf1461086d578063affed0e01461088257600080fd5b80637385e7fb1461066c5780637e0a8c6a1461069c5780638456cb59146106f557806387a131bb1461070a57806389a3b4fb1461073e57600080fd5b806336568abe116101f357806351cff8d9116101ac57806351cff8d914610587578063544fe464146105a75780635c975abb146105f4578063651fd2681461060c5780637256d8721461062c578063734afa2b1461064c57600080fd5b806336568abe146104d8578063365fc213146104f8578063389ed2671461051857806339c82de61461054c5780633f4ba83a1461055f5780634995c3e71461057457600080fd5b806315af3b671161024557806315af3b671461036d5780631f9bb777146103a8578063215e73e114610438578063248a9ca31461046857806327a65d3f146104985780632f2ff15d146104b857600080fd5b80625856cb1461028c57806301ffc9a7146102cc5780630c05f82c146102fc5780630fb5a6b41461033557806312ca7dbb1461034b57600080fd5b3661028757005b600080fd5b34801561029857600080fd5b506102b96102a7366004613956565b600d6020526000908152604090205481565b6040519081526020015b60405180910390f35b3480156102d857600080fd5b506102ec6102e736600461396f565b610ab1565b60405190151581526020016102c3565b34801561030857600080fd5b506102b96103173660046139ae565b6001600160a01b031660009081526003602052604090206002015490565b34801561034157600080fd5b506102b960025481565b34801561035757600080fd5b5061036b6103663660046139d9565b610ae8565b005b34801561037957600080fd5b506102ec610388366004613a09565b600e60209081526000928352604080842090915290825290205460ff1681565b3480156103b457600080fd5b506104006103c3366004613956565b600c60205260009081526040902080546001820154600283015460038401546004909401546001600160a01b0390931693919260ff909116919085565b604080516001600160a01b0390961686526020860194909452911515928401929092526060830191909152608082015260a0016102c3565b34801561044457600080fd5b506102ec6104533660046139ae565b60096020526000908152604090205460ff1681565b34801561047457600080fd5b506102b9610483366004613956565b60009081526020819052604090206001015490565b3480156104a457600080fd5b5061036b6104b3366004613b0b565b610b4c565b3480156104c457600080fd5b5061036b6104d3366004613a09565b610c83565b3480156104e457600080fd5b5061036b6104f3366004613a09565b610cad565b34801561050457600080fd5b5061036b610513366004613bce565b610d30565b34801561052457600080fd5b506102b97f139c2898040ef16910dc9f44dc697df79363da767d8bc92f2e310312b816e46d81565b61036b61055a366004613d34565b610d94565b34801561056b57600080fd5b5061036b6111bf565b61036b610582366004613df4565b6111f4565b34801561059357600080fd5b5061036b6105a23660046139ae565b6113fa565b3480156105b357600080fd5b506105db7f40c10f190000000000000000000000000000000000000000000000000000000081565b6040516001600160e01b031990911681526020016102c3565b34801561060057600080fd5b5060015460ff166102ec565b34801561061857600080fd5b506102b96106273660046139ae565b6114a0565b34801561063857600080fd5b506102b9610647366004613956565b6114d5565b34801561065857600080fd5b5061036b610667366004613e4c565b61151c565b34801561067857600080fd5b506102ec610687366004613956565b600a6020526000908152604090205460ff1681565b3480156106a857600080fd5b5060408051808201909152600d81526c6275726e2875696e743235362960981b6020909101526105db7f42966c689b5afe9b9b3f8a7103b2a19980d59629bfd6a20a60972312ed41d83681565b34801561070157600080fd5b5061036b6119ca565b34801561071657600080fd5b506105db7f42966c680000000000000000000000000000000000000000000000000000000081565b34801561074a57600080fd5b506005546102ec90600160a01b900460ff1681565b34801561076b57600080fd5b506102ec61077a366004613a09565b6119fc565b34801561078b57600080fd5b506107e661079a366004613956565b600b6020526000908152604090208054600182015460028301546003840154600485015460058601546006909601546001600160a01b0390951695939460ff9384169492939192911687565b604080516001600160a01b0390981688526020880196909652931515948601949094526060850191909152608084015260a0830191909152151560c082015260e0016102c3565b34801561083957600080fd5b506102b96108483660046139ae565b611a25565b34801561085957600080fd5b5061036b610868366004613ed6565b611a5c565b34801561087957600080fd5b506102b9600081565b34801561088e57600080fd5b506102b960075481565b3480156108a457600080fd5b506102b96108b33660046139ae565b6001600160a01b031660009081526003602052604090206006015490565b3480156108dd57600080fd5b506109057f0000000000000000000000002ff4e5ff77e49eba45bffea2f75814d114f9c90481565b6040516001600160a01b0390911681526020016102c3565b34801561092957600080fd5b5061036b610938366004613f0b565b611a72565b34801561094957600080fd5b506109c76109583660046139ae565b60036020818152600092835260409283902083516080808201865282548252600183015482850152600283015482870152938201546060808301919091528551948501865260048301548552600583015493850193909352600682015494840194909452600701549082015282565b6040516102c3929190613f6e565b3480156109e157600080fd5b5061036b6109f0366004613956565b611a87565b61036b610a03366004613fba565b611ac7565b61036b610a16366004614040565b611db9565b348015610a2757600080fd5b5061036b610a36366004613a09565b611eee565b348015610a4757600080fd5b50610905610a56366004613956565b611f13565b348015610a6757600080fd5b5061036b610a76366004613956565b611f2e565b348015610a8757600080fd5b506102b960065481565b348015610a9d57600080fd5b50600554610905906001600160a01b031681565b60006001600160e01b03198216637965db0b60e01b1480610ae257506301ffc9a760e01b6001600160e01b03198316145b92915050565b6000610af3816120a3565b6000838152600a6020908152604091829020805460ff1916851515908117909155915191825284917f8670d56066b58d6b5eff6e9ff63fb7bc28c6d4ec9676c45cf2360314e9e9c24a91015b60405180910390a2505050565b6000610b57816120a3565b8151835114610b7957604051633f4e2f2960e21b815260040160405180910390fd5b60005b8351811015610c7d57828181518110610b9757610b976140d1565b602002602001015160096000868481518110610bb557610bb56140d1565b60200260200101516001600160a01b03166001600160a01b0316815260200190815260200160002060006101000a81548160ff021916908315150217905550838181518110610c0657610c066140d1565b60200260200101516001600160a01b03167fd3577f4d8ca054ac85e150e2a592220c54234d070e9f8ed0199869b7a17b2520848381518110610c4a57610c4a6140d1565b6020026020010151604051610c63911515815260200190565b60405180910390a280610c75816140fd565b915050610b7c565b50505050565b600082815260208190526040902060010154610c9e816120a3565b610ca883836120ad565b505050565b6001600160a01b0381163314610d225760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201526e103937b632b9903337b91039b2b63360891b60648201526084015b60405180910390fd5b610d2c8282612131565b5050565b6000610d3b816120a3565b60058054831515600160a01b0260ff60a01b199091161790556040517f2b777c07ea46054410835bc43625d87926c44d9a6b4ec49a7e3248d434cfe5b590610d8890841515815260200190565b60405180910390a15050565b610d9c612196565b610da46121ef565b85600003610dc557604051636c13c71b60e01b815260040160405180910390fd5b604051633b46f5db60e21b8152600481018790526000907f0000000000000000000000002ff4e5ff77e49eba45bffea2f75814d114f9c9046001600160a01b03169063ed1bd76c90602401602060405180830381865afa158015610e2d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e519190614116565b90508015610f3057610e71336005546001600160a01b0316903084612237565b600554610ea8906001600160a01b03167f0000000000000000000000002ff4e5ff77e49eba45bffea2f75814d114f9c904836122a2565b600554604051631ae0d5eb60e21b81526001600160a01b039182166004820152602481018390527f0000000000000000000000002ff4e5ff77e49eba45bffea2f75814d114f9c90490911690636b8357ac90604401600060405180830381600087803b158015610f1757600080fd5b505af1158015610f2b573d6000803e3d6000fd5b505050505b610f3a33886123b7565b6000610f466000611a25565b905087811015610f6957604051633ebc3cb560e11b815260040160405180910390fd5b610f746000896126d2565b610f7d8561272c565b60065485511015610fa157604051633f4e2f2960e21b815260040160405180910390fd5b6001600160a01b038916610fc857604051633f4e2f2960e21b815260040160405180910390fd5b600654600003610feb5760405163d123a59d60e01b815260040160405180910390fd5b6000610ff687611f13565b6001600160a01b03160361101d57604051634ea9399d60e01b815260040160405180910390fd5b6000868152600a602052604090205460ff161561104d5760405163e5d48ceb60e01b815260040160405180910390fd5b6000611058876114d5565b60078054919250600061106a836140fd565b919050555060006040518060a001604052808c6001600160a01b031681526020018b81526020018a15158152602001600654815260200183815250905087600d60008481526020019081526020016000208190555080600c600084815260200190815260200160002060008201518160000160006101000a8154816001600160a01b0302191690836001600160a01b031602179055506020820151816001015560408201518160020160006101000a81548160ff021916908315150217905550606082015181600301556080820151816004015590505061114f8189898989346127e4565b600654604080519182526001600160a01b038d16602083015281018b905289151560608201523390899084907f7214d2a3fc41a2bc94a7a69749a3d127afd8d8e79e0573dbe7892d84ee6fdd749060800160405180910390a4505050506111b66001600455565b50505050505050565b7f139c2898040ef16910dc9f44dc697df79363da767d8bc92f2e310312b816e46d6111e9816120a3565b6111f1612a29565b50565b6111fc612196565b6112046121ef565b6000838152600d6020526040812054908190036112345760405163789d685360e01b815260040160405180910390fd5b6000848152600c6020908152604091829020825160a08101845281546001600160a01b0316815260018083015493820193909352600282015460ff16151593810193909352600381015460608401819052600490910154608084015290036113d55760006112a185611a25565b905081602001518110156112c857604051633ebc3cb560e11b815260040160405180910390fd5b846001600160a01b031663a4cbec7334856112e287611f13565b88876040516020016112f4919061412f565b6040516020818303038152906040526040518663ffffffff1660e01b815260040161132294939291906141bf565b60206040518083038185885af1158015611340573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052508101906113659190614116565b5060405186907fdc2df65ad98a3640553c4a6fa9e8b28b7cac67002cff20bfa132ad69b51b9e6d90600090a26040516001600160a01b038616815286907f5a94fe36b9c96bbd30dafe9eb364e4d1ac0751c6f8c114bd0977952694149c079060200160405180910390a2506113ee565b604051633f4e2f2960e21b815260040160405180910390fd5b5050610ca86001600455565b6000611405816120a3565b6001600160a01b03821661142c57604051633f4e2f2960e21b815260040160405180910390fd5b6000826001600160a01b03164760405160006040518083038185875af1925050503d8060008114611479576040519150601f19603f3d011682016040523d82523d6000602084013e61147e565b606091505b5050905080610ca857604051631bc19cc560e31b815260040160405180910390fd5b6001600160a01b038116600090815260036020819052604082209081015460028201548254600190930154610ae29390612a7b565b600081466007546040516020016114ff939291909283526020830191909152604082015260600190565b604051602081830303815290604052805190602001209050919050565b611524612196565b61152c6121ef565b806001600160a01b031661153f83611f13565b6001600160a01b03161461156657604051633f4e2f2960e21b815260040160405180910390fd5b600061157484860186614206565b905080606001516001036117325760808101516000908152600b6020526040902060010154156115b757604051632da0ab1960e01b815260040160405180910390fd5b6040805160e08101825282516001600160a01b039081168252602080850151818401908152858501511515848601908152600160608601818152608080880183815260a089018d815260c08a01858152928c01516000908152600b9098529987209851895498166001600160a01b0319909816979097178855935191870191909155905160028601805491151560ff1992831617905590516003860155925160048501559351600584015592516006909201805492151592909116919091179055611681336114a0565b905081602001518110156116a857604051633ebc3cb560e11b815260040160405180910390fd5b6116b6338360200151612af5565b816040015180156116d05750600554600160a01b900460ff165b156116ec576116e782600001518360200151612b4c565b6116fe565b6116fe82600001518360200151613002565b60808201516040517fe843a2101c5af088cd2648db06f117411c38047d50a9f499f99cd99adb41490a90600090a250611982565b3360009081526009602052604090205460ff1661176257604051635cfb665160e01b815260040160405180910390fd5b60808101516000908152600e6020908152604080832033845290915290205460ff1615156001036117a65760405163c7d1b4c160e01b815260040160405180910390fd5b608080820180516000908152600e602090815260408083203384528252808320805460ff1916600190811790915593518352600b8252808320815160e08101835281546001600160a01b031681529481015492850192909252600282015460ff908116151591850191909152600382015460608501819052600483015495850195909552600582015460a085015260069091015416151560c0830152909190036118a3576040518060e0016040528083600001516001600160a01b031681526020018360200151815260200183604001511515815260200160018152602001836060015181526020018581526020016000151581525090506118b7565b606081018051906118b3826140fd565b9052505b80608001518160600151106118f55760808201516040517fb23e240f843ae6fd7bc9d43aef3333ff0333d69a51e80b0d869df7546162622590600090a25b6080808301516000908152600b6020908152604091829020845181546001600160a01b039091166001600160a01b03199091161781559084015160018201559083015160028201805491151560ff199283161790556060840151600383015591830151600482015560a0830151600582015560c09092015160069092018054921515929091169190911790555b6080810151604080518581523360208201527fb56eee7198c2db26c3593517941abaa02608050011d356b4f29e8a5c6a269aed910160405180910390a250610c7d6001600455565b7f139c2898040ef16910dc9f44dc697df79363da767d8bc92f2e310312b816e46d6119f4816120a3565b6111f16130eb565b6000918252602082815260408084206001600160a01b0393909316845291905290205460ff1690565b6001600160a01b0381166000908152600360205260408120600781015460068201546004830154600590930154610ae29390612a7b565b6000611a67816120a3565b610c7d848484613126565b6000611a7d816120a3565b610ca883836131be565b6000611a92816120a3565b60068290556040518281527fe125065bd51f3e795dd066e6f64e43cedd27dc38adf0062c7be9eaa209ae114790602001610d88565b611acf612196565b611ad76121ef565b84600003611af857604051636c13c71b60e01b815260040160405180910390fd5b84611b0283611a25565b1015611b2157604051633ebc3cb560e11b815260040160405180910390fd5b611b2b82866126d2565b611b3533866123b7565b6001600160a01b038616611b5c57604051633f4e2f2960e21b815260040160405180910390fd5b6000611b6784611f13565b6001600160a01b031603611b8e57604051634ea9399d60e01b815260040160405180910390fd5b6000838152600a602052604090205460ff1615611bbe5760405163e5d48ceb60e01b815260040160405180910390fd5b6000611bc9846114d5565b600780549192506000611bdb836140fd565b90915550506000818152600d60209081526040808320879055805160a0810182526001600160a01b038b811682528184018b81528a1515838501908152600160608501818152608086018a81528a8a52600c90985295909720845181546001600160a01b031916908516178155915196820196909655945160028601805460ff191691151591909117905591516003850155915160049093019290925590841663a4cbec733487611c8b81611f13565b8786604051602001611c9d919061412f565b6040516020818303038152906040526040518663ffffffff1660e01b8152600401611ccb94939291906141bf565b60206040518083038185885af1158015611ce9573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190611d0e9190614116565b5060408051600181526001600160a01b038a166020820152808201899052871515606082015290513391879185917f7214d2a3fc41a2bc94a7a69749a3d127afd8d8e79e0573dbe7892d84ee6fdd74919081900360800190a46040516001600160a01b038516815282907f5a94fe36b9c96bbd30dafe9eb364e4d1ac0751c6f8c114bd0977952694149c079060200160405180910390a25050611db16001600455565b505050505050565b611dc1612196565b611dc96121ef565b6000848152600d602052604081205490819003611df95760405163789d685360e01b815260040160405180910390fd5b600654600003611e1c5760405163d123a59d60e01b815260040160405180910390fd5b611e258461272c565b6000858152600c6020908152604091829020825160a08101845281546001600160a01b0316815260018083015493820193909352600282015460ff16151593810193909352600381015460608401819052600490910154608084015211156113d5578351855114611ea957604051633f4e2f2960e21b815260040160405180910390fd5b611eb78183878787346127e4565b60405186907fdc2df65ad98a3640553c4a6fa9e8b28b7cac67002cff20bfa132ad69b51b9e6d90600090a25050610c7d6001600455565b600082815260208190526040902060010154611f09816120a3565b610ca88383612131565b6000908152600860205260409020546001600160a01b031690565b611f36612196565b611f3e6121ef565b6000818152600b602052604081206001810154909103611f715760405163789d685360e01b815260040160405180910390fd5b600681015460ff1615611f9757604051632da0ab1960e01b815260040160405180910390fd5b806004015481600301541015611fc057604051631b8d5dc160e01b815260040160405180910390fd5b6000611fcc60006114a0565b90508160010154811015611ff357604051633ebc3cb560e11b815260040160405180910390fd5b61200260008360010154612af5565b60068201805460ff19166001179055600282015460ff16801561202e5750600554600160a01b900460ff165b15612052578154600183015461204d916001600160a01b031690612b4c565b61206c565b8154600183015461206c916001600160a01b031690613002565b60405183907fe843a2101c5af088cd2648db06f117411c38047d50a9f499f99cd99adb41490a90600090a250506111f16001600455565b6111f181336132dd565b6120b782826119fc565b610d2c576000828152602081815260408083206001600160a01b03851684529091529020805460ff191660011790556120ed3390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b61213b82826119fc565b15610d2c576000828152602081815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b6002600454036121e85760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610d19565b6002600455565b60015460ff16156122355760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b6044820152606401610d19565b565b6040516001600160a01b0380851660248301528316604482015260648101829052610c7d9085906323b872dd60e01b906084015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152613336565b80158061231c5750604051636eb1769f60e11b81523060048201526001600160a01b03838116602483015284169063dd62ed3e90604401602060405180830381865afa1580156122f6573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061231a9190614116565b155b6123875760405162461bcd60e51b815260206004820152603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527520746f206e6f6e2d7a65726f20616c6c6f77616e636560501b6064820152608401610d19565b6040516001600160a01b038316602482015260448101829052610ca890849063095ea7b360e01b9060640161226b565b6005546040516370a0823160e01b81526001600160a01b03848116600483015260009216906370a0823190602401602060405180830381865afa158015612402573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124269190614116565b60408051808201909152600d81526c6275726e2875696e743235362960981b602090910152905060006317ad327360e31b7f42966c68000000000000000000000000000000000000000000000000000000006001600160e01b031916016125615760055461249f906001600160a01b0316853086612237565b60055460408051602480820187905282518083039091018152604490910182526020810180516001600160e01b03167f42966c68000000000000000000000000000000000000000000000000000000006001600160e01b03191617905290516001600160a01b03909216916125149190614284565b6000604051808303816000865af19150503d8060008114612551576040519150601f19603f3d011682016040523d82523d6000602084013e612556565b606091505b505080915050612626565b600554604080516001600160a01b038781166024830152604480830188905283518084039091018152606490920183526020820180516001600160e01b03167f42966c68000000000000000000000000000000000000000000000000000000006001600160e01b031916179052915191909216916125de91614284565b6000604051808303816000865af19150503d806000811461261b576040519150601f19603f3d011682016040523d82523d6000602084013e612620565b606091505b50909150505b6005546040516370a0823160e01b81526001600160a01b03868116600483015260009216906370a0823190602401602060405180830381865afa158015612671573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126959190614116565b90508115806126ad57506126a984846142a0565b8114155b156126cb5760405163da67ce5960e01b815260040160405180910390fd5b5050505050565b60006126dd83611a25565b6001600160a01b038416600090815260036020526040902042600490910155905061270882826142a0565b6001600160a01b039093166000908152600360205260409020600701929092555050565b805160005b61273c6001836142a0565b811015610ca85760006127508260016142b3565b90505b828110156127d15783818151811061276d5761276d6140d1565b60200260200101516001600160a01b0316848381518110612790576127906140d1565b60200260200101516001600160a01b0316036127bf57604051636e7c990560e01b815260040160405180910390fd5b806127c9816140fd565b915050612753565b50806127dc816140fd565b915050612731565b825184511415806127f757508151845114155b1561281557604051633f4e2f2960e21b815260040160405180910390fd5b6000805b8551811015612a085760096000878381518110612838576128386140d1565b6020908102919091018101516001600160a01b0316825281019190915260400160009081205460ff161515900361288257604051635cfb665160e01b815260040160405180910390fd5b858181518110612894576128946140d1565b60200260200101516001600160a01b031663a4cbec738683815181106128bc576128bc6140d1565b6020026020010151896128ce8b611f13565b8886815181106128e0576128e06140d1565b60200260200101518d6040516020016128f9919061412f565b6040516020818303038152906040526040518663ffffffff1660e01b815260040161292794939291906141bf565b60206040518083038185885af1158015612945573d6000803e3d6000fd5b50505050506040513d601f19601f8201168201806040525081019061296a9190614116565b5087608001517f5a94fe36b9c96bbd30dafe9eb364e4d1ac0751c6f8c114bd0977952694149c078783815181106129a3576129a36140d1565b60200260200101516040516129c791906001600160a01b0391909116815260200190565b60405180910390a28481815181106129e1576129e16140d1565b6020026020010151826129f491906142b3565b915080612a00816140fd565b915050612819565b508181146111b65760405163c0a890d760e01b815260040160405180910390fd5b612a3161340b565b6001805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a1565b83838114612aed574260025484612a9291906142b3565b11612a9e575082612aed565b4260025484612aad91906142b3565b1115612aed576000612abf84426142a0565b90506000612acd84836142c6565b612ad790846142b3565b9050858111612ae65780612ae8565b855b925050505b949350505050565b6000612b00836114a0565b6001600160a01b03841660009081526003602052604090204290559050612b2782826142a0565b6001600160a01b03909316600090815260036020819052604090912001929092555050565b60055460408051600481526024810182526020810180516001600160e01b03166333662b8160e11b179052905160009283926001600160a01b0390911691612b949190614284565b6000604051808303816000865af19150503d8060008114612bd1576040519150601f19603f3d011682016040523d82523d6000602084013e612bd6565b606091505b5091509150818015612be85750805115155b15612ffc57600081806020019051810190612c0391906142dd565b90506001600160a01b03811615612ffa576005546040516370a0823160e01b81523060048201526000916001600160a01b0316906370a0823190602401602060405180830381865afa158015612c5d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c819190614116565b9050612c8d3086613002565b6005546040516370a0823160e01b815230600482015260009183916001600160a01b03909116906370a0823190602401602060405180830381865afa158015612cda573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612cfe9190614116565b612d0891906142a0565b60408051600481526024810182526020810180516001600160e01b0316633312a88160e21b179052905191925060009182916001600160a01b03871691612d4f9190614284565b6000604051808303816000865af19150503d8060008114612d8c576040519150601f19603f3d011682016040523d82523d6000602084013e612d91565b606091505b506005546040516001600160a01b03898116602483015260448201889052939550919350919091169060640160408051601f198184030181529181526020820180516001600160e01b031663095ea7b360e01b17905251612df29190614284565b6000604051808303816000865af19150503d8060008114612e2f576040519150601f19603f3d011682016040523d82523d6000602084013e612e34565b606091505b5090975050861580612e44575081155b80612ec05750600554604051636eb1769f60e11b81523060048201526001600160a01b0387811660248301528592169063dd62ed3e90604401602060405180830381865afa158015612e9a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ebe9190614116565b105b15612ee757600554612edc906001600160a01b03168a85613454565b505050505050505050565b600081806020019051810190612efd91906142dd565b90506001600160a01b038116612f3057600554612f24906001600160a01b03168b86613454565b50505050505050505050565b856001600160a01b031684604051602401612f4d91815260200190565b60408051601f198184030181529181526020820180516001600160e01b0316632e1a7d4d60e01b17905251612f829190614284565b6000604051808303816000865af19150503d8060008114612fbf576040519150601f19603f3d011682016040523d82523d6000602084013e612fc4565b606091505b50508098505087612fe657600554612f24906001600160a01b03168b86613454565b612f246001600160a01b0382168b86613454565b505b610c7d84845b6005546040516001600160a01b0384811660248301526044820184905260009216907f40c10f19000000000000000000000000000000000000000000000000000000009060640160408051601f198184030181529181526020820180516001600160e01b03166001600160e01b03199094169390931790925290516130879190614284565b6000604051808303816000865af19150503d80600081146130c4576040519150601f19603f3d011682016040523d82523d6000602084013e6130c9565b606091505b5050905080610ca857604051638d938d7b60e01b815260040160405180910390fd5b6130f36121ef565b6001805460ff1916811790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a25833612a5e565b61313360026000196142fa565b82118061314b575061314860026000196142fa565b81115b15613169576040516301e8cafb60e71b815260040160405180910390fd5b6131738383613484565b61317d8382613529565b60408051838152602081018390526001600160a01b038516917f93f3bbfe8cfb354ec059175107653f49f6eb479a8622a7d83866ea015435c9449101610b3f565b80518251146131e057604051633f4e2f2960e21b815260040160405180910390fd5b60005b8251811015610ca8578181815181106131fe576131fe6140d1565b60200260200101516008600085848151811061321c5761321c6140d1565b6020026020010151815260200190815260200160002060006101000a8154816001600160a01b0302191690836001600160a01b03160217905550818181518110613268576132686140d1565b60200260200101516001600160a01b03167f3083b3b95a46df5c4a6cdd1fa242b76f968ed75bf364ec15de70e5b043f628318483815181106132ac576132ac6140d1565b60200260200101516040516132c391815260200190565b60405180910390a2806132d5816140fd565b9150506131e3565b6132e782826119fc565b610d2c576132f4816135d3565b6132ff8360206135e5565b60405160200161331092919061431c565b60408051601f198184030181529082905262461bcd60e51b8252610d1991600401614391565b600061338b826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166137879092919063ffffffff16565b90508051600014806133ac5750808060200190518101906133ac91906143a4565b610ca85760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610d19565b60015460ff166122355760405162461bcd60e51b815260206004820152601460248201527314185d5cd8589b194e881b9bdd081c185d5cd95960621b6044820152606401610d19565b6040516001600160a01b038316602482015260448101829052610ca890849063a9059cbb60e01b9060640161226b565b6001600160a01b038216600090815260036020526040812060020154906134aa846114a0565b6001600160a01b038516600090815260036020526040902060020184905590506134d5838383613796565b6001600160a01b038516600090815260036020819052604090912001556002546134ff90846142fa565b6001600160a01b039094166000908152600360205260409020600181019490945550504290915550565b6001600160a01b0382166000908152600360205260408120600601549061354f84611a25565b6001600160a01b0385166000908152600360205260409020600601849055905061357a838383613796565b6001600160a01b0385166000908152600360205260409020600701556002546135a390846142fa565b6001600160a01b039094166000908152600360205260409020600581019490945550504260049092019190915550565b6060610ae26001600160a01b03831660145b606060006135f48360026142c6565b6135ff9060026142b3565b6001600160401b0381111561361657613616613a2e565b6040519080825280601f01601f191660200182016040528015613640576020820181803683370190505b509050600360fc1b8160008151811061365b5761365b6140d1565b60200101906001600160f81b031916908160001a905350600f60fb1b8160018151811061368a5761368a6140d1565b60200101906001600160f81b031916908160001a90535060006136ae8460026142c6565b6136b99060016142b3565b90505b6001811115613731576f181899199a1a9b1b9c1cb0b131b232b360811b85600f16601081106136ed576136ed6140d1565b1a60f81b828281518110613703576137036140d1565b60200101906001600160f81b031916908160001a90535060049490941c9361372a816143c1565b90506136bc565b5083156137805760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401610d19565b9392505050565b6060612aed84846000856137ed565b600080848411156137cc576137ab85856142a0565b90508083116137bb5760006137c5565b6137c581846142a0565b91506137e5565b6137d684866142a0565b90506137e281846142b3565b91505b509392505050565b60608247101561384e5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b6064820152608401610d19565b600080866001600160a01b0316858760405161386a9190614284565b60006040518083038185875af1925050503d80600081146138a7576040519150601f19603f3d011682016040523d82523d6000602084013e6138ac565b606091505b5091509150612ae88783838760608315613927578251600003613920576001600160a01b0385163b6139205760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610d19565b5081612aed565b612aed838381511561393c5781518083602001fd5b8060405162461bcd60e51b8152600401610d199190614391565b60006020828403121561396857600080fd5b5035919050565b60006020828403121561398157600080fd5b81356001600160e01b03198116811461378057600080fd5b6001600160a01b03811681146111f157600080fd5b6000602082840312156139c057600080fd5b813561378081613999565b80151581146111f157600080fd5b600080604083850312156139ec57600080fd5b8235915060208301356139fe816139cb565b809150509250929050565b60008060408385031215613a1c57600080fd5b8235915060208301356139fe81613999565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b0381118282101715613a6c57613a6c613a2e565b604052919050565b60006001600160401b03821115613a8d57613a8d613a2e565b5060051b60200190565b600082601f830112613aa857600080fd5b81356020613abd613ab883613a74565b613a44565b82815260059290921b84018101918181019086841115613adc57600080fd5b8286015b84811015613b00578035613af381613999565b8352918301918301613ae0565b509695505050505050565b60008060408385031215613b1e57600080fd5b82356001600160401b0380821115613b3557600080fd5b613b4186838701613a97565b9350602091508185013581811115613b5857600080fd5b85019050601f81018613613b6b57600080fd5b8035613b79613ab882613a74565b81815260059190911b82018301908381019088831115613b9857600080fd5b928401925b82841015613bbf578335613bb0816139cb565b82529284019290840190613b9d565b80955050505050509250929050565b600060208284031215613be057600080fd5b8135613780816139cb565b600082601f830112613bfc57600080fd5b81356020613c0c613ab883613a74565b82815260059290921b84018101918181019086841115613c2b57600080fd5b8286015b84811015613b005780358352918301918301613c2f565b600082601f830112613c5757600080fd5b81356001600160401b03811115613c7057613c70613a2e565b613c83601f8201601f1916602001613a44565b818152846020838601011115613c9857600080fd5b816020850160208301376000918101602001919091529392505050565b600082601f830112613cc657600080fd5b81356020613cd6613ab883613a74565b82815260059290921b84018101918181019086841115613cf557600080fd5b8286015b84811015613b005780356001600160401b03811115613d185760008081fd5b613d268986838b0101613c46565b845250918301918301613cf9565b600080600080600080600060e0888a031215613d4f57600080fd5b8735613d5a81613999565b9650602088013595506040880135613d71816139cb565b94506060880135935060808801356001600160401b0380821115613d9457600080fd5b613da08b838c01613a97565b945060a08a0135915080821115613db657600080fd5b613dc28b838c01613beb565b935060c08a0135915080821115613dd857600080fd5b50613de58a828b01613cb5565b91505092959891949750929550565b600080600060608486031215613e0957600080fd5b833592506020840135613e1b81613999565b915060408401356001600160401b03811115613e3657600080fd5b613e4286828701613c46565b9150509250925092565b60008060008060608587031215613e6257600080fd5b84356001600160401b0380821115613e7957600080fd5b818701915087601f830112613e8d57600080fd5b813581811115613e9c57600080fd5b886020828501011115613eae57600080fd5b6020928301965094505085013591506040850135613ecb81613999565b939692955090935050565b600080600060608486031215613eeb57600080fd5b8335613ef681613999565b95602085013595506040909401359392505050565b60008060408385031215613f1e57600080fd5b82356001600160401b0380821115613f3557600080fd5b613f4186838701613beb565b93506020850135915080821115613f5757600080fd5b50613f6485828601613a97565b9150509250929050565b82518152602080840151818301526040808501518184015260608086015181850152845160808501529184015160a084015283015160c083015282015160e08201526101008101613780565b60008060008060008060c08789031215613fd357600080fd5b8635613fde81613999565b9550602087013594506040870135613ff5816139cb565b935060608701359250608087013561400c81613999565b915060a08701356001600160401b0381111561402757600080fd5b61403389828a01613c46565b9150509295509295509295565b6000806000806080858703121561405657600080fd5b8435935060208501356001600160401b038082111561407457600080fd5b61408088838901613a97565b9450604087013591508082111561409657600080fd5b6140a288838901613beb565b935060608701359150808211156140b857600080fd5b506140c587828801613cb5565b91505092959194509250565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b60006001820161410f5761410f6140e7565b5060010190565b60006020828403121561412857600080fd5b5051919050565b81516001600160a01b0316815260208083015190820152604080830151151590820152606080830151908201526080918201519181019190915260a00190565b60005b8381101561418a578181015183820152602001614172565b50506000910152565b600081518084526141ab81602086016020860161416f565b601f01601f19169290920160200192915050565b8481526001600160a01b03841660208201526080604082018190526000906141e990830185614193565b82810360608401526141fb8185614193565b979650505050505050565b600060a0828403121561421857600080fd5b60405160a081018181106001600160401b038211171561423a5761423a613a2e565b604052823561424881613999565b8152602083810135908201526040830135614262816139cb565b6040820152606083810135908201526080928301359281019290925250919050565b6000825161429681846020870161416f565b9190910192915050565b81810381811115610ae257610ae26140e7565b80820180821115610ae257610ae26140e7565b8082028115828204841417610ae257610ae26140e7565b6000602082840312156142ef57600080fd5b815161378081613999565b60008261431757634e487b7160e01b600052601260045260246000fd5b500490565b7f416363657373436f6e74726f6c3a206163636f756e742000000000000000000081526000835161435481601785016020880161416f565b7001034b99036b4b9b9b4b733903937b6329607d1b601791840191820152835161438581602884016020880161416f565b01602801949350505050565b6020815260006137806020830184614193565b6000602082840312156143b657600080fd5b8151613780816139cb565b6000816143d0576143d06140e7565b50600019019056fea264697066735822122014555f41f27d588b1fdd35163b5e30a50cb4cb9721ea9ab185fb14f5b2e8b1c464736f6c63430008130033

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

000000000000000000000000909583f64948e4c6a9c7a9285c292e9d9302fb960000000000000000000000005db7ad07d4903579a0342f7332bd4e3ed480d1af0000000000000000000000005db7ad07d4903579a0342f7332bd4e3ed480d1af0000000000000000000000002ff4e5ff77e49eba45bffea2f75814d114f9c90400000000000000000000000051b206899bcfcbf4530412a43ed5c2336f91c90c0000000000000000000000000000000000000000000000000000000000015180000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000042000000000000000000000000000000000000000000000000000000000000004e000000000000000000000000000000000000000000000000000000000000005a040c10f190000000000000000000000000000000000000000000000000000000042966c6800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000a4b1000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000002105000000000000000000000000000000000000000000000000000000000000e7080000000000000000000000000000000000000000000000000000000000013e3100000000000000000000000000000000000000000000000000000000000000890000000000000000000000000000000000000000000000000000000000000d0a000000000000000000000000000000000000000000000000000000000000008200000000000000000000000000000000000000000000000000000000000138de0000000000000000000000000000000000000000000000000000000000001388000000000000000000000000000000000000000000000000000000000000009200000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000038000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000000df00000000000000000000000000000000000000000000000000000000000182320000000000000000000000000000000000000000000000000000000000000005000000000000000000000000cb8530f2b20f3c62602f01d3a78bcc58e9aea8530000000000000000000000005ef37628d45c80740fb6db7ed9c0a753b4f85263000000000000000000000000a9bd5559531fe372e866ad4337599962d5810e010000000000000000000000000ea1c09b884dc5758ed1d9bd006dceb20d31c439000000000000000000000000dea0243b5ee5200d1feba615eaa7385598417b8600000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000033b2e3c9fd0803ce80000000000000000000000000000000000000000000000033b2e3c9fd0803ce80000000000000000000000000000000000000000000000033b2e3c9fd0803ce80000000000000000000000000000000000000000000000033b2e3c9fd0803ce80000000000000000000000000000000000000000000000033b2e3c9fd0803ce800000000000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000033b2e3c9fd0803ce80000000000000000000000000000000000000000000000033b2e3c9fd0803ce80000000000000000000000000000000000000000000000033b2e3c9fd0803ce80000000000000000000000000000000000000000000000033b2e3c9fd0803ce80000000000000000000000000000000000000000000000033b2e3c9fd0803ce8000000

-----Decoded View---------------
Arg [0] : _addresses (address[5]): 0x909583f64948e4C6A9c7A9285C292E9d9302fb96,0x5dB7AD07D4903579A0342F7332Bd4E3eD480d1AF,0x5dB7AD07D4903579A0342F7332Bd4E3eD480d1AF,0x2FF4E5Ff77e49ebA45bffEa2F75814d114f9c904,0x51b206899BcFCBf4530412a43ed5C2336f91c90C
Arg [1] : _duration (uint256): 86400
Arg [2] : _minBridges (uint256): 0
Arg [3] : _multiBridgeAdapters (address[]):
Arg [4] : _chainId (uint256[]): 1,42161,10,8453,59144,81457,137,3338,130,80094,5000,146,288,56,100,223,98866
Arg [5] : _bridges (address[]): 0xCB8530f2B20F3c62602f01D3a78BCC58e9aEa853,0x5eF37628d45C80740fb6dB7eD9c0a753b4f85263,0xA9bD5559531fe372E866AD4337599962d5810E01,0x0EA1C09B884dc5758eD1D9Bd006DCeb20d31c439,0xdEA0243b5EE5200d1FEBA615eAa7385598417B86
Arg [6] : _mintingLimits (uint256[]): 1000000000000000000000000000,1000000000000000000000000000,1000000000000000000000000000,1000000000000000000000000000,1000000000000000000000000000
Arg [7] : _burningLimits (uint256[]): 1000000000000000000000000000,1000000000000000000000000000,1000000000000000000000000000,1000000000000000000000000000,1000000000000000000000000000
Arg [8] : _selectors (bytes4[2]): System.Byte[],System.Byte[]

-----Encoded View---------------
51 Constructor Arguments found :
Arg [0] : 000000000000000000000000909583f64948e4c6a9c7a9285c292e9d9302fb96
Arg [1] : 0000000000000000000000005db7ad07d4903579a0342f7332bd4e3ed480d1af
Arg [2] : 0000000000000000000000005db7ad07d4903579a0342f7332bd4e3ed480d1af
Arg [3] : 0000000000000000000000002ff4e5ff77e49eba45bffea2f75814d114f9c904
Arg [4] : 00000000000000000000000051b206899bcfcbf4530412a43ed5c2336f91c90c
Arg [5] : 0000000000000000000000000000000000000000000000000000000000015180
Arg [6] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [7] : 00000000000000000000000000000000000000000000000000000000000001c0
Arg [8] : 00000000000000000000000000000000000000000000000000000000000001e0
Arg [9] : 0000000000000000000000000000000000000000000000000000000000000420
Arg [10] : 00000000000000000000000000000000000000000000000000000000000004e0
Arg [11] : 00000000000000000000000000000000000000000000000000000000000005a0
Arg [12] : 40c10f1900000000000000000000000000000000000000000000000000000000
Arg [13] : 42966c6800000000000000000000000000000000000000000000000000000000
Arg [14] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [15] : 0000000000000000000000000000000000000000000000000000000000000011
Arg [16] : 0000000000000000000000000000000000000000000000000000000000000001
Arg [17] : 000000000000000000000000000000000000000000000000000000000000a4b1
Arg [18] : 000000000000000000000000000000000000000000000000000000000000000a
Arg [19] : 0000000000000000000000000000000000000000000000000000000000002105
Arg [20] : 000000000000000000000000000000000000000000000000000000000000e708
Arg [21] : 0000000000000000000000000000000000000000000000000000000000013e31
Arg [22] : 0000000000000000000000000000000000000000000000000000000000000089
Arg [23] : 0000000000000000000000000000000000000000000000000000000000000d0a
Arg [24] : 0000000000000000000000000000000000000000000000000000000000000082
Arg [25] : 00000000000000000000000000000000000000000000000000000000000138de
Arg [26] : 0000000000000000000000000000000000000000000000000000000000001388
Arg [27] : 0000000000000000000000000000000000000000000000000000000000000092
Arg [28] : 0000000000000000000000000000000000000000000000000000000000000120
Arg [29] : 0000000000000000000000000000000000000000000000000000000000000038
Arg [30] : 0000000000000000000000000000000000000000000000000000000000000064
Arg [31] : 00000000000000000000000000000000000000000000000000000000000000df
Arg [32] : 0000000000000000000000000000000000000000000000000000000000018232
Arg [33] : 0000000000000000000000000000000000000000000000000000000000000005
Arg [34] : 000000000000000000000000cb8530f2b20f3c62602f01d3a78bcc58e9aea853
Arg [35] : 0000000000000000000000005ef37628d45c80740fb6db7ed9c0a753b4f85263
Arg [36] : 000000000000000000000000a9bd5559531fe372e866ad4337599962d5810e01
Arg [37] : 0000000000000000000000000ea1c09b884dc5758ed1d9bd006dceb20d31c439
Arg [38] : 000000000000000000000000dea0243b5ee5200d1feba615eaa7385598417b86
Arg [39] : 0000000000000000000000000000000000000000000000000000000000000005
Arg [40] : 0000000000000000000000000000000000000000033b2e3c9fd0803ce8000000
Arg [41] : 0000000000000000000000000000000000000000033b2e3c9fd0803ce8000000
Arg [42] : 0000000000000000000000000000000000000000033b2e3c9fd0803ce8000000
Arg [43] : 0000000000000000000000000000000000000000033b2e3c9fd0803ce8000000
Arg [44] : 0000000000000000000000000000000000000000033b2e3c9fd0803ce8000000
Arg [45] : 0000000000000000000000000000000000000000000000000000000000000005
Arg [46] : 0000000000000000000000000000000000000000033b2e3c9fd0803ce8000000
Arg [47] : 0000000000000000000000000000000000000000033b2e3c9fd0803ce8000000
Arg [48] : 0000000000000000000000000000000000000000033b2e3c9fd0803ce8000000
Arg [49] : 0000000000000000000000000000000000000000033b2e3c9fd0803ce8000000
Arg [50] : 0000000000000000000000000000000000000000033b2e3c9fd0803ce8000000


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

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