MATIC Price: $0.755739 (+10.36%)
Gas: 75 GWei
 

Overview

MATIC Balance

Polygon PoS Chain LogoPolygon PoS Chain LogoPolygon PoS Chain Logo0 MATIC

MATIC Value

$0.00

Sponsored

Transaction Hash
Method
Block
From
To
Value
0x60806040409167232023-03-29 22:53:46418 days ago1680130426IN
 Create: BridgeFacet
0 MATIC0.9121378200

Parent Transaction Hash Block From To Value
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
BridgeFacet

Compiler Version
v0.8.17+commit.8df45f5f

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion
File 1 of 38 : BridgeFacet.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.17;

import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import {Address} from "@openzeppelin/contracts/utils/Address.sol";

import {ExcessivelySafeCall} from "../../../shared/libraries/ExcessivelySafeCall.sol";
import {TypedMemView} from "../../../shared/libraries/TypedMemView.sol";
import {TypeCasts} from "../../../shared/libraries/TypeCasts.sol";

import {IOutbox} from "../../../messaging/interfaces/IOutbox.sol";
import {IConnectorManager} from "../../../messaging/interfaces/IConnectorManager.sol";

import {BaseConnextFacet} from "./BaseConnextFacet.sol";

import {AssetLogic} from "../libraries/AssetLogic.sol";
import {ExecuteArgs, TransferInfo, DestinationTransferStatus, TokenConfig} from "../libraries/LibConnextStorage.sol";
import {BridgeMessage} from "../libraries/BridgeMessage.sol";
import {Constants} from "../libraries/Constants.sol";
import {TokenId} from "../libraries/TokenId.sol";

import {IXReceiver} from "../interfaces/IXReceiver.sol";
import {IAavePool} from "../interfaces/IAavePool.sol";
import {IBridgeToken} from "../interfaces/IBridgeToken.sol";

/**
 * @notice Defines the fields needed for an asset transfer
 * @param asset - The address of the asset
 * @param amount - The amount of the asset
 */
struct AssetTransfer {
  address asset;
  uint256 amount;
}

contract BridgeFacet is BaseConnextFacet {
  // ============ Libraries ============

  using TypedMemView for bytes;
  using TypedMemView for bytes29;
  using BridgeMessage for bytes29;
  using SafeERC20 for IERC20Metadata;

  // ========== Custom Errors ===========

  error BridgeFacet__addRemote_invalidRouter();
  error BridgeFacet__addRemote_invalidDomain();
  error BridgeFacet__onlyDelegate_notDelegate();
  error BridgeFacet__addSequencer_invalidSequencer();
  error BridgeFacet__addSequencer_alreadyApproved();
  error BridgeFacet__removeSequencer_notApproved();
  error BridgeFacet__setXAppConnectionManager_domainsDontMatch();
  error BridgeFacet__xcall_nativeAssetNotSupported();
  error BridgeFacet__xcall_emptyTo();
  error BridgeFacet__xcall_invalidSlippage();
  error BridgeFacet_xcall__emptyLocalAsset();
  error BridgeFacet__xcall_capReached();
  error BridgeFacet__execute_unapprovedSender();
  error BridgeFacet__execute_wrongDomain();
  error BridgeFacet__execute_notSupportedSequencer();
  error BridgeFacet__execute_invalidSequencerSignature();
  error BridgeFacet__execute_maxRoutersExceeded();
  error BridgeFacet__execute_notSupportedRouter();
  error BridgeFacet__execute_invalidRouterSignature();
  error BridgeFacet__execute_notApprovedForPortals();
  error BridgeFacet__execute_badFastLiquidityStatus();
  error BridgeFacet__execute_notReconciled();
  error BridgeFacet__execute_externalCallFailed();
  error BridgeFacet__excecute_insufficientGas();
  error BridgeFacet__executePortalTransfer_insufficientAmountWithdrawn();
  error BridgeFacet__bumpTransfer_valueIsZero();
  error BridgeFacet__bumpTransfer_noRelayerVault();
  error BridgeFacet__forceUpdateSlippage_invalidSlippage();
  error BridgeFacet__forceUpdateSlippage_notDestination();
  error BridgeFacet__forceReceiveLocal_notDestination();
  error BridgeFacet__mustHaveRemote_destinationNotSupported();

  // ============ Properties ============

  // ============ Events ============

  /**
   * @notice Emitted when `xcall` is called on the origin domain of a transfer.
   * @param transferId - The unique identifier of the crosschain transfer.
   * @param nonce - The bridge nonce of the transfer on the origin domain.
   * @param messageHash - The hash of the message bytes (containing all transfer info) that were bridged.
   * @param params - The `TransferInfo` provided to the function.
   * @param asset - The asset sent in with xcall
   * @param amount - The amount sent in with xcall
   * @param local - The local asset that is controlled by the bridge and can be burned/minted
   */
  event XCalled(
    bytes32 indexed transferId,
    uint256 indexed nonce,
    bytes32 indexed messageHash,
    TransferInfo params,
    address asset,
    uint256 amount,
    address local,
    bytes messageBody
  );

  /**
   * @notice Emitted when a transfer has its external data executed
   * @param transferId - The unique identifier of the crosschain transfer.
   * @param success - Whether calldata succeeded
   * @param returnData - Return bytes from the IXReceiver
   */
  event ExternalCalldataExecuted(bytes32 indexed transferId, bool success, bytes returnData);

  /**
   * @notice Emitted when `execute` is called on the destination domain of a transfer.
   * @dev `execute` may be called when providing fast liquidity or when processing a reconciled (slow) transfer.
   * @param transferId - The unique identifier of the crosschain transfer.
   * @param to - The recipient `TransferInfo.to` provided, created as indexed parameter.
   * @param asset - The asset the recipient is given or the external call is executed with. Should be the
   * adopted asset on that chain.
   * @param args - The `ExecuteArgs` provided to the function.
   * @param local - The local asset that was either supplied by the router for a fast-liquidity transfer or
   * minted by the bridge in a reconciled (slow) transfer. Could be the same as the adopted `asset` param.
   * @param amount - The amount of transferring asset the recipient address receives or the external call is
   * executed with.
   * @param caller - The account that called the function.
   */
  event Executed(
    bytes32 indexed transferId,
    address indexed to,
    address indexed asset,
    ExecuteArgs args,
    address local,
    uint256 amount,
    address caller
  );

  /**
   * @notice Emitted when `_bumpTransfer` is called by an user on the origin domain both in
   * `xcall` and `bumpTransfer`
   * @param transferId - The unique identifier of the crosschain transaction
   * @param increase - The additional amount fees increased by
   * @param asset - The asset the fee was increased with
   * @param caller - The account that called the function
   */
  event TransferRelayerFeesIncreased(bytes32 indexed transferId, uint256 increase, address asset, address caller);

  /**
   * @notice Emitted when `forceUpdateSlippage` is called by user-delegated EOA
   * on the destination domain
   * @param transferId - The unique identifier of the crosschain transaction
   * @param slippage - The updated slippage boundary
   */
  event SlippageUpdated(bytes32 indexed transferId, uint256 slippage);

  /**
   * @notice Emitted when `forceReceiveLocal` is called by a user-delegated EOA
   * on the destination domain
   * @param transferId - The unique identifier of the crosschain transaction
   */
  event ForceReceiveLocal(bytes32 indexed transferId);

  /**
   * @notice Emitted when a router used Aave Portal liquidity for fast transfer
   * @param transferId - The unique identifier of the crosschain transaction
   * @param router - The authorized router that used Aave Portal liquidity
   * @param asset - The asset that was provided by Aave Portal
   * @param amount - The amount of asset that was provided by Aave Portal
   */
  event AavePortalMintUnbacked(bytes32 indexed transferId, address indexed router, address asset, uint256 amount);

  /**
   * @notice Emitted when a new remote instance is added
   * @param domain - The domain the remote instance is on
   * @param remote - The address of the remote instance
   * @param caller - The account that called the function
   */
  event RemoteAdded(uint32 domain, address remote, address caller);

  /**
   * @notice Emitted when a sequencer is added or removed from allowlists
   * @param sequencer - The sequencer address to be added or removed
   * @param caller - The account that called the function
   */
  event SequencerAdded(address sequencer, address caller);

  /**
   * @notice Emitted when a sequencer is added or removed from allowlists
   * @param sequencer - The sequencer address to be added or removed
   * @param caller - The account that called the function
   */
  event SequencerRemoved(address sequencer, address caller);

  /**
   * @notice Emitted `xAppConnectionManager` is updated
   * @param updated - The updated address
   * @param caller - The account that called the function
   */
  event XAppConnectionManagerSet(address updated, address caller);

  // ============ Modifiers ============

  /**
   * @notice Only accept a transfer's designated delegate.
   * @param _params The TransferInfo of the transfer.
   */
  modifier onlyDelegate(TransferInfo calldata _params) {
    if (_params.delegate != msg.sender) revert BridgeFacet__onlyDelegate_notDelegate();
    _;
  }

  // ============ Getters ============

  function routedTransfers(bytes32 _transferId) public view returns (address[] memory) {
    return s.routedTransfers[_transferId];
  }

  function transferStatus(bytes32 _transferId) public view returns (DestinationTransferStatus) {
    return s.transferStatus[_transferId];
  }

  function remote(uint32 _domain) public view returns (address) {
    return TypeCasts.bytes32ToAddress(s.remotes[_domain]);
  }

  function domain() public view returns (uint32) {
    return s.domain;
  }

  function nonce() public view returns (uint256) {
    return s.nonce;
  }

  function approvedSequencers(address _sequencer) external view returns (bool) {
    return s.approvedSequencers[_sequencer];
  }

  function xAppConnectionManager() public view returns (address) {
    return address(s.xAppConnectionManager);
  }

  // ============ Admin Functions ==============

  /**
   * @notice Used to add an approved sequencer to the allowlist.
   * @param _sequencer - The sequencer address to add.
   */
  function addSequencer(address _sequencer) external onlyOwnerOrAdmin {
    if (_sequencer == address(0)) revert BridgeFacet__addSequencer_invalidSequencer();

    if (s.approvedSequencers[_sequencer]) revert BridgeFacet__addSequencer_alreadyApproved();
    s.approvedSequencers[_sequencer] = true;

    emit SequencerAdded(_sequencer, msg.sender);
  }

  /**
   * @notice Used to remove an approved sequencer from the allowlist.
   * @param _sequencer - The sequencer address to remove.
   */
  function removeSequencer(address _sequencer) external onlyOwnerOrAdmin {
    if (!s.approvedSequencers[_sequencer]) revert BridgeFacet__removeSequencer_notApproved();
    delete s.approvedSequencers[_sequencer];

    emit SequencerRemoved(_sequencer, msg.sender);
  }

  /**
   * @notice Modify the contract the xApp uses to validate Replica contracts
   * @param _xAppConnectionManager The address of the xAppConnectionManager contract
   */
  function setXAppConnectionManager(address _xAppConnectionManager) external onlyOwnerOrAdmin {
    IConnectorManager manager = IConnectorManager(_xAppConnectionManager);
    if (manager.localDomain() != s.domain) {
      revert BridgeFacet__setXAppConnectionManager_domainsDontMatch();
    }
    emit XAppConnectionManagerSet(_xAppConnectionManager, msg.sender);
    s.xAppConnectionManager = manager;
  }

  /**
   * @notice Register the address of a Router contract for the same xApp on a remote chain
   * @param _domain The domain of the remote xApp Router
   * @param _router The address of the remote xApp Router
   */
  function enrollRemoteRouter(uint32 _domain, bytes32 _router) external onlyOwnerOrAdmin {
    if (_router == bytes32("")) revert BridgeFacet__addRemote_invalidRouter();

    // Make sure we aren't setting the current domain (or an empty one) as the connextion.
    if (_domain == 0 || _domain == s.domain) {
      revert BridgeFacet__addRemote_invalidDomain();
    }

    s.remotes[_domain] = _router;
    emit RemoteAdded(_domain, TypeCasts.bytes32ToAddress(_router), msg.sender);
  }

  // ============ Public Functions: Bridge ==============

  function xcall(
    uint32 _destination,
    address _to,
    address _asset,
    address _delegate,
    uint256 _amount,
    uint256 _slippage,
    bytes calldata _callData
  ) external payable nonXCallReentrant returns (bytes32) {
    // NOTE: Here, we fill in as much information as we can for the TransferInfo.
    // Some info is left blank and will be assigned in the internal `_xcall` function (e.g.
    // `normalizedIn`, `bridgedAmt`, canonical info, etc).
    TransferInfo memory params = TransferInfo({
      to: _to,
      callData: _callData,
      originDomain: s.domain,
      destinationDomain: _destination,
      delegate: _delegate,
      // `receiveLocal: false` indicates we should always deliver the adopted asset on the
      // destination chain, swapping from the local asset if needed.
      receiveLocal: false,
      slippage: _slippage,
      originSender: msg.sender,
      // The following values should be assigned in _xcall.
      nonce: 0,
      canonicalDomain: 0,
      bridgedAmt: 0,
      normalizedIn: 0,
      canonicalId: bytes32(0)
    });
    return _xcall(params, AssetTransfer(_asset, _amount), AssetTransfer(address(0), msg.value));
  }

  function xcallIntoLocal(
    uint32 _destination,
    address _to,
    address _asset,
    address _delegate,
    uint256 _amount,
    uint256 _slippage,
    bytes calldata _callData
  ) external payable nonXCallReentrant returns (bytes32) {
    // NOTE: Here, we fill in as much information as we can for the TransferInfo.
    // Some info is left blank and will be assigned in the internal `_xcall` function (e.g.
    // `normalizedIn`, `bridgedAmt`, canonical info, etc).
    TransferInfo memory params = TransferInfo({
      to: _to,
      callData: _callData,
      originDomain: s.domain,
      destinationDomain: _destination,
      delegate: _delegate,
      // `receiveLocal: true` indicates we should always deliver the local asset on the
      // destination chain, and NOT swap into any adopted assets.
      receiveLocal: true,
      slippage: _slippage,
      originSender: msg.sender,
      // The following values should be assigned in _xcall.
      nonce: 0,
      canonicalDomain: 0,
      bridgedAmt: 0,
      normalizedIn: 0,
      canonicalId: bytes32(0)
    });
    return _xcall(params, AssetTransfer(_asset, _amount), AssetTransfer(address(0), msg.value));
  }

  function xcall(
    uint32 _destination,
    address _to,
    address _asset,
    address _delegate,
    uint256 _amount,
    uint256 _slippage,
    bytes calldata _callData,
    uint256 _relayerFee
  ) external nonXCallReentrant returns (bytes32) {
    // NOTE: Here, we fill in as much information as we can for the TransferInfo.
    // Some info is left blank and will be assigned in the internal `_xcall` function (e.g.
    // `normalizedIn`, `bridgedAmt`, canonical info, etc).
    TransferInfo memory params = TransferInfo({
      to: _to,
      callData: _callData,
      originDomain: s.domain,
      destinationDomain: _destination,
      delegate: _delegate,
      // `receiveLocal: false` indicates we should always deliver the adopted asset on the
      // destination chain, swapping from the local asset if needed.
      receiveLocal: false,
      slippage: _slippage,
      originSender: msg.sender,
      // The following values should be assigned in _xcall.
      nonce: 0,
      canonicalDomain: 0,
      bridgedAmt: 0,
      normalizedIn: 0,
      canonicalId: bytes32(0)
    });
    return _xcall(params, AssetTransfer(_asset, _amount), AssetTransfer(_asset, _relayerFee));
  }

  function xcallIntoLocal(
    uint32 _destination,
    address _to,
    address _asset,
    address _delegate,
    uint256 _amount,
    uint256 _slippage,
    bytes calldata _callData,
    uint256 _relayerFee
  ) external nonXCallReentrant returns (bytes32) {
    // NOTE: Here, we fill in as much information as we can for the TransferInfo.
    // Some info is left blank and will be assigned in the internal `_xcall` function (e.g.
    // `normalizedIn`, `bridgedAmt`, canonical info, etc).
    TransferInfo memory params = TransferInfo({
      to: _to,
      callData: _callData,
      originDomain: s.domain,
      destinationDomain: _destination,
      delegate: _delegate,
      // `receiveLocal: true` indicates we should always deliver the local asset on the
      // destination chain, and NOT swap into any adopted assets.
      receiveLocal: true,
      slippage: _slippage,
      originSender: msg.sender,
      // The following values should be assigned in _xcall.
      nonce: 0,
      canonicalDomain: 0,
      bridgedAmt: 0,
      normalizedIn: 0,
      canonicalId: bytes32(0)
    });
    return _xcall(params, AssetTransfer(_asset, _amount), AssetTransfer(_asset, _relayerFee));
  }

  /**
   * @notice Called on a destination domain to disburse correct assets to end recipient and execute any included
   * calldata.
   *
   * @dev Can be called before or after `handle` [reconcile] is called (regarding the same transfer), depending on
   * whether the fast liquidity route (i.e. funds provided by routers) is being used for this transfer. As a result,
   * executed calldata (including properties like `originSender`) may or may not be verified depending on whether the
   * reconcile has been completed (i.e. the optimistic confirmation period has elapsed).
   *
   * @param _args - ExecuteArgs arguments.
   * @return bytes32 - The transfer ID of the crosschain transfer. Should match the xcall's transfer ID in order for
   * reconciliation to occur.
   */
  function execute(ExecuteArgs calldata _args) external nonReentrant whenNotPaused returns (bytes32) {
    (bytes32 transferId, DestinationTransferStatus status) = _executeSanityChecks(_args);

    DestinationTransferStatus updated = status == DestinationTransferStatus.Reconciled
      ? DestinationTransferStatus.Completed
      : DestinationTransferStatus.Executed;

    s.transferStatus[transferId] = updated;

    // Supply assets to target recipient. Use router liquidity when this is a fast transfer, or mint bridge tokens
    // when this is a slow transfer.
    // NOTE: Asset will be adopted unless specified to `receiveLocal` in params.
    (uint256 amountOut, address asset, address local) = _handleExecuteLiquidity(
      transferId,
      AssetLogic.calculateCanonicalHash(_args.params.canonicalId, _args.params.canonicalDomain),
      updated != DestinationTransferStatus.Completed,
      _args
    );

    // Execute the transaction using the designated calldata.
    uint256 amount = _handleExecuteTransaction(
      _args,
      amountOut,
      asset,
      transferId,
      updated == DestinationTransferStatus.Completed
    );

    // Emit event.
    emit Executed(transferId, _args.params.to, asset, _args, local, amount, msg.sender);

    return transferId;
  }

  /**
   * @notice Anyone can call this function on the origin domain to increase the relayer fee for a transfer.
   * @param _transferId - The unique identifier of the crosschain transaction
   */
  function bumpTransfer(bytes32 _transferId) external payable nonReentrant whenNotPaused {
    if (msg.value == 0) revert BridgeFacet__bumpTransfer_valueIsZero();
    _bumpTransfer(_transferId, address(0), msg.value);
  }

  /**
   * @notice Anyone can call this function on the origin domain t o increase the relayer fee for
   * a given transfer using a specific asset.
   * @param _transferId - The unique identifier of the crosschain transaction
   * @param _relayerFeeAsset - The asset you are bumping fee with
   * @param _relayerFee - The amount you want to bump transfer fee with
   */
  function bumpTransfer(
    bytes32 _transferId,
    address _relayerFeeAsset,
    uint256 _relayerFee
  ) external nonReentrant whenNotPaused {
    if (_relayerFee == 0) revert BridgeFacet__bumpTransfer_valueIsZero();
    // check that the asset is whitelisted (the following reverts if asset
    // is not approved)
    _getApprovedCanonicalId(_relayerFeeAsset);
    // handle transferring asset to the relayer fee vault
    _bumpTransfer(_transferId, _relayerFeeAsset, _relayerFee);
  }

  /**
   * @notice Allows a user-specified account to update the slippage they are willing
   * to take on destination transfers.
   *
   * @param _params TransferInfo associated with the transfer
   * @param _slippage The updated slippage
   */
  function forceUpdateSlippage(TransferInfo calldata _params, uint256 _slippage) external onlyDelegate(_params) {
    // Sanity check slippage
    if (_slippage > Constants.BPS_FEE_DENOMINATOR) {
      revert BridgeFacet__forceUpdateSlippage_invalidSlippage();
    }

    // Should only be called on destination domain
    if (_params.destinationDomain != s.domain) {
      revert BridgeFacet__forceUpdateSlippage_notDestination();
    }

    // Get transferId
    bytes32 transferId = _calculateTransferId(_params);

    // Store overrides
    s.slippage[transferId] = _slippage;

    // Emit event
    emit SlippageUpdated(transferId, _slippage);
  }

  /**
   * @notice Allows a user-specified account to withdraw the local asset directly
   * @dev Calldata will still be executed with the local asset. `IXReceiver` contracts
   * should be able to handle local assets in event of failures.
   * @param _params TransferInfo associated with the transfer
   */
  function forceReceiveLocal(TransferInfo calldata _params) external onlyDelegate(_params) {
    // Should only be called on destination domain
    if (_params.destinationDomain != s.domain) {
      revert BridgeFacet__forceReceiveLocal_notDestination();
    }

    // Get transferId
    bytes32 transferId = _calculateTransferId(_params);

    // Store overrides
    s.receiveLocalOverride[transferId] = true;

    // Emit event
    emit ForceReceiveLocal(transferId);
  }

  // ============ Internal: Bridge ============

  /**
   * @notice Initiates a cross-chain transfer of funds and/or calldata
   *
   * @dev For ERC20 transfers, this contract must have approval to transfer the input (transacting) assets. The adopted
   * assets will be swapped for their local asset counterparts (i.e. bridgeable tokens) via the configured AMM if
   * necessary. In the event that the adopted assets *are* local bridge assets, no swap is needed. The local tokens will
   * then be sent via the bridge router. If the local assets are representational for an asset on another chain, we will
   * burn the tokens here. If the local assets are canonical (meaning that the adopted<>local asset pairing is native
   * to this chain), we will custody the tokens here.
   *
   * @param _params - The TransferInfo arguments.
   * @return bytes32 - The transfer ID of the newly created crosschain transfer.
   */
  function _xcall(
    TransferInfo memory _params,
    AssetTransfer memory _asset,
    AssetTransfer memory _relayer
  )
    internal
    // address _asset,
    // uint256 _amount,
    // address _relayerFeeAsset,
    // uint256 _relayerFee
    whenNotPaused
    returns (bytes32)
  {
    // Sanity checks.
    bytes32 remoteInstance;
    {
      // Not native asset.
      // NOTE: We support using address(0) as an intuitive default if you are sending a 0-value
      // transfer. In that edge case, address(0) will not be registered as a supported asset, but should
      // pass the `isLocalOrigin` check
      if (_asset.asset == address(0) && _asset.amount != 0) {
        revert BridgeFacet__xcall_nativeAssetNotSupported();
      }

      // Destination domain is supported.
      // NOTE: This check implicitly also checks that `_params.destinationDomain != s.domain`, because the index
      // `s.domain` of `s.remotes` should always be `bytes32(0)`.
      remoteInstance = _mustHaveRemote(_params.destinationDomain);

      // Recipient defined.
      if (_params.to == address(0)) {
        revert BridgeFacet__xcall_emptyTo();
      }

      if (_params.slippage > Constants.BPS_FEE_DENOMINATOR) {
        revert BridgeFacet__xcall_invalidSlippage();
      }
    }

    // NOTE: The local asset will stay address(0) if input asset is address(0) in the event of a
    // 0-value transfer. Otherwise, the local address will be retrieved below
    address local;
    bytes32 transferId;
    TokenId memory canonical;
    bool isCanonical;
    {
      // Check that the asset is supported -- can be either adopted or local.
      // NOTE: Above we check that you can only have `address(0)` as the input asset if this is a
      // 0-value transfer. Because 0-value transfers short-circuit all checks on mappings keyed on
      // hash(canonicalId, canonicalDomain), this is safe even when the address(0) asset is not
      // allowlisted.
      if (_asset.asset != address(0)) {
        // Retrieve the canonical token information.
        bytes32 key;
        (canonical, key) = _getApprovedCanonicalId(_asset.asset);

        // Get the token config.
        TokenConfig storage config = AssetLogic.getConfig(key);

        // Set boolean flag
        isCanonical = _params.originDomain == canonical.domain;

        // Get the local address
        local = isCanonical ? TypeCasts.bytes32ToAddress(canonical.id) : config.representation;
        if (local == address(0)) {
          revert BridgeFacet_xcall__emptyLocalAsset();
        }

        {
          // Enforce liquidity caps.
          // NOTE: Safe to do this before the swap because canonical domains do
          // not hit the AMMs (local == canonical).
          uint256 cap = config.cap;
          if (isCanonical && cap > 0) {
            // NOTE: this method includes router liquidity as part of the caps,
            // not only the minted amount
            uint256 newCustodiedAmount = config.custodied + _asset.amount;
            if (newCustodiedAmount > cap) {
              revert BridgeFacet__xcall_capReached();
            }
            s.tokenConfigs[key].custodied = newCustodiedAmount;
          }
        }

        // Update TransferInfo to reflect the canonical token information.
        _params.canonicalDomain = canonical.domain;
        _params.canonicalId = canonical.id;

        if (_asset.amount > 0) {
          // Transfer funds of input asset to the contract from the user.
          AssetLogic.handleIncomingAsset(_asset.asset, _asset.amount);

          // Swap to the local asset from adopted if applicable.
          _params.bridgedAmt = AssetLogic.swapToLocalAssetIfNeeded(
            key,
            _asset.asset,
            local,
            _asset.amount,
            _params.slippage
          );

          // Get the normalized amount in (amount sent in by user in 18 decimals).
          // NOTE: when getting the decimals from `_asset`, you don't know if you are looking for
          // adopted or local assets
          _params.normalizedIn = AssetLogic.normalizeDecimals(
            _asset.asset == local ? config.representationDecimals : config.adoptedDecimals,
            Constants.DEFAULT_NORMALIZED_DECIMALS,
            _asset.amount
          );
        }
      }

      // Calculate the transfer ID.
      _params.nonce = s.nonce++;
      transferId = _calculateTransferId(_params);
    }

    // Handle the relayer fee.
    // NOTE: This has to be done *after* transferring in + swapping assets because
    // the transfer id uses the amount that is bridged (i.e. amount in local asset).
    if (_relayer.amount > 0) {
      _bumpTransfer(transferId, _relayer.asset, _relayer.amount);
    }

    // Send the crosschain message.
    _sendMessageAndEmit(
      transferId,
      _params,
      _asset.asset,
      _asset.amount,
      remoteInstance,
      canonical,
      local,
      isCanonical
    );

    return transferId;
  }

  /**
   * @notice An internal function to handle the bumping of transfers
   * @param _transferId - The unique identifier of the crosschain transaction
   * @param _relayerFeeAsset - The asset you are bumping fee with
   * @param _relayerFee - The amount you want to bump transfer fee with
   */
  function _bumpTransfer(bytes32 _transferId, address _relayerFeeAsset, uint256 _relayerFee) internal {
    address relayerVault = s.relayerFeeVault;
    if (relayerVault == address(0)) revert BridgeFacet__bumpTransfer_noRelayerVault();
    if (_relayerFeeAsset == address(0)) {
      Address.sendValue(payable(relayerVault), _relayerFee);
    } else {
      // Pull funds from user to this contract
      // NOTE: could transfer to `relayerFeeVault`, but that would be unintuitive for user
      // approvals
      AssetLogic.handleIncomingAsset(_relayerFeeAsset, _relayerFee);

      // Transfer asset to relayerVault.
      AssetLogic.handleOutgoingAsset(_relayerFeeAsset, relayerVault, _relayerFee);
    }

    emit TransferRelayerFeesIncreased(_transferId, _relayerFee, _relayerFeeAsset, msg.sender);
  }

  /**
   * @notice Holds the logic to recover the signer from an encoded payload.
   * @dev Will hash and convert to an eth signed message.
   * @param _signed The hash that was signed.
   * @param _sig The signature from which we will recover the signer.
   */
  function _recoverSignature(bytes32 _signed, bytes calldata _sig) internal pure returns (address) {
    // Recover
    return ECDSA.recover(ECDSA.toEthSignedMessageHash(_signed), _sig);
  }

  /**
   * @notice Performs some sanity checks for `execute`.
   * @dev Need this to prevent stack too deep.
   * @param _args ExecuteArgs that were passed in to the `execute` call.
   */
  function _executeSanityChecks(ExecuteArgs calldata _args) private view returns (bytes32, DestinationTransferStatus) {
    // If the sender is not approved relayer, revert
    if (!s.approvedRelayers[msg.sender] && msg.sender != _args.params.delegate) {
      revert BridgeFacet__execute_unapprovedSender();
    }

    // If this is not the destination domain revert
    if (_args.params.destinationDomain != s.domain) {
      revert BridgeFacet__execute_wrongDomain();
    }

    // Path length refers to the number of facilitating routers. A transfer is considered 'multipath'
    // if multiple routers provide liquidity (in even 'shares') for it.
    uint256 pathLength = _args.routers.length;

    // Derive transfer ID based on given arguments.
    bytes32 transferId = _calculateTransferId(_args.params);

    // Retrieve the reconciled record.
    DestinationTransferStatus status = s.transferStatus[transferId];

    if (pathLength != 0) {
      // Make sure number of routers is below the configured maximum.
      if (pathLength > s.maxRoutersPerTransfer) revert BridgeFacet__execute_maxRoutersExceeded();

      // Check to make sure the transfer has not been reconciled (no need for routers if the transfer is
      // already reconciled; i.e. if there are routers provided, the transfer must *not* be reconciled).
      if (status != DestinationTransferStatus.None) revert BridgeFacet__execute_badFastLiquidityStatus();

      // NOTE: The sequencer address may be empty and no signature needs to be provided in the case of the
      // slow liquidity route (i.e. no routers involved). Additionally, the sequencer does not need to be the
      // msg.sender.
      // Check to make sure the sequencer address provided is approved
      if (!s.approvedSequencers[_args.sequencer]) {
        revert BridgeFacet__execute_notSupportedSequencer();
      }
      // Check to make sure the sequencer provided did sign the transfer ID and router path provided.
      // NOTE: when caps are enforced, this signature also acts as protection from malicious routers looking
      // to block the network. routers could `execute` a fake transaction, and use up the rest of the `custodied`
      // bandwidth, causing future `execute`s to fail. this would also cause a break in the accounting, where the
      // `custodied` balance no longer tracks representation asset minting / burning
      if (
        _args.sequencer != _recoverSignature(keccak256(abi.encode(transferId, _args.routers)), _args.sequencerSignature)
      ) {
        revert BridgeFacet__execute_invalidSequencerSignature();
      }

      // Hash the payload for which each router should have produced a signature.
      // Each router should have signed the `transferId` (which implicitly signs call params,
      // amount, and tokenId) as well as the `pathLength`, or the number of routers with which
      // they are splitting liquidity provision.
      bytes32 routerHash = keccak256(abi.encode(transferId, pathLength));

      for (uint256 i; i < pathLength; ) {
        // Make sure the router is approved, if applicable.
        // If router ownership is renounced (_RouterOwnershipRenounced() is true), then the router allowlist
        // no longer applies and we can skip this approval step.
        if (!_isRouterAllowlistRemoved() && !s.routerConfigs[_args.routers[i]].approved) {
          revert BridgeFacet__execute_notSupportedRouter();
        }

        // Validate the signature. We'll recover the signer's address using the expected payload and basic ECDSA
        // signature scheme recovery. The address for each signature must match the router's address.
        if (_args.routers[i] != _recoverSignature(routerHash, _args.routerSignatures[i])) {
          revert BridgeFacet__execute_invalidRouterSignature();
        }

        unchecked {
          ++i;
        }
      }
    } else {
      // If there are no routers for this transfer, this `execute` must be a slow liquidity route; in which
      // case, we must make sure the transfer's been reconciled.
      if (status != DestinationTransferStatus.Reconciled) revert BridgeFacet__execute_notReconciled();
    }

    return (transferId, status);
  }

  /**
   * @notice Calculates fast transfer amount.
   * @param _amount Transfer amount
   * @param _numerator Numerator
   * @param _denominator Denominator
   */
  function _muldiv(uint256 _amount, uint256 _numerator, uint256 _denominator) private pure returns (uint256) {
    return (_amount * _numerator) / _denominator;
  }

  /**
   * @notice Execute liquidity process used when calling `execute`.
   * @dev Will revert with underflow if any router in the path has insufficient liquidity to provide
   * for the transfer.
   * @dev Need this to prevent stack too deep.
   */
  function _handleExecuteLiquidity(
    bytes32 _transferId,
    bytes32 _key,
    bool _isFast,
    ExecuteArgs calldata _args
  ) private returns (uint256, address, address) {
    // Save the addresses of all routers providing liquidity for this transfer.
    s.routedTransfers[_transferId] = _args.routers;

    // Get the local asset contract address (if applicable).
    address local;
    if (_args.params.canonicalDomain != 0) {
      local = _getLocalAsset(_key, _args.params.canonicalId, _args.params.canonicalDomain);
    }

    // If this is a zero-value transfer, short-circuit remaining logic.
    if (_args.params.bridgedAmt == 0) {
      return (0, local, local);
    }

    // Get the receive local status
    bool receiveLocal = _args.params.receiveLocal || s.receiveLocalOverride[_transferId];

    uint256 toSwap = _args.params.bridgedAmt;
    // If this is a fast liquidity path, we should handle deducting from applicable routers' liquidity.
    // If this is a slow liquidity path, the transfer must have been reconciled (if we've reached this point),
    // and the funds would have been custodied in this contract. The exact custodied amount is untracked in state
    // (since the amount is hashed in the transfer ID itself) - thus, no updates are required.
    if (_isFast) {
      uint256 pathLen = _args.routers.length;

      // Calculate amount that routers will provide with the fast-liquidity fee deducted.
      toSwap = _muldiv(_args.params.bridgedAmt, s.LIQUIDITY_FEE_NUMERATOR, Constants.BPS_FEE_DENOMINATOR);

      if (pathLen == 1) {
        // If router does not have enough liquidity, try to use Aave Portals.
        // NOTE: Only one router should be responsible for taking on this credit risk, and it should only deal
        // with transfers expecting adopted assets (to avoid introducing runtime slippage).
        if (!receiveLocal && s.routerBalances[_args.routers[0]][local] < toSwap && s.aavePool != address(0)) {
          if (!s.routerConfigs[_args.routers[0]].portalApproved) revert BridgeFacet__execute_notApprovedForPortals();

          // Portals deliver the adopted asset directly; return after portal execution is completed.
          (uint256 portalDeliveredAmount, address adoptedAsset) = _executePortalTransfer(
            _transferId,
            _key,
            toSwap,
            _args.routers[0]
          );
          return (portalDeliveredAmount, adoptedAsset, local);
        } else {
          // Decrement the router's liquidity.
          s.routerBalances[_args.routers[0]][local] -= toSwap;
        }
      } else {
        // For each router, assert they are approved, and deduct liquidity.
        uint256 routerAmount = toSwap / pathLen;
        for (uint256 i; i < pathLen - 1; ) {
          // Decrement router's liquidity.
          // NOTE: If any router in the path has insufficient liquidity, this will revert with an underflow error.
          s.routerBalances[_args.routers[i]][local] -= routerAmount;

          unchecked {
            ++i;
          }
        }
        // The last router in the multipath will sweep the remaining balance to account for remainder dust.
        uint256 toSweep = routerAmount + (toSwap % pathLen);
        s.routerBalances[_args.routers[pathLen - 1]][local] -= toSweep;
      }
    }

    // If it is the canonical domain, decrease custodied value
    if (s.domain == _args.params.canonicalDomain && AssetLogic.getConfig(_key).cap > 0) {
      // NOTE: safe to use the amount here instead of post-swap because there are no
      // AMMs on the canonical domain (assuming canonical == adopted on canonical domain)
      s.tokenConfigs[_key].custodied -= toSwap;
    }

    // If the local asset is specified, or the adopted asset was overridden (e.g. when user facing slippage
    // conditions outside of their boundaries), exit without swapping.
    if (receiveLocal) {
      // Delete override
      delete s.receiveLocalOverride[_transferId];

      return (toSwap, local, local);
    }

    // Swap out of representational asset into adopted asset if needed.
    uint256 slippageOverride = s.slippage[_transferId];
    // delete for gas refund
    delete s.slippage[_transferId];

    (uint256 amount, address adopted) = AssetLogic.swapFromLocalAssetIfNeeded(
      _key,
      local,
      toSwap,
      slippageOverride != 0 ? slippageOverride : _args.params.slippage,
      _args.params.normalizedIn
    );
    return (amount, adopted, local);
  }

  /**
   * @notice Process the transfer, and calldata if needed, when calling `execute`
   * @dev Need this to prevent stack too deep
   */
  function _handleExecuteTransaction(
    ExecuteArgs calldata _args,
    uint256 _amountOut,
    address _asset, // adopted (or local if specified)
    bytes32 _transferId,
    bool _reconciled
  ) private returns (uint256) {
    // transfer funds to recipient
    AssetLogic.handleOutgoingAsset(_asset, _args.params.to, _amountOut);

    // execute the calldata
    _executeCalldata(_transferId, _amountOut, _asset, _reconciled, _args.params);

    return _amountOut;
  }

  /**
   * @notice Executes external calldata.
   * 
   * @dev Once a transfer is reconciled (i.e. data is authenticated), external calls will
   * fail gracefully. This means errors will be emitted in an event, but the function itself
   * will not revert.

   * In the case where a transaction is *not* reconciled (i.e. data is unauthenticated), this
   * external call will fail loudly. This allows all functions that rely on authenticated data
   * (using a specific check on the origin sender), to be forced into the slow path for
   * execution to succeed.
   * 
   */
  function _executeCalldata(
    bytes32 _transferId,
    uint256 _amount,
    address _asset,
    bool _reconciled,
    TransferInfo calldata _params
  ) internal {
    // execute the calldata
    if (keccak256(_params.callData) == Constants.EMPTY_HASH) {
      // no call data, return amount out
      return;
    }

    (bool success, bytes memory returnData) = ExcessivelySafeCall.excessivelySafeCall(
      _params.to,
      gasleft() - Constants.EXECUTE_CALLDATA_RESERVE_GAS,
      0, // native asset value (always 0)
      Constants.DEFAULT_COPY_BYTES, // only copy 256 bytes back as calldata
      abi.encodeWithSelector(
        IXReceiver.xReceive.selector,
        _transferId,
        _amount,
        _asset,
        _reconciled ? _params.originSender : address(0), // use passed in value iff authenticated
        _params.originDomain,
        _params.callData
      )
    );

    if (!_reconciled && !success) {
      // See above devnote, reverts if unsuccessful on fast path
      revert BridgeFacet__execute_externalCallFailed();
    }

    emit ExternalCalldataExecuted(_transferId, success, returnData);
  }

  /**
   * @notice Uses Aave Portals to provide fast liquidity
   */
  function _executePortalTransfer(
    bytes32 _transferId,
    bytes32 _key,
    uint256 _fastTransferAmount,
    address _router
  ) internal returns (uint256, address) {
    // Calculate local to adopted swap output if needed
    address adopted = _getAdoptedAsset(_key);

    IAavePool(s.aavePool).mintUnbacked(adopted, _fastTransferAmount, address(this), Constants.AAVE_REFERRAL_CODE);

    // Improvement: Instead of withdrawing to address(this), withdraw directly to the user or executor to save 1 transfer
    uint256 amountWithdrawn = IAavePool(s.aavePool).withdraw(adopted, _fastTransferAmount, address(this));

    if (amountWithdrawn < _fastTransferAmount) revert BridgeFacet__executePortalTransfer_insufficientAmountWithdrawn();

    // Store principle debt
    s.portalDebt[_transferId] = _fastTransferAmount;

    // Store fee debt
    s.portalFeeDebt[_transferId] = (s.aavePortalFeeNumerator * _fastTransferAmount) / Constants.BPS_FEE_DENOMINATOR;

    emit AavePortalMintUnbacked(_transferId, _router, adopted, _fastTransferAmount);

    return (_fastTransferAmount, adopted);
  }

  // ============ Internal: Send & Emit Xcalled============

  /**
   * @notice Format and send transfer message to a remote chain.
   *
   * @param _transferId Unique identifier for the transfer.
   * @param _params The TransferInfo.
   * @param _connextion The connext instance on the destination domain.
   * @param _canonical The canonical token ID/domain info.
   * @param _local The local token address.
   * @param _amount The token amount.
   * @param _isCanonical Whether or not the local token is the canonical asset (i.e. this is the token's
   * "home" chain).
   */
  function _sendMessageAndEmit(
    bytes32 _transferId,
    TransferInfo memory _params,
    address _asset,
    uint256 _amount,
    bytes32 _connextion,
    TokenId memory _canonical,
    address _local,
    bool _isCanonical
  ) private {
    // Remove tokens from circulation on this chain if applicable.
    uint256 bridgedAmt = _params.bridgedAmt;
    if (bridgedAmt > 0) {
      if (!_isCanonical) {
        // If the token originates on a remote chain, burn the representational tokens on this chain.
        IBridgeToken(_local).burn(address(this), bridgedAmt);
      }
      // IFF the token IS the canonical token (i.e. originates on this chain), we lock the input tokens in escrow
      // in this contract, as an equal amount of representational assets will be minted on the destination chain.
      // NOTE: The tokens should be in the contract already at this point from xcall.
    }

    bytes memory _messageBody = abi.encodePacked(
      _canonical.domain,
      _canonical.id,
      BridgeMessage.Types.Transfer,
      bridgedAmt,
      _transferId
    );

    // Send message to destination chain bridge router.
    // return message hash and unhashed body
    (bytes32 messageHash, bytes memory messageBody) = IOutbox(s.xAppConnectionManager.home()).dispatch(
      _params.destinationDomain,
      _connextion,
      _messageBody
    );

    // emit event
    emit XCalled(_transferId, _params.nonce, messageHash, _params, _asset, _amount, _local, messageBody);
  }

  /**
   * @notice Assert that the given domain has a xApp Router registered and return its address
   * @param _domain The domain of the chain for which to get the xApp Router
   * @return _remote The address of the remote xApp Router on _domain
   */
  function _mustHaveRemote(uint32 _domain) internal view returns (bytes32 _remote) {
    _remote = s.remotes[_domain];
    if (_remote == bytes32(0)) {
      revert BridgeFacet__mustHaveRemote_destinationNotSupported();
    }
  }
}

File 1 of 38 : OwnableUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)

pragma solidity ^0.8.0;

import "../utils/ContextUpgradeable.sol";
import "../proxy/utils/Initializable.sol";

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

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

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

    function __Ownable_init_unchained() internal onlyInitializing {
        _transferOwnership(_msgSender());
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

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

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
    }

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

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

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

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[49] private __gap;
}

File 1 of 38 : Initializable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (proxy/utils/Initializable.sol)

pragma solidity ^0.8.2;

import "../../utils/AddressUpgradeable.sol";

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

    /**
     * @dev Indicates that the contract is in the process of being initialized.
     */
    bool private _initializing;

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

    /**
     * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
     * `onlyInitializing` functions can be used to initialize parent contracts.
     *
     * Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a
     * constructor.
     *
     * Emits an {Initialized} event.
     */
    modifier initializer() {
        bool isTopLevelCall = !_initializing;
        require(
            (isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),
            "Initializable: contract is already initialized"
        );
        _initialized = 1;
        if (isTopLevelCall) {
            _initializing = true;
        }
        _;
        if (isTopLevelCall) {
            _initializing = false;
            emit Initialized(1);
        }
    }

    /**
     * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
     * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
     * used to initialize parent contracts.
     *
     * A reinitializer may be used after the original initialization step. This is essential to configure modules that
     * are added through upgrades and that require initialization.
     *
     * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
     * cannot be nested. If one is invoked in the context of another, execution will revert.
     *
     * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
     * a contract, executing them in the right order is up to the developer or operator.
     *
     * WARNING: setting the version to 255 will prevent any future reinitialization.
     *
     * Emits an {Initialized} event.
     */
    modifier reinitializer(uint8 version) {
        require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
        _initialized = version;
        _initializing = true;
        _;
        _initializing = false;
        emit Initialized(version);
    }

    /**
     * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
     * {initializer} and {reinitializer} modifiers, directly or indirectly.
     */
    modifier onlyInitializing() {
        require(_initializing, "Initializable: contract is not initializing");
        _;
    }

    /**
     * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
     * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
     * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
     * through proxies.
     *
     * Emits an {Initialized} event the first time it is successfully executed.
     */
    function _disableInitializers() internal virtual {
        require(!_initializing, "Initializable: contract is initializing");
        if (_initialized < type(uint8).max) {
            _initialized = type(uint8).max;
            emit Initialized(type(uint8).max);
        }
    }

    /**
     * @dev Internal function that returns the initialized version. Returns `_initialized`
     */
    function _getInitializedVersion() internal view returns (uint8) {
        return _initialized;
    }

    /**
     * @dev Internal function that returns the initialized version. Returns `_initializing`
     */
    function _isInitializing() internal view returns (bool) {
        return _initializing;
    }
}

File 1 of 38 : ERC20Upgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/ERC20.sol)

pragma solidity ^0.8.0;

import "./IERC20Upgradeable.sol";
import "./extensions/IERC20MetadataUpgradeable.sol";
import "../../utils/ContextUpgradeable.sol";
import "../../proxy/utils/Initializable.sol";

/**
 * @dev Implementation of the {IERC20} interface.
 *
 * This implementation is agnostic to the way tokens are created. This means
 * that a supply mechanism has to be added in a derived contract using {_mint}.
 * For a generic mechanism see {ERC20PresetMinterPauser}.
 *
 * TIP: For a detailed writeup see our guide
 * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
 * to implement supply mechanisms].
 *
 * We have followed general OpenZeppelin Contracts guidelines: functions revert
 * instead returning `false` on failure. This behavior is nonetheless
 * conventional and does not conflict with the expectations of ERC20
 * applications.
 *
 * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
 * This allows applications to reconstruct the allowance for all accounts just
 * by listening to said events. Other implementations of the EIP may not emit
 * these events, as it isn't required by the specification.
 *
 * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
 * functions have been added to mitigate the well-known issues around setting
 * allowances. See {IERC20-approve}.
 */
contract ERC20Upgradeable is Initializable, ContextUpgradeable, IERC20Upgradeable, IERC20MetadataUpgradeable {
    mapping(address => uint256) private _balances;

    mapping(address => mapping(address => uint256)) private _allowances;

    uint256 private _totalSupply;

    string private _name;
    string private _symbol;

    /**
     * @dev Sets the values for {name} and {symbol}.
     *
     * The default value of {decimals} is 18. To select a different value for
     * {decimals} you should overload it.
     *
     * All two of these values are immutable: they can only be set once during
     * construction.
     */
    function __ERC20_init(string memory name_, string memory symbol_) internal onlyInitializing {
        __ERC20_init_unchained(name_, symbol_);
    }

    function __ERC20_init_unchained(string memory name_, string memory symbol_) internal onlyInitializing {
        _name = name_;
        _symbol = symbol_;
    }

    /**
     * @dev Returns the name of the token.
     */
    function name() public view virtual override returns (string memory) {
        return _name;
    }

    /**
     * @dev Returns the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() public view virtual override returns (string memory) {
        return _symbol;
    }

    /**
     * @dev Returns the number of decimals used to get its user representation.
     * For example, if `decimals` equals `2`, a balance of `505` tokens should
     * be displayed to a user as `5.05` (`505 / 10 ** 2`).
     *
     * Tokens usually opt for a value of 18, imitating the relationship between
     * Ether and Wei. This is the value {ERC20} uses, unless this function is
     * overridden;
     *
     * NOTE: This information is only used for _display_ purposes: it in
     * no way affects any of the arithmetic of the contract, including
     * {IERC20-balanceOf} and {IERC20-transfer}.
     */
    function decimals() public view virtual override returns (uint8) {
        return 18;
    }

    /**
     * @dev See {IERC20-totalSupply}.
     */
    function totalSupply() public view virtual override returns (uint256) {
        return _totalSupply;
    }

    /**
     * @dev See {IERC20-balanceOf}.
     */
    function balanceOf(address account) public view virtual override returns (uint256) {
        return _balances[account];
    }

    /**
     * @dev See {IERC20-transfer}.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - the caller must have a balance of at least `amount`.
     */
    function transfer(address to, uint256 amount) public virtual override returns (bool) {
        address owner = _msgSender();
        _transfer(owner, to, amount);
        return true;
    }

    /**
     * @dev See {IERC20-allowance}.
     */
    function allowance(address owner, address spender) public view virtual override returns (uint256) {
        return _allowances[owner][spender];
    }

    /**
     * @dev See {IERC20-approve}.
     *
     * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
     * `transferFrom`. This is semantically equivalent to an infinite approval.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function approve(address spender, uint256 amount) public virtual override returns (bool) {
        address owner = _msgSender();
        _approve(owner, spender, amount);
        return true;
    }

    /**
     * @dev See {IERC20-transferFrom}.
     *
     * Emits an {Approval} event indicating the updated allowance. This is not
     * required by the EIP. See the note at the beginning of {ERC20}.
     *
     * NOTE: Does not update the allowance if the current allowance
     * is the maximum `uint256`.
     *
     * Requirements:
     *
     * - `from` and `to` cannot be the zero address.
     * - `from` must have a balance of at least `amount`.
     * - the caller must have allowance for ``from``'s tokens of at least
     * `amount`.
     */
    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) public virtual override returns (bool) {
        address spender = _msgSender();
        _spendAllowance(from, spender, amount);
        _transfer(from, to, amount);
        return true;
    }

    /**
     * @dev Atomically increases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
        address owner = _msgSender();
        _approve(owner, spender, allowance(owner, spender) + addedValue);
        return true;
    }

    /**
     * @dev Atomically decreases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `spender` must have allowance for the caller of at least
     * `subtractedValue`.
     */
    function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
        address owner = _msgSender();
        uint256 currentAllowance = allowance(owner, spender);
        require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
        unchecked {
            _approve(owner, spender, currentAllowance - subtractedValue);
        }

        return true;
    }

    /**
     * @dev Moves `amount` of tokens from `from` to `to`.
     *
     * This internal function is equivalent to {transfer}, and can be used to
     * e.g. implement automatic token fees, slashing mechanisms, etc.
     *
     * Emits a {Transfer} event.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `from` must have a balance of at least `amount`.
     */
    function _transfer(
        address from,
        address to,
        uint256 amount
    ) internal virtual {
        require(from != address(0), "ERC20: transfer from the zero address");
        require(to != address(0), "ERC20: transfer to the zero address");

        _beforeTokenTransfer(from, to, amount);

        uint256 fromBalance = _balances[from];
        require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
        unchecked {
            _balances[from] = fromBalance - amount;
            // Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by
            // decrementing then incrementing.
            _balances[to] += amount;
        }

        emit Transfer(from, to, amount);

        _afterTokenTransfer(from, to, amount);
    }

    /** @dev Creates `amount` tokens and assigns them to `account`, increasing
     * the total supply.
     *
     * Emits a {Transfer} event with `from` set to the zero address.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     */
    function _mint(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: mint to the zero address");

        _beforeTokenTransfer(address(0), account, amount);

        _totalSupply += amount;
        unchecked {
            // Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above.
            _balances[account] += amount;
        }
        emit Transfer(address(0), account, amount);

        _afterTokenTransfer(address(0), account, amount);
    }

    /**
     * @dev Destroys `amount` tokens from `account`, reducing the
     * total supply.
     *
     * Emits a {Transfer} event with `to` set to the zero address.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     * - `account` must have at least `amount` tokens.
     */
    function _burn(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: burn from the zero address");

        _beforeTokenTransfer(account, address(0), amount);

        uint256 accountBalance = _balances[account];
        require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
        unchecked {
            _balances[account] = accountBalance - amount;
            // Overflow not possible: amount <= accountBalance <= totalSupply.
            _totalSupply -= amount;
        }

        emit Transfer(account, address(0), amount);

        _afterTokenTransfer(account, address(0), amount);
    }

    /**
     * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
     *
     * This internal function is equivalent to `approve`, and can be used to
     * e.g. set automatic allowances for certain subsystems, etc.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `owner` cannot be the zero address.
     * - `spender` cannot be the zero address.
     */
    function _approve(
        address owner,
        address spender,
        uint256 amount
    ) internal virtual {
        require(owner != address(0), "ERC20: approve from the zero address");
        require(spender != address(0), "ERC20: approve to the zero address");

        _allowances[owner][spender] = amount;
        emit Approval(owner, spender, amount);
    }

    /**
     * @dev Updates `owner` s allowance for `spender` based on spent `amount`.
     *
     * Does not update the allowance amount in case of infinite allowance.
     * Revert if not enough allowance is available.
     *
     * Might emit an {Approval} event.
     */
    function _spendAllowance(
        address owner,
        address spender,
        uint256 amount
    ) internal virtual {
        uint256 currentAllowance = allowance(owner, spender);
        if (currentAllowance != type(uint256).max) {
            require(currentAllowance >= amount, "ERC20: insufficient allowance");
            unchecked {
                _approve(owner, spender, currentAllowance - amount);
            }
        }
    }

    /**
     * @dev Hook that is called before any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * will be transferred to `to`.
     * - when `from` is zero, `amount` tokens will be minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 amount
    ) internal virtual {}

    /**
     * @dev Hook that is called after any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * has been transferred to `to`.
     * - when `from` is zero, `amount` tokens have been minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens have been burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _afterTokenTransfer(
        address from,
        address to,
        uint256 amount
    ) internal virtual {}

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[45] private __gap;
}

File 1 of 38 : ERC20BurnableUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/extensions/ERC20Burnable.sol)

pragma solidity ^0.8.0;

import "../ERC20Upgradeable.sol";
import "../../../utils/ContextUpgradeable.sol";
import "../../../proxy/utils/Initializable.sol";

/**
 * @dev Extension of {ERC20} that allows token holders to destroy both their own
 * tokens and those that they have an allowance for, in a way that can be
 * recognized off-chain (via event analysis).
 */
abstract contract ERC20BurnableUpgradeable is Initializable, ContextUpgradeable, ERC20Upgradeable {
    function __ERC20Burnable_init() internal onlyInitializing {
    }

    function __ERC20Burnable_init_unchained() internal onlyInitializing {
    }
    /**
     * @dev Destroys `amount` tokens from the caller.
     *
     * See {ERC20-_burn}.
     */
    function burn(uint256 amount) public virtual {
        _burn(_msgSender(), amount);
    }

    /**
     * @dev Destroys `amount` tokens from `account`, deducting from the caller's
     * allowance.
     *
     * See {ERC20-_burn} and {ERC20-allowance}.
     *
     * Requirements:
     *
     * - the caller must have allowance for ``accounts``'s tokens of at least
     * `amount`.
     */
    function burnFrom(address account, uint256 amount) public virtual {
        _spendAllowance(account, _msgSender(), amount);
        _burn(account, amount);
    }

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[50] private __gap;
}

File 1 of 38 : IERC20MetadataUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)

pragma solidity ^0.8.0;

import "../IERC20Upgradeable.sol";

/**
 * @dev Interface for the optional metadata functions from the ERC20 standard.
 *
 * _Available since v4.1._
 */
interface IERC20MetadataUpgradeable is IERC20Upgradeable {
    /**
     * @dev Returns the name of the token.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the symbol of the token.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the decimals places of the token.
     */
    function decimals() external view returns (uint8);
}

File 1 of 38 : IERC20Upgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20Upgradeable {
    /**
     * @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);
}

File 1 of 38 : AddressUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol)

pragma solidity ^0.8.1;

/**
 * @dev Collection of functions related to the address type
 */
library AddressUpgradeable {
    /**
     * @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
     * ====
     *
     * [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://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

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

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

File 1 of 38 : ContextUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity ^0.8.0;
import "../proxy/utils/Initializable.sol";

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

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

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

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[50] private __gap;
}

File 1 of 38 : draft-IERC20Permit.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-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);
}

File 1 of 38 : IERC20Metadata.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)

pragma solidity ^0.8.0;

import "../IERC20.sol";

/**
 * @dev Interface for the optional metadata functions from the ERC20 standard.
 *
 * _Available since v4.1._
 */
interface IERC20Metadata is IERC20 {
    /**
     * @dev Returns the name of the token.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the symbol of the token.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the decimals places of the token.
     */
    function decimals() external view returns (uint8);
}

File 1 of 38 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.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);
}

File 1 of 38 : SafeERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.0;

import "../IERC20.sol";
import "../extensions/draft-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;

    function safeTransfer(
        IERC20 token,
        address to,
        uint256 value
    ) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
    }

    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));
    }

    function safeIncreaseAllowance(
        IERC20 token,
        address spender,
        uint256 value
    ) internal {
        uint256 newAllowance = token.allowance(address(this), spender) + value;
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }

    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");
            uint256 newAllowance = oldAllowance - value;
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
        }
    }

    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");
        if (returndata.length > 0) {
            // Return data is optional
            require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
        }
    }
}

File 1 of 38 : Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.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
     * ====
     *
     * [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://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

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

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

File 1 of 38 : ECDSA.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/ECDSA.sol)

pragma solidity ^0.8.0;

import "../Strings.sol";

/**
 * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
 *
 * These functions can be used to verify that a message was signed by the holder
 * of the private keys of a given address.
 */
library ECDSA {
    enum RecoverError {
        NoError,
        InvalidSignature,
        InvalidSignatureLength,
        InvalidSignatureS,
        InvalidSignatureV // Deprecated in v4.8
    }

    function _throwError(RecoverError error) private pure {
        if (error == RecoverError.NoError) {
            return; // no error: do nothing
        } else if (error == RecoverError.InvalidSignature) {
            revert("ECDSA: invalid signature");
        } else if (error == RecoverError.InvalidSignatureLength) {
            revert("ECDSA: invalid signature length");
        } else if (error == RecoverError.InvalidSignatureS) {
            revert("ECDSA: invalid signature 's' value");
        }
    }

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature` or error string. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {toEthSignedMessageHash} on it.
     *
     * Documentation for signature generation:
     * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
     * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
     *
     * _Available since v4.3._
     */
    function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
        if (signature.length == 65) {
            bytes32 r;
            bytes32 s;
            uint8 v;
            // ecrecover takes the signature parameters, and the only way to get them
            // currently is to use assembly.
            /// @solidity memory-safe-assembly
            assembly {
                r := mload(add(signature, 0x20))
                s := mload(add(signature, 0x40))
                v := byte(0, mload(add(signature, 0x60)))
            }
            return tryRecover(hash, v, r, s);
        } else {
            return (address(0), RecoverError.InvalidSignatureLength);
        }
    }

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature`. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {toEthSignedMessageHash} on it.
     */
    function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, signature);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
     *
     * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
     *
     * _Available since v4.3._
     */
    function tryRecover(
        bytes32 hash,
        bytes32 r,
        bytes32 vs
    ) internal pure returns (address, RecoverError) {
        bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
        uint8 v = uint8((uint256(vs) >> 255) + 27);
        return tryRecover(hash, v, r, s);
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
     *
     * _Available since v4.2._
     */
    function recover(
        bytes32 hash,
        bytes32 r,
        bytes32 vs
    ) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, r, vs);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `v`,
     * `r` and `s` signature fields separately.
     *
     * _Available since v4.3._
     */
    function tryRecover(
        bytes32 hash,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal pure returns (address, RecoverError) {
        // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
        // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
        // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
        // signatures from current libraries generate a unique signature with an s-value in the lower half order.
        //
        // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
        // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
        // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
        // these malleable signatures as well.
        if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
            return (address(0), RecoverError.InvalidSignatureS);
        }

        // If the signature is valid (and not malleable), return the signer address
        address signer = ecrecover(hash, v, r, s);
        if (signer == address(0)) {
            return (address(0), RecoverError.InvalidSignature);
        }

        return (signer, RecoverError.NoError);
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `v`,
     * `r` and `s` signature fields separately.
     */
    function recover(
        bytes32 hash,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, v, r, s);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Returns an Ethereum Signed Message, created from a `hash`. This
     * produces hash corresponding to the one signed with the
     * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
     * JSON-RPC method as part of EIP-191.
     *
     * See {recover}.
     */
    function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
        // 32 is the length in bytes of hash,
        // enforced by the type signature above
        return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
    }

    /**
     * @dev Returns an Ethereum Signed Message, created from `s`. This
     * produces hash corresponding to the one signed with the
     * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
     * JSON-RPC method as part of EIP-191.
     *
     * See {recover}.
     */
    function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", Strings.toString(s.length), s));
    }

    /**
     * @dev Returns an Ethereum Signed Typed Data, created from a
     * `domainSeparator` and a `structHash`. This produces hash corresponding
     * to the one signed with the
     * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
     * JSON-RPC method as part of EIP-712.
     *
     * See {recover}.
     */
    function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
    }
}

File 1 of 38 : Math.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.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) {
                return prod0 / denominator;
            }

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            require(denominator > prod1);

            ///////////////////////////////////////////////
            // 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 10, 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 * 8) < value ? 1 : 0);
        }
    }
}

File 1 of 38 : Strings.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/Strings.sol)

pragma solidity ^0.8.0;

import "./math/Math.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 `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);
    }
}

File 1 of 38 : BaseConnextFacet.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.17;

import {TransferInfo, AppStorage, Role} from "../libraries/LibConnextStorage.sol";
import {LibDiamond} from "../libraries/LibDiamond.sol";
import {AssetLogic} from "../libraries/AssetLogic.sol";
import {TokenId} from "../libraries/TokenId.sol";
import {Constants} from "../libraries/Constants.sol";

contract BaseConnextFacet {
  AppStorage internal s;

  // ========== Custom Errors ===========

  error BaseConnextFacet__onlyOwner_notOwner();
  error BaseConnextFacet__onlyProposed_notProposedOwner();
  error BaseConnextFacet__onlyOwnerOrRouter_notOwnerOrRouter();
  error BaseConnextFacet__onlyOwnerOrWatcher_notOwnerOrWatcher();
  error BaseConnextFacet__onlyOwnerOrAdmin_notOwnerOrAdmin();
  error BaseConnextFacet__whenNotPaused_paused();
  error BaseConnextFacet__nonReentrant_reentrantCall();
  error BaseConnextFacet__nonXCallReentrant_reentrantCall();
  error BaseConnextFacet__getAdoptedAsset_assetNotFound();
  error BaseConnextFacet__getApprovedCanonicalId_notAllowlisted();

  // ============ Modifiers ============

  /**
   * @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() {
    // On the first call to nonReentrant, _notEntered will be true
    if (s._status == Constants.ENTERED) revert BaseConnextFacet__nonReentrant_reentrantCall();

    // Any calls to nonReentrant after this point will fail
    s._status = Constants.ENTERED;

    _;

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

  modifier nonXCallReentrant() {
    // On the first call to nonReentrant, _notEntered will be true
    if (s._xcallStatus == Constants.ENTERED) revert BaseConnextFacet__nonXCallReentrant_reentrantCall();

    // Any calls to nonReentrant after this point will fail
    s._xcallStatus = Constants.ENTERED;

    _;

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

  /**
   * @notice Throws if called by any account other than the owner.
   */
  modifier onlyOwner() {
    if (LibDiamond.contractOwner() != msg.sender) revert BaseConnextFacet__onlyOwner_notOwner();
    _;
  }

  /**
   * @notice Throws if called by any account other than the proposed owner.
   */
  modifier onlyProposed() {
    if (s._proposed != msg.sender) revert BaseConnextFacet__onlyProposed_notProposedOwner();
    _;
  }

  /**
   * @notice Throws if called by any account other than the owner and router role.
   */
  modifier onlyOwnerOrRouter() {
    if (LibDiamond.contractOwner() != msg.sender && s.roles[msg.sender] != Role.RouterAdmin)
      revert BaseConnextFacet__onlyOwnerOrRouter_notOwnerOrRouter();
    _;
  }

  /**
   * @notice Throws if called by any account other than the owner and watcher role.
   */
  modifier onlyOwnerOrWatcher() {
    if (LibDiamond.contractOwner() != msg.sender && s.roles[msg.sender] != Role.Watcher)
      revert BaseConnextFacet__onlyOwnerOrWatcher_notOwnerOrWatcher();
    _;
  }

  /**
   * @notice Throws if called by any account other than the owner and admin role.
   */
  modifier onlyOwnerOrAdmin() {
    if (LibDiamond.contractOwner() != msg.sender && s.roles[msg.sender] != Role.Admin)
      revert BaseConnextFacet__onlyOwnerOrAdmin_notOwnerOrAdmin();
    _;
  }

  /**
   * @notice Throws if all functionality is paused
   */
  modifier whenNotPaused() {
    if (s._paused) revert BaseConnextFacet__whenNotPaused_paused();
    _;
  }

  // ============ Internal functions ============
  /**
   * @notice Indicates if the router allowlist has been removed
   */
  function _isRouterAllowlistRemoved() internal view returns (bool) {
    return LibDiamond.contractOwner() == address(0) || s._routerAllowlistRemoved;
  }

  /**
   * @notice Returns the adopted assets for given canonical information
   */
  function _getAdoptedAsset(bytes32 _key) internal view returns (address) {
    address adopted = AssetLogic.getConfig(_key).adopted;
    if (adopted == address(0)) {
      revert BaseConnextFacet__getAdoptedAsset_assetNotFound();
    }
    return adopted;
  }

  /**
   * @notice Returns the adopted assets for given canonical information
   */
  function _getRepresentationAsset(bytes32 _key) internal view returns (address) {
    address representation = AssetLogic.getConfig(_key).representation;
    // If this is address(0), then there is no mintable token for this asset on this
    // domain
    return representation;
  }

  /**
   * @notice Calculates a transferId
   */
  function _calculateTransferId(TransferInfo memory _params) internal pure returns (bytes32) {
    return keccak256(abi.encode(_params));
  }

  /**
   * @notice Internal utility function that combines
   *         `_origin` and `_nonce`.
   * @dev Both origin and nonce should be less than 2^32 - 1
   * @param _origin Domain of chain where the transfer originated
   * @param _nonce The unique identifier for the message from origin to destination
   * @return Returns (`_origin` << 32) & `_nonce`
   */
  function _originAndNonce(uint32 _origin, uint32 _nonce) internal pure returns (uint64) {
    return (uint64(_origin) << 32) | _nonce;
  }

  function _getLocalAsset(
    bytes32 _key,
    bytes32 _id,
    uint32 _domain
  ) internal view returns (address) {
    return AssetLogic.getLocalAsset(_key, _id, _domain, s);
  }

  function _getCanonicalTokenId(address _candidate) internal view returns (TokenId memory) {
    return AssetLogic.getCanonicalTokenId(_candidate, s);
  }

  function _getLocalAndAdoptedToken(
    bytes32 _key,
    bytes32 _id,
    uint32 _domain
  ) internal view returns (address, address) {
    address _local = AssetLogic.getLocalAsset(_key, _id, _domain, s);
    address _adopted = _getAdoptedAsset(_key);
    return (_local, _adopted);
  }

  function _isLocalOrigin(address _token) internal view returns (bool) {
    return AssetLogic.isLocalOrigin(_token, s);
  }

  function _getApprovedCanonicalId(address _candidate) internal view returns (TokenId memory, bytes32) {
    TokenId memory _canonical = _getCanonicalTokenId(_candidate);
    bytes32 _key = AssetLogic.calculateCanonicalHash(_canonical.id, _canonical.domain);
    if (!AssetLogic.getConfig(_key).approval) {
      revert BaseConnextFacet__getApprovedCanonicalId_notAllowlisted();
    }
    return (_canonical, _key);
  }
}

File 2 of 38 : LPToken.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.17;

import {ERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20BurnableUpgradeable.sol";
import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";

/**
 * @title Liquidity Provider Token
 * @notice This token is an ERC20 detailed token with added capability to be minted by the owner.
 * It is used to represent user's shares when providing liquidity to swap contracts.
 * @dev Only Swap contracts should initialize and own LPToken contracts.
 */
contract LPToken is ERC20Upgradeable, OwnableUpgradeable {
  // ============ Storage ============

  // ============ Initializer ============

  /**
   * @notice Initializes this LPToken contract with the given name and symbol
   * @dev The caller of this function will become the owner. A Swap contract should call this
   * in its initializer function.
   * @param name name of this token
   * @param symbol symbol of this token
   */
  function initialize(string memory name, string memory symbol) external initializer returns (bool) {
    __Context_init_unchained();
    __ERC20_init_unchained(name, symbol);
    __Ownable_init_unchained();
    return true;
  }

  // ============ External functions ============

  /**
   * @notice Mints the given amount of LPToken to the recipient.
   * @dev only owner can call this mint function
   * @param recipient address of account to receive the tokens
   * @param amount amount of tokens to mint
   */
  function mint(address recipient, uint256 amount) external onlyOwner {
    require(amount != 0, "LPToken: cannot mint 0");
    _mint(recipient, amount);
  }

  /**
   * @notice Burns the given amount of LPToken from provided account
   * @dev only owner can call this burn function
   * @param account address of account from which to burn token
   * @param amount amount of tokens to mint
   */
  function burnFrom(address account, uint256 amount) external onlyOwner {
    require(amount != 0, "LPToken: cannot burn 0");
    _burn(account, amount);
  }

  // ============ Internal functions ============

  /**
   * @dev Overrides ERC20._beforeTokenTransfer() which get called on every transfers including
   * minting and burning. This ensures that Swap.updateUserWithdrawFees are called everytime.
   * This assumes the owner is set to a Swap contract's address.
   */
  function _beforeTokenTransfer(
    address from,
    address to,
    uint256 amount
  ) internal virtual override(ERC20Upgradeable) {
    super._beforeTokenTransfer(from, to, amount);
    require(to != address(this), "LPToken: cannot send to itself");
  }

  // ============ Upgrade Gap ============
  uint256[50] private __GAP; // gap for upgrade safety
}

File 2 of 38 : IAavePool.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.17;

interface IAavePool {
  /**
   * @dev Mints an `amount` of aTokens to the `onBehalfOf`
   * @param asset The address of the underlying asset to mint
   * @param amount The amount to mint
   * @param onBehalfOf The address that will receive the aTokens
   * @param referralCode Code used to register the integrator originating the operation, for potential rewards.
   *   0 if the action is executed directly by the user, without any middle-man
   **/
  function mintUnbacked(
    address asset,
    uint256 amount,
    address onBehalfOf,
    uint16 referralCode
  ) external;

  /**
   * @dev Back the current unbacked underlying with `amount` and pay `fee`.
   * @param asset The address of the underlying asset to back
   * @param amount The amount to back
   * @param fee The amount paid in fees
   **/
  function backUnbacked(
    address asset,
    uint256 amount,
    uint256 fee
  ) external;

  /**
   * @notice Withdraws an `amount` of underlying asset from the reserve, burning the equivalent aTokens owned
   * E.g. User has 100 aUSDC, calls withdraw() and receives 100 USDC, burning the 100 aUSDC
   * @param asset The address of the underlying asset to withdraw
   * @param amount The underlying amount to be withdrawn
   *   - Send the value type(uint256).max in order to withdraw the whole aToken balance
   * @param to The address that will receive the underlying, same as msg.sender if the user
   *   wants to receive it on his own wallet, or a different address if the beneficiary is a
   *   different wallet
   * @return The final amount withdrawn
   **/
  function withdraw(
    address asset,
    uint256 amount,
    address to
  ) external returns (uint256);
}

File 2 of 38 : IBridgeToken.sol
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity 0.8.17;

import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";

interface IBridgeToken is IERC20Metadata {
  function burn(address _from, uint256 _amnt) external;

  function mint(address _to, uint256 _amnt) external;

  function setDetails(string calldata _name, string calldata _symbol) external;
}

File 2 of 38 : IDiamondCut.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;

/******************************************************************************\
* Author: Nick Mudge <[email protected]> (https://twitter.com/mudgen)
* EIP-2535 Diamonds: https://eips.ethereum.org/EIPS/eip-2535
/******************************************************************************/

interface IDiamondCut {
  enum FacetCutAction {
    Add,
    Replace,
    Remove
  }
  // Add=0, Replace=1, Remove=2

  struct FacetCut {
    address facetAddress;
    FacetCutAction action;
    bytes4[] functionSelectors;
  }

  /// @notice Propose to add/replace/remove any number of functions and optionally execute
  ///         a function with delegatecall
  /// @param _diamondCut Contains the facet addresses and function selectors
  /// @param _init The address of the contract or facet to execute _calldata
  /// @param _calldata A function call, including function selector and arguments
  ///                  _calldata is executed with delegatecall on _init
  function proposeDiamondCut(
    FacetCut[] calldata _diamondCut,
    address _init,
    bytes calldata _calldata
  ) external;

  event DiamondCutProposed(FacetCut[] _diamondCut, address _init, bytes _calldata, uint256 deadline);

  /// @notice Add/replace/remove any number of functions and optionally execute
  ///         a function with delegatecall
  /// @param _diamondCut Contains the facet addresses and function selectors
  /// @param _init The address of the contract or facet to execute _calldata
  /// @param _calldata A function call, including function selector and arguments
  ///                  _calldata is executed with delegatecall on _init
  function diamondCut(
    FacetCut[] calldata _diamondCut,
    address _init,
    bytes calldata _calldata
  ) external;

  event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata);

  /// @notice Propose to add/replace/remove any number of functions and optionally execute
  ///         a function with delegatecall
  /// @param _diamondCut Contains the facet addresses and function selectors
  /// @param _init The address of the contract or facet to execute _calldata
  /// @param _calldata A function call, including function selector and arguments
  ///                  _calldata is executed with delegatecall on _init
  function rescindDiamondCut(
    FacetCut[] calldata _diamondCut,
    address _init,
    bytes calldata _calldata
  ) external;

  /**
   * @notice Returns the acceptance time for a given proposal
   * @param _diamondCut Contains the facet addresses and function selectors
   * @param _init The address of the contract or facet to execute _calldata
   * @param _calldata A function call, including function selector and arguments _calldata is
   * executed with delegatecall on _init
   */
  function getAcceptanceTime(
    FacetCut[] calldata _diamondCut,
    address _init,
    bytes calldata _calldata
  ) external returns (uint256);

  event DiamondCutRescinded(FacetCut[] _diamondCut, address _init, bytes _calldata);
}

File 2 of 38 : IStableSwap.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.17;

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

interface IStableSwap {
  /*** EVENTS ***/

  // events replicated from SwapUtils to make the ABI easier for dumb
  // clients
  event TokenSwap(address indexed buyer, uint256 tokensSold, uint256 tokensBought, uint128 soldId, uint128 boughtId);
  event AddLiquidity(
    address indexed provider,
    uint256[] tokenAmounts,
    uint256[] fees,
    uint256 invariant,
    uint256 lpTokenSupply
  );
  event RemoveLiquidity(address indexed provider, uint256[] tokenAmounts, uint256 lpTokenSupply);
  event RemoveLiquidityOne(
    address indexed provider,
    uint256 lpTokenAmount,
    uint256 lpTokenSupply,
    uint256 boughtId,
    uint256 tokensBought
  );
  event RemoveLiquidityImbalance(
    address indexed provider,
    uint256[] tokenAmounts,
    uint256[] fees,
    uint256 invariant,
    uint256 lpTokenSupply
  );
  event NewAdminFee(uint256 newAdminFee);
  event NewSwapFee(uint256 newSwapFee);
  event NewWithdrawFee(uint256 newWithdrawFee);
  event RampA(uint256 oldA, uint256 newA, uint256 initialTime, uint256 futureTime);
  event StopRampA(uint256 currentA, uint256 time);

  function swap(
    uint8 tokenIndexFrom,
    uint8 tokenIndexTo,
    uint256 dx,
    uint256 minDy,
    uint256 deadline
  ) external returns (uint256);

  function swapExact(
    uint256 amountIn,
    address assetIn,
    address assetOut,
    uint256 minAmountOut,
    uint256 deadline
  ) external payable returns (uint256);

  function swapExactOut(
    uint256 amountOut,
    address assetIn,
    address assetOut,
    uint256 maxAmountIn,
    uint256 deadline
  ) external payable returns (uint256);

  function getA() external view returns (uint256);

  function getToken(uint8 index) external view returns (IERC20);

  function getTokenIndex(address tokenAddress) external view returns (uint8);

  function getTokenBalance(uint8 index) external view returns (uint256);

  function getVirtualPrice() external view returns (uint256);

  // min return calculation functions
  function calculateSwap(
    uint8 tokenIndexFrom,
    uint8 tokenIndexTo,
    uint256 dx
  ) external view returns (uint256);

  function calculateSwapOut(
    uint8 tokenIndexFrom,
    uint8 tokenIndexTo,
    uint256 dy
  ) external view returns (uint256);

  function calculateSwapFromAddress(
    address assetIn,
    address assetOut,
    uint256 amountIn
  ) external view returns (uint256);

  function calculateSwapOutFromAddress(
    address assetIn,
    address assetOut,
    uint256 amountOut
  ) external view returns (uint256);

  function calculateTokenAmount(uint256[] calldata amounts, bool deposit) external view returns (uint256);

  function calculateRemoveLiquidity(uint256 amount) external view returns (uint256[] memory);

  function calculateRemoveLiquidityOneToken(uint256 tokenAmount, uint8 tokenIndex)
    external
    view
    returns (uint256 availableTokenAmount);

  // state modifying functions
  function initialize(
    IERC20[] memory pooledTokens,
    uint8[] memory decimals,
    string memory lpTokenName,
    string memory lpTokenSymbol,
    uint256 a,
    uint256 fee,
    uint256 adminFee,
    address lpTokenTargetAddress
  ) external;

  function addLiquidity(
    uint256[] calldata amounts,
    uint256 minToMint,
    uint256 deadline
  ) external returns (uint256);

  function removeLiquidity(
    uint256 amount,
    uint256[] calldata minAmounts,
    uint256 deadline
  ) external returns (uint256[] memory);

  function removeLiquidityOneToken(
    uint256 tokenAmount,
    uint8 tokenIndex,
    uint256 minAmount,
    uint256 deadline
  ) external returns (uint256);

  function removeLiquidityImbalance(
    uint256[] calldata amounts,
    uint256 maxBurnAmount,
    uint256 deadline
  ) external returns (uint256);
}

File 2 of 38 : IXReceiver.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.17;

interface IXReceiver {
  function xReceive(
    bytes32 _transferId,
    uint256 _amount,
    address _asset,
    address _originSender,
    uint32 _origin,
    bytes memory _callData
  ) external returns (bytes memory);
}

File 2 of 38 : AmplificationUtils.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.17;

import {SwapUtils} from "./SwapUtils.sol";
import {Constants} from "./Constants.sol";

/**
 * @title AmplificationUtils library
 * @notice A library to calculate and ramp the A parameter of a given `SwapUtils.Swap` struct.
 * This library assumes the struct is fully validated.
 */
library AmplificationUtils {
  event RampA(uint256 oldA, uint256 newA, uint256 initialTime, uint256 futureTime);
  event StopRampA(uint256 currentA, uint256 time);

  /**
   * @notice Return A, the amplification coefficient * n ** (n - 1)
   * @dev See the StableSwap paper for details
   * @param self Swap struct to read from
   * @return A parameter
   */
  function getA(SwapUtils.Swap storage self) internal view returns (uint256) {
    return _getAPrecise(self) / Constants.A_PRECISION;
  }

  /**
   * @notice Return A in its raw precision
   * @dev See the StableSwap paper for details
   * @param self Swap struct to read from
   * @return A parameter in its raw precision form
   */
  function getAPrecise(SwapUtils.Swap storage self) internal view returns (uint256) {
    return _getAPrecise(self);
  }

  /**
   * @notice Return A in its raw precision
   * @dev See the StableSwap paper for details
   * @param self Swap struct to read from
   * @return currentA A parameter in its raw precision form
   */
  function _getAPrecise(SwapUtils.Swap storage self) internal view returns (uint256 currentA) {
    uint256 t1 = self.futureATime; // time when ramp is finished
    currentA = self.futureA; // final A value when ramp is finished
    uint256 a0 = self.initialA; // initial A value when ramp is started

    if (a0 != currentA && block.timestamp < t1) {
      uint256 t0 = self.initialATime; // time when ramp is started
      assembly {
        currentA := div(add(mul(a0, sub(t1, timestamp())), mul(currentA, sub(timestamp(), t0))), sub(t1, t0))
      }
    }
  }

  /**
   * @notice Start ramping up or down A parameter towards given futureA_ and futureTime_
   * Checks if the change is too rapid, and commits the new A value only when it falls under
   * the limit range.
   * @param self Swap struct to update
   * @param futureA_ the new A to ramp towards
   * @param futureTime_ timestamp when the new A should be reached
   */
  function rampA(
    SwapUtils.Swap storage self,
    uint256 futureA_,
    uint256 futureTime_
  ) internal {
    require(block.timestamp >= self.initialATime + Constants.MIN_RAMP_DELAY, "Wait 1 day before starting ramp");
    require(futureTime_ >= block.timestamp + Constants.MIN_RAMP_TIME, "Insufficient ramp time");
    require(futureA_ != 0 && futureA_ < Constants.MAX_A, "futureA_ must be > 0 and < MAX_A");

    uint256 initialAPrecise = _getAPrecise(self);
    uint256 futureAPrecise = futureA_ * Constants.A_PRECISION;
    require(initialAPrecise != futureAPrecise, "!valid ramp");

    if (futureAPrecise < initialAPrecise) {
      require(futureAPrecise * Constants.MAX_A_CHANGE >= initialAPrecise, "futureA_ is too small");
    } else {
      require(futureAPrecise <= initialAPrecise * Constants.MAX_A_CHANGE, "futureA_ is too large");
    }

    self.initialA = initialAPrecise;
    self.futureA = futureAPrecise;
    self.initialATime = block.timestamp;
    self.futureATime = futureTime_;

    emit RampA(initialAPrecise, futureAPrecise, block.timestamp, futureTime_);
  }

  /**
   * @notice Stops ramping A immediately. Once this function is called, rampA()
   * cannot be called for another 24 hours
   * @param self Swap struct to update
   */
  function stopRampA(SwapUtils.Swap storage self) internal {
    require(self.futureATime > block.timestamp, "Ramp is already stopped");

    uint256 currentA = _getAPrecise(self);
    self.initialA = currentA;
    self.futureA = currentA;
    self.initialATime = block.timestamp;
    self.futureATime = block.timestamp;

    emit StopRampA(currentA, block.timestamp);
  }
}

File 2 of 38 : AssetLogic.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.17;

import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";

import {TypeCasts} from "../../../shared/libraries/TypeCasts.sol";

import {IStableSwap} from "../interfaces/IStableSwap.sol";

import {LibConnextStorage, AppStorage, TokenConfig} from "./LibConnextStorage.sol";
import {SwapUtils} from "./SwapUtils.sol";
import {Constants} from "./Constants.sol";
import {TokenId} from "./TokenId.sol";

library AssetLogic {
  // ============ Libraries ============

  using SwapUtils for SwapUtils.Swap;
  using SafeERC20 for IERC20Metadata;

  // ============ Errors ============

  error AssetLogic__handleIncomingAsset_nativeAssetNotSupported();
  error AssetLogic__handleIncomingAsset_feeOnTransferNotSupported();
  error AssetLogic__handleOutgoingAsset_notNative();
  error AssetLogic__getTokenIndexFromStableSwapPool_notExist();
  error AssetLogic__getConfig_notRegistered();
  error AssetLogic__swapAsset_externalStableSwapPoolDoesNotExist();

  // ============ Internal: Handle Transfer ============

  function getConfig(bytes32 _key) internal view returns (TokenConfig storage) {
    AppStorage storage s = LibConnextStorage.connextStorage();
    TokenConfig storage config = s.tokenConfigs[_key];

    // Sanity check: not empty
    // NOTE: adopted decimals will *always* be nonzero (or reflect what is onchain
    // for the asset). The same is not true for the representation assets, which
    // will always have 0 decimals on the canonical domain
    if (config.adoptedDecimals < 1) {
      revert AssetLogic__getConfig_notRegistered();
    }

    return config;
  }

  /**
   * @notice Handles transferring funds from msg.sender to the Connext contract.
   * @dev Does NOT work with fee-on-transfer tokens: will revert.
   *
   * @param _asset - The address of the ERC20 token to transfer.
   * @param _amount - The specified amount to transfer.
   */
  function handleIncomingAsset(address _asset, uint256 _amount) internal {
    // Sanity check: if amount is 0, do nothing.
    if (_amount == 0) {
      return;
    }
    // Sanity check: asset address is not zero.
    if (_asset == address(0)) {
      revert AssetLogic__handleIncomingAsset_nativeAssetNotSupported();
    }

    IERC20Metadata asset = IERC20Metadata(_asset);

    // Record starting amount to validate correct amount is transferred.
    uint256 starting = asset.balanceOf(address(this));

    // Transfer asset to contract.
    asset.safeTransferFrom(msg.sender, address(this), _amount);

    // Ensure correct amount was transferred (i.e. this was not a fee-on-transfer token).
    if (asset.balanceOf(address(this)) - starting != _amount) {
      revert AssetLogic__handleIncomingAsset_feeOnTransferNotSupported();
    }
  }

  /**
   * @notice Handles transferring funds from the Connext contract to a specified address
   * @param _asset - The address of the ERC20 token to transfer.
   * @param _to - The recipient address that will receive the funds.
   * @param _amount - The amount to withdraw from contract.
   */
  function handleOutgoingAsset(
    address _asset,
    address _to,
    uint256 _amount
  ) internal {
    // Sanity check: if amount is 0, do nothing.
    if (_amount == 0) {
      return;
    }
    // Sanity check: asset address is not zero.
    if (_asset == address(0)) revert AssetLogic__handleOutgoingAsset_notNative();

    // Transfer ERC20 asset to target recipient.
    SafeERC20.safeTransfer(IERC20Metadata(_asset), _to, _amount);
  }

  // ============ Internal: StableSwap Pools ============

  /**
   * @notice Return the index of the given token address. Reverts if no matching
   * token is found.
   * @param key the hash of the canonical id and domain
   * @param tokenAddress address of the token
   * @return the index of the given token address
   */
  function getTokenIndexFromStableSwapPool(bytes32 key, address tokenAddress) internal view returns (uint8) {
    AppStorage storage s = LibConnextStorage.connextStorage();
    uint8 index = s.tokenIndexes[key][tokenAddress];
    if (address(s.swapStorages[key].pooledTokens[index]) != tokenAddress)
      revert AssetLogic__getTokenIndexFromStableSwapPool_notExist();
    return index;
  }

  // ============ Internal: Handle Swap ============

  /**
   * @notice Swaps an adopted asset to the local (representation or canonical) asset.
   * @dev Will not swap if the asset passed in is the local asset.
   * @param _key - The hash of canonical id and domain.
   * @param _asset - The address of the adopted asset to swap into the local asset.
   * @param _amount - The amount of the adopted asset to swap.
   * @param _slippage - The maximum amount of slippage user will take on from _amount in BPS.
   * @return uint256 The amount of local asset received from swap.
   */
  function swapToLocalAssetIfNeeded(
    bytes32 _key,
    address _asset,
    address _local,
    uint256 _amount,
    uint256 _slippage
  ) internal returns (uint256) {
    // If there's no amount, no need to swap.
    if (_amount == 0) {
      return 0;
    }

    // Check the case where the adopted asset *is* the local asset. If so, no need to swap.
    if (_local == _asset) {
      return _amount;
    }

    // Get the configs.
    TokenConfig storage config = getConfig(_key);

    // Swap the asset to the proper local asset.
    (uint256 out, ) = _swapAsset(
      _key,
      _asset,
      _local,
      _amount,
      calculateSlippageBoundary(config.adoptedDecimals, config.representationDecimals, _amount, _slippage)
    );
    return out;
  }

  /**
   * @notice Swaps a local bridge asset for the adopted asset using the stored stable swap
   * @dev Will not swap if the asset passed in is the adopted asset
   * @param _key the hash of the canonical id and domain
   * @param _asset - The address of the local asset to swap into the adopted asset
   * @param _amount - The amount of the local asset to swap
   * @param _slippage - The minimum amount of slippage user will take on from _amount in BPS
   * @param _normalizedIn - The amount sent in on xcall to take the slippage from, in 18 decimals
   * by convention
   * @return The amount of adopted asset received from swap
   * @return The address of asset received post-swap
   */
  function swapFromLocalAssetIfNeeded(
    bytes32 _key,
    address _asset,
    uint256 _amount,
    uint256 _slippage,
    uint256 _normalizedIn
  ) internal returns (uint256, address) {
    // Get the token config.
    TokenConfig storage config = getConfig(_key);
    address adopted = config.adopted;

    // If the adopted asset is the local asset, no need to swap.
    if (adopted == _asset) {
      return (_amount, adopted);
    }

    // If there's no amount, no need to swap.
    if (_amount == 0) {
      return (_amount, adopted);
    }

    // Swap the asset to the proper local asset
    return
      _swapAsset(
        _key,
        _asset,
        adopted,
        _amount,
        // NOTE: To get the slippage boundary here, you must take the slippage % off of the
        // normalized amount in (at 18 decimals by convention), then convert that amount
        // to the proper decimals of adopted.
        calculateSlippageBoundary(
          Constants.DEFAULT_NORMALIZED_DECIMALS,
          config.adoptedDecimals,
          _normalizedIn,
          _slippage
        )
      );
  }

  /**
   * @notice Swaps a local bridge asset for the adopted asset using the stored stable swap
   * @dev Will not swap if the asset passed in is the adopted asset
   * @param _key the hash of the canonical id and domain
   * @param _asset - The address of the local asset to swap into the adopted asset
   * @param _amount - The exact amount to receive out of the swap
   * @param _maxIn - The most you will supply to the swap
   * @return The amount of local asset put into  swap
   * @return The address of asset received post-swap
   */
  function swapFromLocalAssetIfNeededForExactOut(
    bytes32 _key,
    address _asset,
    uint256 _amount,
    uint256 _maxIn
  ) internal returns (uint256, address) {
    TokenConfig storage config = getConfig(_key);

    // If the adopted asset is the local asset, no need to swap.
    address adopted = config.adopted;
    if (adopted == _asset) {
      return (_amount, adopted);
    }

    return _swapAssetOut(_key, _asset, adopted, _amount, _maxIn);
  }

  /**
   * @notice Swaps assetIn to assetOut using the stored stable swap or internal swap pool.
   * @dev Will not swap if the asset passed in is the adopted asset
   * @param _key - The hash of canonical id and domain.
   * @param _assetIn - The address of the from asset
   * @param _assetOut - The address of the to asset
   * @param _amount - The amount of the local asset to swap
   * @param _minOut - The minimum amount of `_assetOut` the user will accept
   * @return The amount of asset received
   * @return The address of asset received
   */
  function _swapAsset(
    bytes32 _key,
    address _assetIn,
    address _assetOut,
    uint256 _amount,
    uint256 _minOut
  ) internal returns (uint256, address) {
    AppStorage storage s = LibConnextStorage.connextStorage();

    // Retrieve internal swap pool reference.
    SwapUtils.Swap storage ipool = s.swapStorages[_key];

    if (ipool.exists()) {
      // Swap via the internal pool.
      return (
        ipool.swapInternal(
          getTokenIndexFromStableSwapPool(_key, _assetIn),
          getTokenIndexFromStableSwapPool(_key, _assetOut),
          _amount,
          _minOut
        ),
        _assetOut
      );
    } else {
      // Otherwise, swap via external stableswap pool.
      IStableSwap pool = IStableSwap(getConfig(_key).adoptedToLocalExternalPools);

      IERC20Metadata assetIn = IERC20Metadata(_assetIn);

      assetIn.safeApprove(address(pool), 0);
      assetIn.safeIncreaseAllowance(address(pool), _amount);

      // NOTE: If pool is not registered here, then this call will revert.
      return (
        pool.swapExact(_amount, _assetIn, _assetOut, _minOut, block.timestamp + Constants.DEFAULT_DEADLINE_EXTENSION),
        _assetOut
      );
    }
  }

  /**
   * @notice Swaps assetIn to assetOut using the stored stable swap or internal swap pool.
   * @param _key - The hash of the canonical id and domain.
   * @param _assetIn - The address of the from asset.
   * @param _assetOut - The address of the to asset.
   * @param _amountOut - The amount of the _assetOut to swap.
   * @param _maxIn - The most you will supply to the swap.
   * @return amountIn The amount of assetIn. Will be 0 if the swap was unsuccessful (slippage
   * too high).
   * @return assetOut The address of asset received.
   */
  function _swapAssetOut(
    bytes32 _key,
    address _assetIn,
    address _assetOut,
    uint256 _amountOut,
    uint256 _maxIn
  ) internal returns (uint256, address) {
    AppStorage storage s = LibConnextStorage.connextStorage();

    // Retrieve internal swap pool reference. If it doesn't exist, we'll resort to using an
    // external stableswap below.
    SwapUtils.Swap storage ipool = s.swapStorages[_key];

    // Swap the asset to the proper local asset.
    // NOTE: IFF slippage was too high to perform swap in either case: success = false, amountIn = 0
    if (ipool.exists()) {
      // Swap via the internal pool.
      return (
        ipool.swapInternalOut(
          getTokenIndexFromStableSwapPool(_key, _assetIn),
          getTokenIndexFromStableSwapPool(_key, _assetOut),
          _amountOut,
          _maxIn
        ),
        _assetOut
      );
    } else {
      // Otherwise, swap via external stableswap pool.
      // NOTE: This call will revert if the external stableswap pool doesn't exist.
      IStableSwap pool = IStableSwap(getConfig(_key).adoptedToLocalExternalPools);
      address poolAddress = address(pool);

      // Perform the swap.
      // Edge case with some tokens: Example USDT in ETH Mainnet, after the backUnbacked call
      // there could be a remaining allowance if not the whole amount is pulled by aave.
      // Later, if we try to increase the allowance it will fail. USDT demands if allowance
      // is not 0, it has to be set to 0 first.
      // Example: https://github.com/aave/aave-v3-periphery/blob/ca184e5278bcbc10d28c3dbbc604041d7cfac50b/contracts/adapters/paraswap/ParaSwapRepayAdapter.sol#L138-L140
      IERC20Metadata assetIn = IERC20Metadata(_assetIn);

      assetIn.safeApprove(poolAddress, 0);
      assetIn.safeIncreaseAllowance(poolAddress, _maxIn);

      uint256 out = pool.swapExactOut(
        _amountOut,
        _assetIn,
        _assetOut,
        _maxIn,
        block.timestamp + Constants.DEFAULT_DEADLINE_EXTENSION
      );

      // Reset allowance
      assetIn.safeApprove(poolAddress, 0);
      return (out, _assetOut);
    }
  }

  /**
   * @notice Calculate amount of tokens you receive on a local bridge asset for the adopted asset
   * using the stored stable swap
   * @dev Will not use the stored stable swap if the asset passed in is the local asset
   * @param _key - The hash of the canonical id and domain
   * @param _asset - The address of the local asset to swap into the local asset
   * @param _amount - The amount of the local asset to swap
   * @return The amount of local asset received from swap
   * @return The address of asset received post-swap
   */
  function calculateSwapFromLocalAssetIfNeeded(
    bytes32 _key,
    address _asset,
    uint256 _amount
  ) internal view returns (uint256, address) {
    AppStorage storage s = LibConnextStorage.connextStorage();

    // If the adopted asset is the local asset, no need to swap.
    TokenConfig storage config = getConfig(_key);
    address adopted = config.adopted;
    if (adopted == _asset) {
      return (_amount, adopted);
    }

    SwapUtils.Swap storage ipool = s.swapStorages[_key];

    // Calculate the swap using the appropriate pool.
    if (ipool.exists()) {
      // Calculate with internal swap pool.
      uint8 tokenIndexIn = getTokenIndexFromStableSwapPool(_key, _asset);
      uint8 tokenIndexOut = getTokenIndexFromStableSwapPool(_key, adopted);
      return (ipool.calculateSwap(tokenIndexIn, tokenIndexOut, _amount), adopted);
    } else {
      // Otherwise, try to calculate with external pool.
      IStableSwap pool = IStableSwap(config.adoptedToLocalExternalPools);
      // NOTE: This call will revert if no external pool exists.
      return (pool.calculateSwapFromAddress(_asset, adopted, _amount), adopted);
    }
  }

  /**
   * @notice Calculate amount of tokens you receive of a local bridge asset for the adopted asset
   * using the stored stable swap
   * @dev Will not use the stored stable swap if the asset passed in is the local asset
   * @param _asset - The address of the asset to swap into the local asset
   * @param _amount - The amount of the asset to swap
   * @return The amount of local asset received from swap
   * @return The address of asset received post-swap
   */
  function calculateSwapToLocalAssetIfNeeded(
    bytes32 _key,
    address _asset,
    address _local,
    uint256 _amount
  ) internal view returns (uint256, address) {
    AppStorage storage s = LibConnextStorage.connextStorage();

    // If the asset is the local asset, no swap needed
    if (_asset == _local) {
      return (_amount, _local);
    }

    SwapUtils.Swap storage ipool = s.swapStorages[_key];

    // Calculate the swap using the appropriate pool.
    if (ipool.exists()) {
      // if internal swap pool exists
      uint8 tokenIndexIn = getTokenIndexFromStableSwapPool(_key, _asset);
      uint8 tokenIndexOut = getTokenIndexFromStableSwapPool(_key, _local);
      return (ipool.calculateSwap(tokenIndexIn, tokenIndexOut, _amount), _local);
    } else {
      IStableSwap pool = IStableSwap(getConfig(_key).adoptedToLocalExternalPools);

      return (pool.calculateSwapFromAddress(_asset, _local, _amount), _local);
    }
  }

  // ============ Internal: Token ID Helpers ============

  /**
   * @notice Gets the canonical information for a given candidate.
   * @dev First checks the `address(0)` convention, then checks if the asset given is the
   * adopted asset, then calculates the local address.
   * @return TokenId The canonical token ID information for the given candidate.
   */
  function getCanonicalTokenId(address _candidate, AppStorage storage s) internal view returns (TokenId memory) {
    TokenId memory _canonical;
    // If candidate is address(0), return an empty `_canonical`.
    if (_candidate == address(0)) {
      return _canonical;
    }

    // Check to see if candidate is an adopted asset.
    _canonical = s.adoptedToCanonical[_candidate];
    if (_canonical.domain != 0) {
      // Candidate is an adopted asset, return canonical info.
      return _canonical;
    }

    // Candidate was not adopted; it could be the local address.
    // IFF this domain is the canonical domain, then the local == canonical.
    // Otherwise, it will be the representation asset.
    if (isLocalOrigin(_candidate, s)) {
      // The token originates on this domain, canonical information is the information
      // of the candidate
      _canonical.domain = s.domain;
      _canonical.id = TypeCasts.addressToBytes32(_candidate);
    } else {
      // on a remote domain, return the representation
      _canonical = s.representationToCanonical[_candidate];
    }
    return _canonical;
  }

  /**
   * @notice Determine if token is of local origin (i.e. it is a locally originating contract,
   * and NOT a token deployed by the bridge).
   * @param s AppStorage instance.
   * @return bool true if token is locally originating, false otherwise.
   */
  function isLocalOrigin(address _token, AppStorage storage s) internal view returns (bool) {
    // If the token contract WAS deployed by the bridge, it will be stored in this mapping.
    // If so, the token is NOT of local origin.
    if (s.representationToCanonical[_token].domain != 0) {
      return false;
    }
    // If the contract was NOT deployed by the bridge, but the contract does exist, then it
    // IS of local origin. Returns true if code exists at `_addr`.
    return _token.code.length != 0;
  }

  /**
   * @notice Get the local asset address for a given canonical key, id, and domain.
   * @param _key - The hash of canonical id and domain.
   * @param _id Canonical ID.
   * @param _domain Canonical domain.
   * @param s AppStorage instance.
   * @return address of the the local asset.
   */
  function getLocalAsset(
    bytes32 _key,
    bytes32 _id,
    uint32 _domain,
    AppStorage storage s
  ) internal view returns (address) {
    if (_domain == s.domain) {
      // Token is of local origin
      return TypeCasts.bytes32ToAddress(_id);
    } else {
      // Token is a representation of a token of remote origin
      return getConfig(_key).representation;
    }
  }

  /**
   * @notice Calculates the hash of canonical ID and domain.
   * @dev This hash is used as the key for many asset-related mappings.
   * @param _id Canonical ID.
   * @param _domain Canonical domain.
   * @return bytes32 Canonical hash, used as key for accessing token info from mappings.
   */
  function calculateCanonicalHash(bytes32 _id, uint32 _domain) internal pure returns (bytes32) {
    return keccak256(abi.encode(_id, _domain));
  }

  // ============ Internal: Math ============

  /**
   * @notice This function calculates slippage as a %age of the amount in, and normalizes
   * That to the `_out` decimals.
   *
   * @dev This *ONLY* works for 1:1 assets
   *
   * @param _in The decimals of the asset in / amount in
   * @param _out The decimals of the target asset
   * @param _amountIn The starting amount for the swap
   * @param _slippage The slippage allowed for the swap, in BPS
   * @return uint256 The minimum amount out for the swap
   */
  function calculateSlippageBoundary(
    uint8 _in,
    uint8 _out,
    uint256 _amountIn,
    uint256 _slippage
  ) internal pure returns (uint256) {
    if (_amountIn == 0) {
      return 0;
    }
    // Get the min recieved (in same decimals as _amountIn)
    uint256 min = (_amountIn * (Constants.BPS_FEE_DENOMINATOR - _slippage)) / Constants.BPS_FEE_DENOMINATOR;
    return normalizeDecimals(_in, _out, min);
  }

  /**
   * @notice This function translates the _amount in _in decimals
   * to _out decimals
   *
   * @param _in The decimals of the asset in / amount in
   * @param _out The decimals of the target asset
   * @param _amount The value to normalize to the `_out` decimals
   * @return uint256 Normalized decimals.
   */
  function normalizeDecimals(
    uint8 _in,
    uint8 _out,
    uint256 _amount
  ) internal pure returns (uint256) {
    if (_in == _out) {
      return _amount;
    }
    // Convert this value to the same decimals as _out
    uint256 normalized;
    if (_in < _out) {
      normalized = _amount * (10**(_out - _in));
    } else {
      normalized = _amount / (10**(_in - _out));
    }
    return normalized;
  }
}

File 2 of 38 : BridgeMessage.sol
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity 0.8.17;

// ============ External Imports ============
import {TypedMemView} from "../../../shared/libraries/TypedMemView.sol";
import {TokenId} from "./TokenId.sol";

library BridgeMessage {
  // ============ Libraries ============

  using TypedMemView for bytes;
  using TypedMemView for bytes29;

  // ============ Enums ============

  // WARNING: do NOT re-write the numbers / order
  // of message types in an upgrade;
  // will cause in-flight messages to be mis-interpreted
  // The Types enum it defines the types of `views` that we use in BridgeMessage. A view
  // points to a specific part of the memory and can slice bytes out of it. When we give a `type` to a view,
  // we define the structure of the data it points to, so that we can do easy runtime assertions without
  // having to fetch the whole data from memory and check for ourselves. In BridgeMessage.sol
  // the types of `data` we can have are defined in this enum and may belong to different taxonomies.
  // For example, a `Message` includes a `TokenId` and an Action (a `Transfer`).
  // The Message is a different TYPE of data than a TokenId or Transfer, as TokenId and Transfer live inside
  // the message. For that reason, we define them as different data types and we add them to the same enum
  // for ease of use.
  enum Types {
    Invalid, // 0
    TokenId, // 1
    Message, // 2
    Transfer // 3
  }

  // ============ Constants ============

  uint256 private constant TOKEN_ID_LEN = 36; // 4 bytes domain + 32 bytes id
  uint256 private constant IDENTIFIER_LEN = 1;
  uint256 private constant TRANSFER_LEN = 65; // 1 byte identifier + 32 bytes amount + 32 bytes transfer id

  // ============ Modifiers ============

  /**
   * @notice Asserts a message is of type `_t`
   * @param _view The message
   * @param _t The expected type
   */
  modifier typeAssert(bytes29 _view, Types _t) {
    _view.assertType(uint40(_t));
    _;
  }

  // ============ Internal Functions ============

  /**
   * @notice Checks that Action is valid type
   * @param _action The action
   * @return TRUE if action is valid
   */
  function isValidAction(bytes29 _action) internal pure returns (bool) {
    return isTransfer(_action);
  }

  /**
   * @notice Checks that view is a valid message length
   * @param _view The bytes string
   * @return TRUE if message is valid
   */
  function isValidMessageLength(bytes29 _view) internal pure returns (bool) {
    uint256 _len = _view.len();
    return _len == TOKEN_ID_LEN + TRANSFER_LEN;
  }

  /**
   * @notice Formats an action message
   * @param _tokenId The token ID
   * @param _action The action
   * @return The formatted message
   */
  function formatMessage(bytes29 _tokenId, bytes29 _action)
    internal
    view
    typeAssert(_tokenId, Types.TokenId)
    returns (bytes memory)
  {
    require(isValidAction(_action), "!action");
    bytes29[] memory _views = new bytes29[](2);
    _views[0] = _tokenId;
    _views[1] = _action;
    return TypedMemView.join(_views);
  }

  /**
   * @notice Returns the type of the message
   * @param _view The message
   * @return The type of the message
   */
  function messageType(bytes29 _view) internal pure returns (Types) {
    return Types(uint8(_view.typeOf()));
  }

  /**
   * @notice Checks that the message is of the specified type
   * @param _type the type to check for
   * @param _action The message
   * @return True if the message is of the specified type
   */
  function isType(bytes29 _action, Types _type) internal pure returns (bool) {
    return actionType(_action) == uint8(_type) && messageType(_action) == _type;
  }

  /**
   * @notice Checks that the message is of type Transfer
   * @param _action The message
   * @return True if the message is of type Transfer
   */
  function isTransfer(bytes29 _action) internal pure returns (bool) {
    return isType(_action, Types.Transfer);
  }

  /**
   * @notice Formats Transfer
   * @param _amnt The transfer amount
   * @param _transferId The unique identifier of the transfer
   * @return
   */
  function formatTransfer(uint256 _amnt, bytes32 _transferId) internal pure returns (bytes29) {
    return abi.encodePacked(Types.Transfer, _amnt, _transferId).ref(uint40(Types.Transfer));
  }

  /**
   * @notice Serializes a Token ID struct
   * @param _tokenId The token id struct
   * @return The formatted Token ID
   */
  function formatTokenId(TokenId memory _tokenId) internal pure returns (bytes29) {
    return formatTokenId(_tokenId.domain, _tokenId.id);
  }

  /**
   * @notice Creates a serialized Token ID from components
   * @param _domain The domain
   * @param _id The ID
   * @return The formatted Token ID
   */
  function formatTokenId(uint32 _domain, bytes32 _id) internal pure returns (bytes29) {
    return abi.encodePacked(_domain, _id).ref(uint40(Types.TokenId));
  }

  /**
   * @notice Retrieves the domain from a TokenID
   * @param _tokenId The message
   * @return The domain
   */
  function domain(bytes29 _tokenId) internal pure typeAssert(_tokenId, Types.TokenId) returns (uint32) {
    return uint32(_tokenId.indexUint(0, 4));
  }

  /**
   * @notice Retrieves the ID from a TokenID
   * @param _tokenId The message
   * @return The ID
   */
  function id(bytes29 _tokenId) internal pure typeAssert(_tokenId, Types.TokenId) returns (bytes32) {
    // before = 4 bytes domain
    return _tokenId.index(4, 32);
  }

  /**
   * @notice Retrieves the EVM ID
   * @param _tokenId The message
   * @return The EVM ID
   */
  function evmId(bytes29 _tokenId) internal pure typeAssert(_tokenId, Types.TokenId) returns (address) {
    // before = 4 bytes domain + 12 bytes empty to trim for address
    return _tokenId.indexAddress(16);
  }

  /**
   * @notice Retrieves the action identifier from message
   * @param _message The action
   * @return The message type
   */
  function msgType(bytes29 _message) internal pure returns (uint8) {
    return uint8(_message.indexUint(TOKEN_ID_LEN, 1));
  }

  /**
   * @notice Retrieves the identifier from action
   * @param _action The action
   * @return The action type
   */
  function actionType(bytes29 _action) internal pure returns (uint8) {
    return uint8(_action.indexUint(0, 1));
  }

  /**
   * @notice Retrieves the amount from a Transfer
   * @param _transferAction The message
   * @return The amount
   */
  function amnt(bytes29 _transferAction) internal pure returns (uint256) {
    // before = 1 byte identifier = 1 bytes
    return _transferAction.indexUint(1, 32);
  }

  /**
   * @notice Retrieves the transfer id from a Transfer
   * @param _transferAction The message
   * @return The id
   */
  function transferId(bytes29 _transferAction) internal pure returns (bytes32) {
    // before = 1 byte identifier + 32 bytes amount = 33 bytes
    return _transferAction.index(33, 32);
  }

  /**
   * @notice Retrieves the token ID from a Message
   * @param _message The message
   * @return The ID
   */
  function tokenId(bytes29 _message) internal pure typeAssert(_message, Types.Message) returns (bytes29) {
    return _message.slice(0, TOKEN_ID_LEN, uint40(Types.TokenId));
  }

  /**
   * @notice Retrieves the action data from a Message
   * @param _message The message
   * @return The action
   */
  function action(bytes29 _message) internal pure typeAssert(_message, Types.Message) returns (bytes29) {
    uint256 _actionLen = _message.len() - TOKEN_ID_LEN;
    uint40 _type = uint40(msgType(_message));
    return _message.slice(TOKEN_ID_LEN, _actionLen, _type);
  }

  /**
   * @notice Converts to a Message
   * @param _message The message
   * @return The newly typed message
   */
  function tryAsMessage(bytes29 _message) internal pure returns (bytes29) {
    if (isValidMessageLength(_message)) {
      return _message.castTo(uint40(Types.Message));
    }
    return TypedMemView.nullView();
  }

  /**
   * @notice Asserts that the message is of type Message
   * @param _view The message
   * @return The message
   */
  function mustBeMessage(bytes29 _view) internal pure returns (bytes29) {
    return tryAsMessage(_view).assertValid();
  }
}

File 2 of 38 : Constants.sol
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity 0.8.17;

library Constants {
  // ============= Initial Values =============

  /**
   * @notice Sets the initial lp fee at 5 bps
   */
  uint256 public constant INITIAL_LIQUIDITY_FEE_NUMERATOR = 9_995;

  /**
   * @notice Sets the initial max routers per transfer
   */
  uint256 public constant INITIAL_MAX_ROUTERS = 5;

  /**
   * @notice Sets the initial max routers per transfer
   */
  uint16 public constant INITIAL_AAVE_REFERRAL_CODE = 0;

  // =============

  // ============= Unchangeable Values =============
  // ============= Facets

  /**
   * @notice Reentrancy modifier for diamond
   */
  uint256 internal constant NOT_ENTERED = 1;

  /**
   * @notice Reentrancy modifier for diamond
   */
  uint256 internal constant ENTERED = 2;

  /**
   * @notice Contains hash of empty bytes
   */
  bytes32 internal constant EMPTY_HASH = keccak256("");

  /**
   * @notice Denominator for BPS values
   */
  uint256 public constant BPS_FEE_DENOMINATOR = 10_000;

  /**
   * @notice Value for delay used on governance
   */
  uint256 public constant GOVERNANCE_DELAY = 7 days;

  /**
   * @notice Required gas amount to be leftover after passing in `gasleft` when
   * executing calldata (see `_executeCalldata` method).
   */
  uint256 public constant EXECUTE_CALLDATA_RESERVE_GAS = 10_000;

  /**
   * @notice Portal referral code
   */
  uint16 public constant AAVE_REFERRAL_CODE = 0;

  // ============= ConnextPriceOracle
  /**
   * @notice Valid period for a price delivered by the price oracle
   */
  uint256 public constant ORACLE_VALID_PERIOD = 1 minutes;

  /**
   * @notice Valid wiggle room for future timestamps (3s) used by `setDirectPrice`
   */
  uint256 public constant FUTURE_TIME_BUFFER = 3;

  /**
   * @notice Defalt decimals values are normalized to
   */
  uint8 public constant DEFAULT_NORMALIZED_DECIMALS = uint8(18);

  /**
   * @notice Bytes of return data copied back when using `excessivelySafeCall`
   */
  uint16 public constant DEFAULT_COPY_BYTES = 256;

  /**
   * @notice Valid deadline extension used when swapping (1hr)
   */
  uint256 public constant DEFAULT_DEADLINE_EXTENSION = 3600;

  // ============= Swaps
  /**
   * @notice the precision all pools tokens will be converted to
   * @dev stored here to keep easily in sync between `SwapUtils` and `SwapUtilsExternal`
   *
   * The minimum in a pool is 2 (nextUSDC, USDC), and the maximum allowed is 16. While
   * we do not have pools supporting this number of token, allowing a larger value leaves
   * the possibility open to pool multiple stable local/adopted pairs, garnering greater
   * capital efficiency. 16 specifically was chosen as a bit of a sweet spot between the
   * default of 32 and what we will realistically host in pools.
   */
  uint256 public constant MINIMUM_POOLED_TOKENS = 2;
  uint256 public constant MAXIMUM_POOLED_TOKENS = 16;

  /**
   * @notice the precision all pools tokens will be converted to
   * @dev stored here to keep easily in sync between `SwapUtils` and `SwapUtilsExternal`
   */
  uint8 public constant POOL_PRECISION_DECIMALS = 18;

  /**
   * @notice the denominator used to calculate admin and LP fees. For example, an
   * LP fee might be something like tradeAmount.mul(fee).div(FEE_DENOMINATOR)
   * @dev stored here to keep easily in sync between `SwapUtils` and `SwapUtilsExternal`
   */
  uint256 public constant FEE_DENOMINATOR = 1e10;

  /**
   * @notice Max swap fee is 1% or 100bps of each swap
   * @dev stored here to keep easily in sync between `SwapUtils` and `SwapUtilsExternal`
   */
  uint256 public constant MAX_SWAP_FEE = 1e8;

  /**
   * @notice Max adminFee is 100% of the swapFee. adminFee does not add additional fee on top of swapFee.
   * Instead it takes a certain % of the swapFee. Therefore it has no impact on the
   * users but only on the earnings of LPs
   * @dev stored here to keep easily in sync between `SwapUtils` and `SwapUtilsExternal`
   */
  uint256 public constant MAX_ADMIN_FEE = 1e10;

  /**
   * @notice constant value used as max loop limit
   * @dev stored here to keep easily in sync between `SwapUtils` and `SwapUtilsExternal`
   */
  uint256 public constant MAX_LOOP_LIMIT = 256;

  // Constant value used as max delay time for removing swap after disabled
  uint256 internal constant REMOVE_DELAY = 7 days;

  /**
   * @notice constant values used in ramping A calculations
   * @dev stored here to keep easily in sync between `SwapUtils` and `SwapUtilsExternal`
   */
  uint256 public constant A_PRECISION = 100;
  uint256 public constant MAX_A = 10**6;
  uint256 public constant MAX_A_CHANGE = 2;
  uint256 public constant MIN_RAMP_TIME = 14 days;
  uint256 public constant MIN_RAMP_DELAY = 1 days;
}

File 2 of 38 : LibConnextStorage.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.17;

import {IStableSwap} from "../interfaces/IStableSwap.sol";
import {IConnectorManager} from "../../../messaging/interfaces/IConnectorManager.sol";
import {SwapUtils} from "./SwapUtils.sol";
import {TokenId} from "./TokenId.sol";

/**
 * @notice THIS FILE DEFINES OUR STORAGE LAYOUT AND ID GENERATION SCHEMA. IT CAN ONLY BE MODIFIED FREELY FOR FRESH
 * DEPLOYS. If you are modifiying this file for an upgrade, you must **CAREFULLY** ensure
 * the contract storage layout is not impacted.
 *
 * BE VERY CAREFUL MODIFYING THE VALUES IN THIS FILE!
 */

// ============= Enum =============

/// @notice Enum representing address role
// Returns uint
// None     - 0
// Router   - 1
// Watcher  - 2
// Admin    - 3
enum Role {
  None,
  RouterAdmin,
  Watcher,
  Admin
}

/**
 * @notice Enum representing status of destination transfer
 * @dev Status is only assigned on the destination domain, will always be "none" for the
 * origin domains
 * @return uint - Index of value in enum
 */
enum DestinationTransferStatus {
  None, // 0
  Reconciled, // 1
  Executed, // 2
  Completed // 3 - executed + reconciled
}

/**
 * @notice These are the parameters that will remain constant between the
 * two chains. They are supplied on `xcall` and should be asserted on `execute`
 * @property to - The account that receives funds, in the event of a crosschain call,
 * will receive funds if the call fails.
 *
 * @param originDomain - The originating domain (i.e. where `xcall` is called)
 * @param destinationDomain - The final domain (i.e. where `execute` / `reconcile` are called)\
 * @param canonicalDomain - The canonical domain of the asset you are bridging
 * @param to - The address you are sending funds (and potentially data) to
 * @param delegate - An address who can execute txs on behalf of `to`, in addition to allowing relayers
 * @param receiveLocal - If true, will use the local asset on the destination instead of adopted.
 * @param callData - The data to execute on the receiving chain. If no crosschain call is needed, then leave empty.
 * @param slippage - Slippage user is willing to accept from original amount in expressed in BPS (i.e. if
 * a user takes 1% slippage, this is expressed as 1_000)
 * @param originSender - The msg.sender of the xcall
 * @param bridgedAmt - The amount sent over the bridge (after potential AMM on xcall)
 * @param normalizedIn - The amount sent to `xcall`, normalized to 18 decimals
 * @param nonce - The nonce on the origin domain used to ensure the transferIds are unique
 * @param canonicalId - The unique identifier of the canonical token corresponding to bridge assets
 */
struct TransferInfo {
  uint32 originDomain;
  uint32 destinationDomain;
  uint32 canonicalDomain;
  address to;
  address delegate;
  bool receiveLocal;
  bytes callData;
  uint256 slippage;
  address originSender;
  uint256 bridgedAmt;
  uint256 normalizedIn;
  uint256 nonce;
  bytes32 canonicalId;
}

/**
 * @notice
 * @param params - The TransferInfo. These are consistent across sending and receiving chains.
 * @param routers - The routers who you are sending the funds on behalf of.
 * @param routerSignatures - Signatures belonging to the routers indicating permission to use funds
 * for the signed transfer ID.
 * @param sequencer - The sequencer who assigned the router path to this transfer.
 * @param sequencerSignature - Signature produced by the sequencer for path assignment accountability
 * for the path that was signed.
 */
struct ExecuteArgs {
  TransferInfo params;
  address[] routers;
  bytes[] routerSignatures;
  address sequencer;
  bytes sequencerSignature;
}

/**
 * @notice Contains configs for each router
 * @param approved Whether the router is allowlisted, settable by admin
 * @param portalApproved Whether the router is allowlisted for portals, settable by admin
 * @param routerOwners The address that can update the `recipient`
 * @param proposedRouterOwners Owner candidates
 * @param proposedRouterTimestamp When owner candidate was proposed (there is a delay to acceptance)
 */
struct RouterConfig {
  bool approved;
  bool portalApproved;
  address owner;
  address recipient;
  address proposed;
  uint256 proposedTimestamp;
}

/**
 * @notice Contains configurations for tokens
 * @dev Struct will be stored on the hash of the `canonicalId` and `canonicalDomain`. There are also
 * two separate reverse lookups, that deliver plaintext information based on the passed in address (can
 * either be representation or adopted address passed in).
 *
 * If the decimals are updated in a future token upgrade, the transfers should fail. If that happens, the
 * asset and swaps must be removed, and then they can be readded
 *
 * @param representation Address of minted asset on this domain. If the token is of local origin (meaning it was
 * originally deployed on this chain), this MUST map to address(0).
 * @param representationDecimals Decimals of minted asset on this domain
 * @param adopted Address of adopted asset on this domain
 * @param adoptedDecimals Decimals of adopted asset on this domain
 * @param adoptedToLocalExternalPools Holds the AMMs for swapping in and out of local assets
 * @param approval Allowed assets
 * @param cap Liquidity caps of whitelisted assets. If 0, no cap is enforced.
 * @param custodied Custodied balance by address
 */
struct TokenConfig {
  address representation;
  uint8 representationDecimals;
  address adopted;
  uint8 adoptedDecimals;
  address adoptedToLocalExternalPools;
  bool approval;
  uint256 cap;
  uint256 custodied;
}

struct AppStorage {
  //
  // 0
  bool initialized;
  //
  // Connext
  //
  // 1
  uint256 LIQUIDITY_FEE_NUMERATOR;
  /**
   * @notice The local address that is custodying relayer fees
   */
  // 2
  address relayerFeeVault;
  /**
   * @notice Nonce for the contract, used to keep unique transfer ids.
   * @dev Assigned at first interaction (xcall on origin domain).
   */
  // 3
  uint256 nonce;
  /**
   * @notice The domain this contract exists on.
   * @dev Must match the domain identifier, which is distinct from the "chainId".
   */
  // 4
  uint32 domain;
  /**
   * @notice Mapping of adopted to canonical asset information.
   */
  // 5
  mapping(address => TokenId) adoptedToCanonical;
  /**
   * @notice Mapping of representation to canonical asset information.
   */
  // 6
  mapping(address => TokenId) representationToCanonical;
  /**
   * @notice Mapping of hash(canonicalId, canonicalDomain) to token config on this domain.
   */
  // 7
  mapping(bytes32 => TokenConfig) tokenConfigs;
  /**
   * @notice Mapping to track transfer status on destination domain
   */
  // 8
  mapping(bytes32 => DestinationTransferStatus) transferStatus;
  /**
   * @notice Mapping holding router address that provided fast liquidity.
   */
  // 9
  mapping(bytes32 => address[]) routedTransfers;
  /**
   * @notice Mapping of router to available balance of an asset.
   * @dev Routers should always store liquidity that they can expect to receive via the bridge on
   * this domain (the local asset).
   */
  // 10
  mapping(address => mapping(address => uint256)) routerBalances;
  /**
   * @notice Mapping of approved relayers
   * @dev Send relayer fee if msg.sender is approvedRelayer; otherwise revert.
   */
  // 11
  mapping(address => bool) approvedRelayers;
  /**
   * @notice The max amount of routers a payment can be routed through.
   */
  // 12
  uint256 maxRoutersPerTransfer;
  /**
   * @notice Stores a mapping of transfer id to slippage overrides.
   */
  // 13
  mapping(bytes32 => uint256) slippage;
  /**
   * @notice Stores a mapping of transfer id to receive local overrides.
   */
  // 14
  mapping(bytes32 => bool) receiveLocalOverride;
  /**
   * @notice Stores a mapping of remote routers keyed on domains.
   * @dev Addresses are cast to bytes32.
   * This mapping is required because the Connext now contains the BridgeRouter and must implement
   * the remotes interface.
   */
  // 15
  mapping(uint32 => bytes32) remotes;
  //
  // ProposedOwnable
  //
  // 17
  address _proposed;
  // 18
  uint256 _proposedOwnershipTimestamp;
  // 19
  bool _routerAllowlistRemoved;
  // 20
  uint256 _routerAllowlistTimestamp;
  /**
   * @notice Stores a mapping of address to Roles
   * @dev returns uint representing the enum Role value
   */
  // 21
  mapping(address => Role) roles;
  //
  // RouterFacet
  //
  // 22
  mapping(address => RouterConfig) routerConfigs;
  //
  // ReentrancyGuard
  //
  // 23
  uint256 _status;
  // 24
  uint256 _xcallStatus;
  //
  // StableSwap
  //
  /**
   * @notice Mapping holding the AMM storages for swapping in and out of local assets
   * @dev Swaps for an adopted asset <> local asset (i.e. POS USDC <> nextUSDC on polygon)
   * Struct storing data responsible for automatic market maker functionalities. In order to
   * access this data, this contract uses SwapUtils library. For more details, see SwapUtils.sol.
   */
  // 25
  mapping(bytes32 => SwapUtils.Swap) swapStorages;
  /**
   * @notice Maps token address to an index in the pool. Used to prevent duplicate tokens in the pool.
   * @dev getTokenIndex function also relies on this mapping to retrieve token index.
   */
  // 26
  mapping(bytes32 => mapping(address => uint8)) tokenIndexes;
  /**
   * The address of an existing LPToken contract to use as a target
   * this target must be the address which connext deployed on this chain.
   */
  // 27
  address lpTokenTargetAddress;
  /**
   * @notice Stores whether or not bribing, AMMs, have been paused.
   */
  // 28
  bool _paused;
  //
  // AavePortals
  //
  /**
   * @notice Address of Aave Pool contract.
   */
  // 29
  address aavePool;
  /**
   * @notice Fee percentage numerator for using Portal liquidity.
   * @dev Assumes the same basis points as the liquidity fee.
   */
  // 30
  uint256 aavePortalFeeNumerator;
  /**
   * @notice Mapping to store the transfer liquidity amount provided by Aave Portals.
   */
  // 31
  mapping(bytes32 => uint256) portalDebt;
  /**
   * @notice Mapping to store the transfer liquidity amount provided by Aave Portals.
   */
  // 32
  mapping(bytes32 => uint256) portalFeeDebt;
  /**
   * @notice Mapping of approved sequencers
   * @dev Sequencer address provided must belong to an approved sequencer in order to call `execute`
   * for the fast liquidity route.
   */
  // 33
  mapping(address => bool) approvedSequencers;
  /**
   * @notice Remote connection manager for xapp.
   */
  // 34
  IConnectorManager xAppConnectionManager;
}

library LibConnextStorage {
  function connextStorage() internal pure returns (AppStorage storage ds) {
    assembly {
      ds.slot := 0
    }
  }
}

File 2 of 38 : LibDiamond.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;

/******************************************************************************\
* Author: Nick Mudge <[email protected]> (https://twitter.com/mudgen)
* EIP-2535 Diamonds: https://eips.ethereum.org/EIPS/eip-2535
/******************************************************************************/
import {IDiamondCut} from "../interfaces/IDiamondCut.sol";

// Remember to add the loupe functions from DiamondLoupeFacet to the diamond.
// The loupe functions are required by the EIP2535 Diamonds standard

library LibDiamond {
  bytes32 constant DIAMOND_STORAGE_POSITION = keccak256("diamond.standard.diamond.storage");

  struct FacetAddressAndPosition {
    address facetAddress;
    uint96 functionSelectorPosition; // position in facetFunctionSelectors.functionSelectors array
  }

  struct FacetFunctionSelectors {
    bytes4[] functionSelectors;
    uint256 facetAddressPosition; // position of facetAddress in facetAddresses array
  }

  struct DiamondStorage {
    // maps function selector to the facet address and
    // the position of the selector in the facetFunctionSelectors.selectors array
    mapping(bytes4 => FacetAddressAndPosition) selectorToFacetAndPosition;
    // maps facet addresses to function selectors
    mapping(address => FacetFunctionSelectors) facetFunctionSelectors;
    // facet addresses
    address[] facetAddresses;
    // Used to query if a contract implements an interface.
    // Used to implement ERC-165.
    mapping(bytes4 => bool) supportedInterfaces;
    // owner of the contract
    address contractOwner;
    // hash of proposed facets => acceptance time
    mapping(bytes32 => uint256) acceptanceTimes;
    // acceptance delay for upgrading facets
    uint256 acceptanceDelay;
  }

  function diamondStorage() internal pure returns (DiamondStorage storage ds) {
    bytes32 position = DIAMOND_STORAGE_POSITION;
    assembly {
      ds.slot := position
    }
  }

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

  function setContractOwner(address _newOwner) internal {
    DiamondStorage storage ds = diamondStorage();
    emit OwnershipTransferred(ds.contractOwner, _newOwner);
    ds.contractOwner = _newOwner;
  }

  function contractOwner() internal view returns (address contractOwner_) {
    contractOwner_ = diamondStorage().contractOwner;
  }

  function acceptanceDelay() internal view returns (uint256) {
    return diamondStorage().acceptanceDelay;
  }

  function acceptanceTime(bytes32 _key) internal view returns (uint256) {
    return diamondStorage().acceptanceTimes[_key];
  }

  function enforceIsContractOwner() internal view {
    require(msg.sender == diamondStorage().contractOwner, "LibDiamond: !contract owner");
  }

  event DiamondCutProposed(IDiamondCut.FacetCut[] _diamondCut, address _init, bytes _calldata, uint256 deadline);

  function proposeDiamondCut(
    IDiamondCut.FacetCut[] memory _diamondCut,
    address _init,
    bytes memory _calldata
  ) internal {
    // NOTE: to save gas, verification that `proposeDiamondCut` and `diamondCut` are not
    // included is performed in `diamondCut`, where there is already a loop over facets.
    // In the case where these cuts are performed, admins must call `rescindDiamondCut`

    DiamondStorage storage ds = diamondStorage();
    uint256 acceptance = block.timestamp + ds.acceptanceDelay;
    ds.acceptanceTimes[keccak256(abi.encode(_diamondCut, _init, _calldata))] = acceptance;
    emit DiamondCutProposed(_diamondCut, _init, _calldata, acceptance);
  }

  event DiamondCutRescinded(IDiamondCut.FacetCut[] _diamondCut, address _init, bytes _calldata);

  function rescindDiamondCut(
    IDiamondCut.FacetCut[] memory _diamondCut,
    address _init,
    bytes memory _calldata
  ) internal {
    // NOTE: you can always rescind a proposed facet cut as the owner, even if outside of the validity
    // period or befor the delay elpases
    delete diamondStorage().acceptanceTimes[keccak256(abi.encode(_diamondCut, _init, _calldata))];
    emit DiamondCutRescinded(_diamondCut, _init, _calldata);
  }

  event DiamondCut(IDiamondCut.FacetCut[] _diamondCut, address _init, bytes _calldata);

  // Internal function version of diamondCut
  function diamondCut(
    IDiamondCut.FacetCut[] memory _diamondCut,
    address _init,
    bytes memory _calldata
  ) internal {
    DiamondStorage storage ds = diamondStorage();
    bytes32 key = keccak256(abi.encode(_diamondCut, _init, _calldata));
    if (ds.facetAddresses.length != 0) {
      uint256 time = ds.acceptanceTimes[key];
      require(time != 0 && time <= block.timestamp, "LibDiamond: delay not elapsed");
      // Reset the acceptance time to ensure the same set of updates cannot be replayed
      // without going through a proposal window

      // NOTE: the only time this will not be set to 0 is when there are no
      // existing facet addresses (on initialization, or when starting after a bad upgrade,
      // for example).
      // The only relevant case is the initial case, which has no acceptance time. otherwise,
      // there is no way to update the facet selector mapping to call `diamondCut`.
      // Avoiding setting the empty value will save gas on the initial deployment.
      delete ds.acceptanceTimes[key];
    } // Otherwise, this is the first instance of deployment and it can be set automatically
    uint256 len = _diamondCut.length;
    for (uint256 facetIndex; facetIndex < len; ) {
      IDiamondCut.FacetCutAction action = _diamondCut[facetIndex].action;
      if (action == IDiamondCut.FacetCutAction.Add) {
        addFunctions(_diamondCut[facetIndex].facetAddress, _diamondCut[facetIndex].functionSelectors);
      } else if (action == IDiamondCut.FacetCutAction.Replace) {
        replaceFunctions(_diamondCut[facetIndex].facetAddress, _diamondCut[facetIndex].functionSelectors);
      } else if (action == IDiamondCut.FacetCutAction.Remove) {
        removeFunctions(_diamondCut[facetIndex].facetAddress, _diamondCut[facetIndex].functionSelectors);
      } else {
        revert("LibDiamondCut: Incorrect FacetCutAction");
      }

      unchecked {
        ++facetIndex;
      }
    }
    emit DiamondCut(_diamondCut, _init, _calldata);
    initializeDiamondCut(_init, _calldata);
  }

  function addFunctions(address _facetAddress, bytes4[] memory _functionSelectors) internal {
    require(_functionSelectors.length != 0, "LibDiamondCut: No selectors in facet to cut");
    DiamondStorage storage ds = diamondStorage();
    require(_facetAddress != address(0), "LibDiamondCut: Add facet can't be address(0)");
    uint96 selectorPosition = uint96(ds.facetFunctionSelectors[_facetAddress].functionSelectors.length);
    // add new facet address if it does not exist
    if (selectorPosition == 0) {
      addFacet(ds, _facetAddress);
    }
    uint256 len = _functionSelectors.length;
    for (uint256 selectorIndex; selectorIndex < len; ) {
      bytes4 selector = _functionSelectors[selectorIndex];
      address oldFacetAddress = ds.selectorToFacetAndPosition[selector].facetAddress;
      require(oldFacetAddress == address(0), "LibDiamondCut: Can't add function that already exists");
      addFunction(ds, selector, selectorPosition, _facetAddress);
      selectorPosition++;

      unchecked {
        ++selectorIndex;
      }
    }
  }

  function replaceFunctions(address _facetAddress, bytes4[] memory _functionSelectors) internal {
    uint256 len = _functionSelectors.length;
    require(len != 0, "LibDiamondCut: No selectors in facet to cut");
    DiamondStorage storage ds = diamondStorage();
    require(_facetAddress != address(0), "LibDiamondCut: Add facet can't be address(0)");
    uint96 selectorPosition = uint96(ds.facetFunctionSelectors[_facetAddress].functionSelectors.length);
    // add new facet address if it does not exist
    if (selectorPosition == 0) {
      addFacet(ds, _facetAddress);
    }
    for (uint256 selectorIndex; selectorIndex < len; ) {
      bytes4 selector = _functionSelectors[selectorIndex];
      address oldFacetAddress = ds.selectorToFacetAndPosition[selector].facetAddress;
      require(oldFacetAddress != _facetAddress, "LibDiamondCut: Can't replace function with same function");
      removeFunction(ds, oldFacetAddress, selector);
      addFunction(ds, selector, selectorPosition, _facetAddress);
      selectorPosition++;

      unchecked {
        ++selectorIndex;
      }
    }
  }

  function removeFunctions(address _facetAddress, bytes4[] memory _functionSelectors) internal {
    require(_functionSelectors.length != 0, "LibDiamondCut: No selectors in facet to cut");
    DiamondStorage storage ds = diamondStorage();
    // get the propose and cut selectors -- can never remove these
    bytes4 proposeSelector = IDiamondCut.proposeDiamondCut.selector;
    bytes4 cutSelector = IDiamondCut.diamondCut.selector;
    // if function does not exist then do nothing and return
    require(_facetAddress == address(0), "LibDiamondCut: Remove facet address must be address(0)");
    uint256 len = _functionSelectors.length;
    for (uint256 selectorIndex; selectorIndex < len; ) {
      bytes4 selector = _functionSelectors[selectorIndex];
      require(selector != proposeSelector && selector != cutSelector, "LibDiamondCut: Cannot remove cut selectors");
      address oldFacetAddress = ds.selectorToFacetAndPosition[selector].facetAddress;
      removeFunction(ds, oldFacetAddress, selector);

      unchecked {
        ++selectorIndex;
      }
    }
  }

  function addFacet(DiamondStorage storage ds, address _facetAddress) internal {
    enforceHasContractCode(_facetAddress, "LibDiamondCut: New facet has no code");
    ds.facetFunctionSelectors[_facetAddress].facetAddressPosition = ds.facetAddresses.length;
    ds.facetAddresses.push(_facetAddress);
  }

  function addFunction(
    DiamondStorage storage ds,
    bytes4 _selector,
    uint96 _selectorPosition,
    address _facetAddress
  ) internal {
    ds.selectorToFacetAndPosition[_selector].functionSelectorPosition = _selectorPosition;
    ds.facetFunctionSelectors[_facetAddress].functionSelectors.push(_selector);
    ds.selectorToFacetAndPosition[_selector].facetAddress = _facetAddress;
  }

  function removeFunction(
    DiamondStorage storage ds,
    address _facetAddress,
    bytes4 _selector
  ) internal {
    require(_facetAddress != address(0), "LibDiamondCut: Can't remove function that doesn't exist");
    // an immutable function is a function defined directly in a diamond
    require(_facetAddress != address(this), "LibDiamondCut: Can't remove immutable function");
    // replace selector with last selector, then delete last selector
    uint256 selectorPosition = ds.selectorToFacetAndPosition[_selector].functionSelectorPosition;
    uint256 lastSelectorPosition = ds.facetFunctionSelectors[_facetAddress].functionSelectors.length - 1;
    // if not the same then replace _selector with lastSelector
    if (selectorPosition != lastSelectorPosition) {
      bytes4 lastSelector = ds.facetFunctionSelectors[_facetAddress].functionSelectors[lastSelectorPosition];
      ds.facetFunctionSelectors[_facetAddress].functionSelectors[selectorPosition] = lastSelector;
      ds.selectorToFacetAndPosition[lastSelector].functionSelectorPosition = uint96(selectorPosition);
    }
    // delete the last selector
    ds.facetFunctionSelectors[_facetAddress].functionSelectors.pop();
    delete ds.selectorToFacetAndPosition[_selector];

    // if no more selectors for facet address then delete the facet address
    if (lastSelectorPosition == 0) {
      // replace facet address with last facet address and delete last facet address
      uint256 lastFacetAddressPosition = ds.facetAddresses.length - 1;
      uint256 facetAddressPosition = ds.facetFunctionSelectors[_facetAddress].facetAddressPosition;
      if (facetAddressPosition != lastFacetAddressPosition) {
        address lastFacetAddress = ds.facetAddresses[lastFacetAddressPosition];
        ds.facetAddresses[facetAddressPosition] = lastFacetAddress;
        ds.facetFunctionSelectors[lastFacetAddress].facetAddressPosition = facetAddressPosition;
      }
      ds.facetAddresses.pop();
      delete ds.facetFunctionSelectors[_facetAddress].facetAddressPosition;
    }
  }

  function initializeDiamondCut(address _init, bytes memory _calldata) internal {
    if (_init == address(0)) {
      require(_calldata.length == 0, "LibDiamondCut: _init is address(0) but_calldata is not empty");
    } else {
      require(_calldata.length != 0, "LibDiamondCut: _calldata is empty but _init is not address(0)");
      if (_init != address(this)) {
        enforceHasContractCode(_init, "LibDiamondCut: _init address has no code");
      }
      (bool success, bytes memory error) = _init.delegatecall(_calldata);
      if (!success) {
        if (error.length != 0) {
          // bubble up the error
          revert(string(error));
        } else {
          revert("LibDiamondCut: _init function reverted");
        }
      }
    }
  }

  function enforceHasContractCode(address _contract, string memory _errorMessage) internal view {
    require(_contract.code.length != 0, _errorMessage);
  }
}

File 2 of 38 : MathUtils.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.17;

/**
 * @title MathUtils library
 * @notice A library to be used in conjunction with SafeMath. Contains functions for calculating
 * differences between two uint256.
 */
library MathUtils {
  /**
   * @notice Compares a and b and returns true if the difference between a and b
   *         is less than 1 or equal to each other.
   * @param a uint256 to compare with
   * @param b uint256 to compare with
   * @return True if the difference between a and b is less than 1 or equal,
   *         otherwise return false
   */
  function within1(uint256 a, uint256 b) internal pure returns (bool) {
    return (difference(a, b) < 1 + 1); // instead of <=1
  }

  /**
   * @notice Calculates absolute difference between a and b
   * @param a uint256 to compare with
   * @param b uint256 to compare with
   * @return Difference between a and b
   */
  function difference(uint256 a, uint256 b) internal pure returns (uint256) {
    if (a > b) {
      return a - b;
    }
    return b - a;
  }
}

File 2 of 38 : SwapUtils.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.17;

import {SafeERC20, IERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

import {LPToken} from "../helpers/LPToken.sol";

import {AmplificationUtils} from "./AmplificationUtils.sol";
import {MathUtils} from "./MathUtils.sol";
import {AssetLogic} from "./AssetLogic.sol";
import {Constants} from "./Constants.sol";

/**
 * @title SwapUtils library
 * @notice A library to be used within Swap.sol. Contains functions responsible for custody and AMM functionalities.
 * @dev Contracts relying on this library must initialize SwapUtils.Swap struct then use this library
 * for SwapUtils.Swap struct. Note that this library contains both functions called by users and admins.
 * Admin functions should be protected within contracts using this library.
 */
library SwapUtils {
  using SafeERC20 for IERC20;
  using MathUtils for uint256;

  /*** EVENTS ***/

  event TokenSwap(
    bytes32 indexed key,
    address indexed buyer,
    uint256 tokensSold,
    uint256 tokensBought,
    uint128 soldId,
    uint128 boughtId
  );
  event AddLiquidity(
    bytes32 indexed key,
    address indexed provider,
    uint256[] tokenAmounts,
    uint256[] fees,
    uint256 invariant,
    uint256 lpTokenSupply
  );
  event RemoveLiquidity(bytes32 indexed key, address indexed provider, uint256[] tokenAmounts, uint256 lpTokenSupply);
  event RemoveLiquidityOne(
    bytes32 indexed key,
    address indexed provider,
    uint256 lpTokenAmount,
    uint256 lpTokenSupply,
    uint256 boughtId,
    uint256 tokensBought
  );
  event RemoveLiquidityImbalance(
    bytes32 indexed key,
    address indexed provider,
    uint256[] tokenAmounts,
    uint256[] fees,
    uint256 invariant,
    uint256 lpTokenSupply
  );
  event NewAdminFee(bytes32 indexed key, uint256 newAdminFee);
  event NewSwapFee(bytes32 indexed key, uint256 newSwapFee);

  struct Swap {
    // variables around the ramp management of A,
    // the amplification coefficient * n ** (n - 1)
    // see Curve stableswap paper for details
    bytes32 key;
    uint256 initialA;
    uint256 futureA;
    uint256 initialATime;
    uint256 futureATime;
    // fee calculation
    uint256 swapFee;
    uint256 adminFee;
    LPToken lpToken;
    // contract references for all tokens being pooled
    IERC20[] pooledTokens;
    // multipliers for each pooled token's precision to get to Constants.POOL_PRECISION_DECIMALS
    // for example, TBTC has 18 decimals, so the multiplier should be 1. WBTC
    // has 8, so the multiplier should be 10 ** 18 / 10 ** 8 => 10 ** 10
    uint256[] tokenPrecisionMultipliers;
    // the pool balance of each token, in the token's precision
    // the contract's actual token balance might differ
    uint256[] balances;
    // the admin fee balance of each token, in the token's precision
    uint256[] adminFees;
    // the flag if this pool disabled by admin. once disabled, only remove liquidity will work.
    bool disabled;
    // once pool disabled, admin can remove pool after passed removeTime. and reinitialize.
    uint256 removeTime;
  }

  // Struct storing variables used in calculations in the
  // calculateWithdrawOneTokenDY function to avoid stack too deep errors
  struct CalculateWithdrawOneTokenDYInfo {
    uint256 d0;
    uint256 d1;
    uint256 newY;
    uint256 feePerToken;
    uint256 preciseA;
  }

  // Struct storing variables used in calculations in the
  // {add,remove}Liquidity functions to avoid stack too deep errors
  struct ManageLiquidityInfo {
    uint256 d0;
    uint256 d1;
    uint256 d2;
    uint256 preciseA;
    LPToken lpToken;
    uint256 totalSupply;
    uint256[] balances;
    uint256[] multipliers;
  }

  /*** VIEW & PURE FUNCTIONS ***/

  function _getAPrecise(Swap storage self) private view returns (uint256) {
    return AmplificationUtils._getAPrecise(self);
  }

  /**
   * @notice Calculate the dy, the amount of selected token that user receives and
   * the fee of withdrawing in one token
   * @param tokenAmount the amount to withdraw in the pool's precision
   * @param tokenIndex which token will be withdrawn
   * @param self Swap struct to read from
   * @return the amount of token user will receive
   */
  function calculateWithdrawOneToken(
    Swap storage self,
    uint256 tokenAmount,
    uint8 tokenIndex
  ) internal view returns (uint256) {
    (uint256 availableTokenAmount, ) = _calculateWithdrawOneToken(
      self,
      tokenAmount,
      tokenIndex,
      self.lpToken.totalSupply()
    );
    return availableTokenAmount;
  }

  function _calculateWithdrawOneToken(
    Swap storage self,
    uint256 tokenAmount,
    uint8 tokenIndex,
    uint256 totalSupply
  ) private view returns (uint256, uint256) {
    uint256 dy;
    uint256 newY;
    uint256 currentY;

    (dy, newY, currentY) = calculateWithdrawOneTokenDY(self, tokenIndex, tokenAmount, totalSupply);

    // dy_0 (without fees)
    // dy, dy_0 - dy

    uint256 dySwapFee = (currentY - newY) / self.tokenPrecisionMultipliers[tokenIndex] - dy;

    return (dy, dySwapFee);
  }

  /**
   * @notice Calculate the dy of withdrawing in one token
   * @param self Swap struct to read from
   * @param tokenIndex which token will be withdrawn
   * @param tokenAmount the amount to withdraw in the pools precision
   * @return the d and the new y after withdrawing one token
   */
  function calculateWithdrawOneTokenDY(
    Swap storage self,
    uint8 tokenIndex,
    uint256 tokenAmount,
    uint256 totalSupply
  )
    internal
    view
    returns (
      uint256,
      uint256,
      uint256
    )
  {
    // Get the current D, then solve the stableswap invariant
    // y_i for D - tokenAmount
    uint256[] memory xp = _xp(self);

    require(tokenIndex < xp.length, "index out of range");

    CalculateWithdrawOneTokenDYInfo memory v = CalculateWithdrawOneTokenDYInfo(0, 0, 0, 0, 0);
    v.preciseA = _getAPrecise(self);
    v.d0 = getD(xp, v.preciseA);
    v.d1 = v.d0 - ((tokenAmount * v.d0) / totalSupply);

    require(tokenAmount <= xp[tokenIndex], "exceeds available");

    v.newY = getYD(v.preciseA, tokenIndex, xp, v.d1);

    uint256[] memory xpReduced = new uint256[](xp.length);

    v.feePerToken = _feePerToken(self.swapFee, xp.length);
    // TODO: Set a length variable (at top) instead of reading xp.length on each loop.
    uint256 len = xp.length;
    for (uint256 i; i < len; ) {
      uint256 xpi = xp[i];
      // if i == tokenIndex, dxExpected = xp[i] * d1 / d0 - newY
      // else dxExpected = xp[i] - (xp[i] * d1 / d0)
      // xpReduced[i] -= dxExpected * fee / Constants.FEE_DENOMINATOR
      xpReduced[i] =
        xpi -
        ((((i == tokenIndex) ? ((xpi * v.d1) / v.d0 - v.newY) : (xpi - (xpi * v.d1) / v.d0)) * v.feePerToken) /
          Constants.FEE_DENOMINATOR);

      unchecked {
        ++i;
      }
    }

    uint256 dy = xpReduced[tokenIndex] - getYD(v.preciseA, tokenIndex, xpReduced, v.d1);
    dy = (dy - 1) / (self.tokenPrecisionMultipliers[tokenIndex]);

    return (dy, v.newY, xp[tokenIndex]);
  }

  /**
   * @notice Calculate the price of a token in the pool with given
   * precision-adjusted balances and a particular D.
   *
   * @dev This is accomplished via solving the invariant iteratively.
   * See the StableSwap paper and Curve.fi implementation for further details.
   *
   * x_1**2 + x1 * (sum' - (A*n**n - 1) * D / (A * n**n)) = D ** (n + 1) / (n ** (2 * n) * prod' * A)
   * x_1**2 + b*x_1 = c
   * x_1 = (x_1**2 + c) / (2*x_1 + b)
   *
   * @param a the amplification coefficient * n ** (n - 1). See the StableSwap paper for details.
   * @param tokenIndex Index of token we are calculating for.
   * @param xp a precision-adjusted set of pool balances. Array should be
   * the same cardinality as the pool.
   * @param d the stableswap invariant
   * @return the price of the token, in the same precision as in xp
   */
  function getYD(
    uint256 a,
    uint8 tokenIndex,
    uint256[] memory xp,
    uint256 d
  ) internal pure returns (uint256) {
    uint256 numTokens = xp.length;
    require(tokenIndex < numTokens, "Token not found");

    uint256 c = d;
    uint256 s;
    uint256 nA = a * numTokens;

    for (uint256 i; i < numTokens; ) {
      if (i != tokenIndex) {
        s += xp[i];
        c = (c * d) / (xp[i] * numTokens);
        // If we were to protect the division loss we would have to keep the denominator separate
        // and divide at the end. However this leads to overflow with large numTokens or/and D.
        // c = c * D * D * D * ... overflow!
      }

      unchecked {
        ++i;
      }
    }
    c = (c * d * Constants.A_PRECISION) / (nA * numTokens);

    uint256 b = s + ((d * Constants.A_PRECISION) / nA);
    uint256 yPrev;
    // Select d as the starting point of the Newton method. Because y < D
    // D is the best option as the starting point in case the pool is very imbalanced.
    uint256 y = d;
    for (uint256 i; i < Constants.MAX_LOOP_LIMIT; ) {
      yPrev = y;
      y = ((y * y) + c) / ((y * 2) + b - d);
      if (y.within1(yPrev)) {
        return y;
      }

      unchecked {
        ++i;
      }
    }
    revert("Approximation did not converge");
  }

  /**
   * @notice Get D, the StableSwap invariant, based on a set of balances and a particular A.
   * @param xp a precision-adjusted set of pool balances. Array should be the same cardinality
   * as the pool.
   * @param a the amplification coefficient * n ** (n - 1) in A_PRECISION.
   * See the StableSwap paper for details
   * @return the invariant, at the precision of the pool
   */
  function getD(uint256[] memory xp, uint256 a) internal pure returns (uint256) {
    uint256 numTokens = xp.length;
    uint256 s;
    for (uint256 i; i < numTokens; ) {
      s += xp[i];

      unchecked {
        ++i;
      }
    }
    if (s == 0) {
      return 0;
    }

    uint256 prevD;
    uint256 d = s;
    uint256 nA = a * numTokens;

    for (uint256 i; i < Constants.MAX_LOOP_LIMIT; ) {
      uint256 dP = d;
      for (uint256 j; j < numTokens; ) {
        dP = (dP * d) / (xp[j] * numTokens);
        // If we were to protect the division loss we would have to keep the denominator separate
        // and divide at the end. However this leads to overflow with large numTokens or/and D.
        // dP = dP * D * D * D * ... overflow!

        unchecked {
          ++j;
        }
      }
      prevD = d;
      d =
        (((nA * s) / Constants.A_PRECISION + dP * numTokens) * d) /
        ((((nA - Constants.A_PRECISION) * d) / Constants.A_PRECISION + (numTokens + 1) * dP));
      if (d.within1(prevD)) {
        return d;
      }

      unchecked {
        ++i;
      }
    }

    // Convergence should occur in 4 loops or less. If this is reached, there may be something wrong
    // with the pool. If this were to occur repeatedly, LPs should withdraw via `removeLiquidity()`
    // function which does not rely on D.
    revert("D does not converge");
  }

  /**
   * @notice Given a set of balances and precision multipliers, return the
   * precision-adjusted balances.
   *
   * @param balances an array of token balances, in their native precisions.
   * These should generally correspond with pooled tokens.
   *
   * @param precisionMultipliers an array of multipliers, corresponding to
   * the amounts in the balances array. When multiplied together they
   * should yield amounts at the pool's precision.
   *
   * @return an array of amounts "scaled" to the pool's precision
   */
  function _xp(uint256[] memory balances, uint256[] memory precisionMultipliers)
    internal
    pure
    returns (uint256[] memory)
  {
    uint256 numTokens = balances.length;
    require(numTokens == precisionMultipliers.length, "mismatch multipliers");
    uint256[] memory xp = new uint256[](numTokens);
    for (uint256 i; i < numTokens; ) {
      xp[i] = balances[i] * precisionMultipliers[i];

      unchecked {
        ++i;
      }
    }
    return xp;
  }

  /**
   * @notice Return the precision-adjusted balances of all tokens in the pool
   * @param self Swap struct to read from
   * @return the pool balances "scaled" to the pool's precision, allowing
   * them to be more easily compared.
   */
  function _xp(Swap storage self) internal view returns (uint256[] memory) {
    return _xp(self.balances, self.tokenPrecisionMultipliers);
  }

  /**
   * @notice Get the virtual price, to help calculate profit
   * @param self Swap struct to read from
   * @return the virtual price, scaled to precision of Constants.POOL_PRECISION_DECIMALS
   */
  function getVirtualPrice(Swap storage self) internal view returns (uint256) {
    uint256 d = getD(_xp(self), _getAPrecise(self));
    LPToken lpToken = self.lpToken;
    uint256 supply = lpToken.totalSupply();
    if (supply != 0) {
      return (d * (10**uint256(Constants.POOL_PRECISION_DECIMALS))) / supply;
    }
    return 0;
  }

  /**
   * @notice Calculate the new balances of the tokens given the indexes of the token
   * that is swapped from (FROM) and the token that is swapped to (TO).
   * This function is used as a helper function to calculate how much TO token
   * the user should receive on swap.
   *
   * @param preciseA precise form of amplification coefficient
   * @param tokenIndexFrom index of FROM token
   * @param tokenIndexTo index of TO token
   * @param x the new total amount of FROM token
   * @param xp balances of the tokens in the pool
   * @return the amount of TO token that should remain in the pool
   */
  function getY(
    uint256 preciseA,
    uint8 tokenIndexFrom,
    uint8 tokenIndexTo,
    uint256 x,
    uint256[] memory xp
  ) internal pure returns (uint256) {
    uint256 numTokens = xp.length;
    require(tokenIndexFrom != tokenIndexTo, "compare token to itself");
    require(tokenIndexFrom < numTokens && tokenIndexTo < numTokens, "token not found");

    uint256 d = getD(xp, preciseA);
    uint256 c = d;
    uint256 s;
    uint256 nA = numTokens * preciseA;

    uint256 _x;
    for (uint256 i; i < numTokens; ) {
      if (i == tokenIndexFrom) {
        _x = x;
      } else if (i != tokenIndexTo) {
        _x = xp[i];
      } else {
        unchecked {
          ++i;
        }
        continue;
      }
      s += _x;
      c = (c * d) / (_x * numTokens);
      // If we were to protect the division loss we would have to keep the denominator separate
      // and divide at the end. However this leads to overflow with large numTokens or/and D.
      // c = c * D * D * D * ... overflow!

      unchecked {
        ++i;
      }
    }
    c = (c * d * Constants.A_PRECISION) / (nA * numTokens);
    uint256 b = s + ((d * Constants.A_PRECISION) / nA);
    uint256 yPrev;
    uint256 y = d;

    // iterative approximation
    for (uint256 i; i < Constants.MAX_LOOP_LIMIT; ) {
      yPrev = y;
      y = ((y * y) + c) / ((y * 2) + b - d);
      if (y.within1(yPrev)) {
        return y;
      }

      unchecked {
        ++i;
      }
    }
    revert("Approximation did not converge");
  }

  /**
   * @notice Externally calculates a swap between two tokens.
   * @param self Swap struct to read from
   * @param tokenIndexFrom the token to sell
   * @param tokenIndexTo the token to buy
   * @param dx the number of tokens to sell. If the token charges a fee on transfers,
   * use the amount that gets transferred after the fee.
   * @return dy the number of tokens the user will get
   */
  function calculateSwap(
    Swap storage self,
    uint8 tokenIndexFrom,
    uint8 tokenIndexTo,
    uint256 dx
  ) internal view returns (uint256 dy) {
    (dy, ) = _calculateSwap(self, tokenIndexFrom, tokenIndexTo, dx, self.balances);
  }

  /**
   * @notice Externally calculates a swap between two tokens.
   * @param self Swap struct to read from
   * @param tokenIndexFrom the token to sell
   * @param tokenIndexTo the token to buy
   * @param dy the number of tokens to buy.
   * @return dx the number of tokens the user have to transfer + fee
   */
  function calculateSwapInv(
    Swap storage self,
    uint8 tokenIndexFrom,
    uint8 tokenIndexTo,
    uint256 dy
  ) internal view returns (uint256 dx) {
    (dx, ) = _calculateSwapInv(self, tokenIndexFrom, tokenIndexTo, dy, self.balances);
  }

  /**
   * @notice Internally calculates a swap between two tokens.
   *
   * @dev The caller is expected to transfer the actual amounts (dx and dy)
   * using the token contracts.
   *
   * @param self Swap struct to read from
   * @param tokenIndexFrom the token to sell
   * @param tokenIndexTo the token to buy
   * @param dx the number of tokens to sell. If the token charges a fee on transfers,
   * use the amount that gets transferred after the fee.
   * @return dy the number of tokens the user will get in the token's precision. ex WBTC -> 8
   * @return dyFee the associated fee in multiplied precision (Constants.POOL_PRECISION_DECIMALS)
   */
  function _calculateSwap(
    Swap storage self,
    uint8 tokenIndexFrom,
    uint8 tokenIndexTo,
    uint256 dx,
    uint256[] memory balances
  ) internal view returns (uint256 dy, uint256 dyFee) {
    uint256[] memory multipliers = self.tokenPrecisionMultipliers;
    uint256[] memory xp = _xp(balances, multipliers);
    require(tokenIndexFrom < xp.length && tokenIndexTo < xp.length, "index out of range");
    uint256 x = dx * multipliers[tokenIndexFrom] + xp[tokenIndexFrom];
    uint256 y = getY(_getAPrecise(self), tokenIndexFrom, tokenIndexTo, x, xp);
    dy = xp[tokenIndexTo] - y - 1;
    dyFee = (dy * self.swapFee) / Constants.FEE_DENOMINATOR;
    dy = (dy - dyFee) / multipliers[tokenIndexTo];
  }

  /**
   * @notice Internally calculates a swap between two tokens.
   *
   * @dev The caller is expected to transfer the actual amounts (dx and dy)
   * using the token contracts.
   *
   * @param self Swap struct to read from
   * @param tokenIndexFrom the token to sell
   * @param tokenIndexTo the token to buy
   * @param dy the number of tokens to buy. If the token charges a fee on transfers,
   * use the amount that gets transferred after the fee.
   * @return dx the number of tokens the user have to deposit in the token's precision. ex WBTC -> 8
   * @return dxFee the associated fee in multiplied precision (Constants.POOL_PRECISION_DECIMALS)
   */
  function _calculateSwapInv(
    Swap storage self,
    uint8 tokenIndexFrom,
    uint8 tokenIndexTo,
    uint256 dy,
    uint256[] memory balances
  ) internal view returns (uint256 dx, uint256 dxFee) {
    require(tokenIndexFrom != tokenIndexTo, "compare token to itself");
    uint256[] memory multipliers = self.tokenPrecisionMultipliers;
    uint256[] memory xp = _xp(balances, multipliers);
    require(tokenIndexFrom < xp.length && tokenIndexTo < xp.length, "index out of range");

    uint256 a = _getAPrecise(self);
    uint256 d0 = getD(xp, a);

    xp[tokenIndexTo] = xp[tokenIndexTo] - (dy * multipliers[tokenIndexTo]);
    uint256 x = getYD(a, tokenIndexFrom, xp, d0);
    dx = (x + 1) - xp[tokenIndexFrom];
    dxFee = (dx * self.swapFee) / Constants.FEE_DENOMINATOR;
    dx = (dx + dxFee) / multipliers[tokenIndexFrom];
  }

  /**
   * @notice A simple method to calculate amount of each underlying
   * tokens that is returned upon burning given amount of
   * LP tokens
   *
   * @param amount the amount of LP tokens that would to be burned on
   * withdrawal
   * @return array of amounts of tokens user will receive
   */
  function calculateRemoveLiquidity(Swap storage self, uint256 amount) internal view returns (uint256[] memory) {
    return _calculateRemoveLiquidity(self.balances, amount, self.lpToken.totalSupply());
  }

  function _calculateRemoveLiquidity(
    uint256[] memory balances,
    uint256 amount,
    uint256 totalSupply
  ) internal pure returns (uint256[] memory) {
    require(amount <= totalSupply, "exceed total supply");

    uint256 numBalances = balances.length;
    uint256[] memory amounts = new uint256[](numBalances);

    for (uint256 i; i < numBalances; ) {
      amounts[i] = (balances[i] * amount) / totalSupply;

      unchecked {
        ++i;
      }
    }
    return amounts;
  }

  /**
   * @notice A simple method to calculate prices from deposits or
   * withdrawals, excluding fees but including slippage. This is
   * helpful as an input into the various "min" parameters on calls
   * to fight front-running
   *
   * @dev This shouldn't be used outside frontends for user estimates.
   *
   * @param self Swap struct to read from
   * @param amounts an array of token amounts to deposit or withdrawal,
   * corresponding to pooledTokens. The amount should be in each
   * pooled token's native precision. If a token charges a fee on transfers,
   * use the amount that gets transferred after the fee.
   * @param deposit whether this is a deposit or a withdrawal
   * @return if deposit was true, total amount of lp token that will be minted and if
   * deposit was false, total amount of lp token that will be burned
   */
  function calculateTokenAmount(
    Swap storage self,
    uint256[] calldata amounts,
    bool deposit
  ) internal view returns (uint256) {
    uint256[] memory balances = self.balances;
    uint256 numBalances = balances.length;
    require(amounts.length == numBalances, "invalid length of amounts");

    uint256 a = _getAPrecise(self);
    uint256[] memory multipliers = self.tokenPrecisionMultipliers;

    uint256 d0 = getD(_xp(balances, multipliers), a);
    for (uint256 i; i < numBalances; ) {
      if (deposit) {
        balances[i] = balances[i] + amounts[i];
      } else {
        balances[i] = balances[i] - amounts[i];
      }

      unchecked {
        ++i;
      }
    }
    uint256 d1 = getD(_xp(balances, multipliers), a);
    uint256 totalSupply = self.lpToken.totalSupply();

    if (deposit) {
      return ((d1 - d0) * totalSupply) / d0;
    } else {
      return ((d0 - d1) * totalSupply) / d0;
    }
  }

  /**
   * @notice return accumulated amount of admin fees of the token with given index
   * @param self Swap struct to read from
   * @param index Index of the pooled token
   * @return admin balance in the token's precision
   */
  function getAdminBalance(Swap storage self, uint256 index) internal view returns (uint256) {
    require(index < self.pooledTokens.length, "index out of range");
    return self.adminFees[index];
  }

  /**
   * @notice internal helper function to calculate fee per token multiplier used in
   * swap fee calculations
   * @param swapFee swap fee for the tokens
   * @param numTokens number of tokens pooled
   */
  function _feePerToken(uint256 swapFee, uint256 numTokens) internal pure returns (uint256) {
    return (swapFee * numTokens) / ((numTokens - 1) * 4);
  }

  /*** STATE MODIFYING FUNCTIONS ***/

  /**
   * @notice swap two tokens in the pool
   * @param self Swap struct to read from and write to
   * @param tokenIndexFrom the token the user wants to sell
   * @param tokenIndexTo the token the user wants to buy
   * @param dx the amount of tokens the user wants to sell
   * @param minDy the min amount the user would like to receive, or revert.
   * @return amount of token user received on swap
   */
  function swap(
    Swap storage self,
    uint8 tokenIndexFrom,
    uint8 tokenIndexTo,
    uint256 dx,
    uint256 minDy
  ) internal returns (uint256) {
    require(!self.disabled, "disabled pool");
    {
      IERC20 tokenFrom = self.pooledTokens[tokenIndexFrom];
      require(dx <= tokenFrom.balanceOf(msg.sender), "swap more than you own");
      // Reverts for fee on transfer
      AssetLogic.handleIncomingAsset(address(tokenFrom), dx);
    }

    uint256 dy;
    uint256 dyFee;
    uint256[] memory balances = self.balances;
    (dy, dyFee) = _calculateSwap(self, tokenIndexFrom, tokenIndexTo, dx, balances);
    require(dy >= minDy, "dy < minDy");

    uint256 dyAdminFee = (dyFee * self.adminFee) /
      Constants.FEE_DENOMINATOR /
      self.tokenPrecisionMultipliers[tokenIndexTo];

    self.balances[tokenIndexFrom] = balances[tokenIndexFrom] + dx;
    self.balances[tokenIndexTo] = balances[tokenIndexTo] - dy - dyAdminFee;
    if (dyAdminFee != 0) {
      self.adminFees[tokenIndexTo] = self.adminFees[tokenIndexTo] + dyAdminFee;
    }

    AssetLogic.handleOutgoingAsset(address(self.pooledTokens[tokenIndexTo]), msg.sender, dy);

    emit TokenSwap(self.key, msg.sender, dx, dy, tokenIndexFrom, tokenIndexTo);

    return dy;
  }

  /**
   * @notice swap two tokens in the pool
   * @param self Swap struct to read from and write to
   * @param tokenIndexFrom the token the user wants to sell
   * @param tokenIndexTo the token the user wants to buy
   * @param dy the amount of tokens the user wants to buy
   * @param maxDx the max amount the user would like to send.
   * @return amount of token user have to transfer on swap
   */
  function swapOut(
    Swap storage self,
    uint8 tokenIndexFrom,
    uint8 tokenIndexTo,
    uint256 dy,
    uint256 maxDx
  ) internal returns (uint256) {
    require(!self.disabled, "disabled pool");
    require(dy <= self.balances[tokenIndexTo], ">pool balance");

    uint256 dx;
    uint256 dxFee;
    uint256[] memory balances = self.balances;
    (dx, dxFee) = _calculateSwapInv(self, tokenIndexFrom, tokenIndexTo, dy, balances);
    require(dx <= maxDx, "dx > maxDx");

    uint256 dxAdminFee = (dxFee * self.adminFee) /
      Constants.FEE_DENOMINATOR /
      self.tokenPrecisionMultipliers[tokenIndexFrom];

    self.balances[tokenIndexFrom] = balances[tokenIndexFrom] + dx - dxAdminFee;
    self.balances[tokenIndexTo] = balances[tokenIndexTo] - dy;
    if (dxAdminFee != 0) {
      self.adminFees[tokenIndexFrom] = self.adminFees[tokenIndexFrom] + dxAdminFee;
    }

    {
      IERC20 tokenFrom = self.pooledTokens[tokenIndexFrom];
      require(dx <= tokenFrom.balanceOf(msg.sender), "more than you own");
      // Reverts for fee on transfer
      AssetLogic.handleIncomingAsset(address(tokenFrom), dx);
    }

    AssetLogic.handleOutgoingAsset(address(self.pooledTokens[tokenIndexTo]), msg.sender, dy);

    emit TokenSwap(self.key, msg.sender, dx, dy, tokenIndexFrom, tokenIndexTo);

    return dx;
  }

  /**
   * @notice swap two tokens in the pool internally
   * @param self Swap struct to read from and write to
   * @param tokenIndexFrom the token the user wants to sell
   * @param tokenIndexTo the token the user wants to buy
   * @param dx the amount of tokens the user wants to sell
   * @param minDy the min amount the user would like to receive, or revert.
   * @return amount of token user received on swap
   */
  function swapInternal(
    Swap storage self,
    uint8 tokenIndexFrom,
    uint8 tokenIndexTo,
    uint256 dx,
    uint256 minDy
  ) internal returns (uint256) {
    require(!self.disabled, "disabled pool");
    require(dx <= self.balances[tokenIndexFrom], "more than pool balance");

    uint256 dy;
    uint256 dyFee;
    uint256[] memory balances = self.balances;
    (dy, dyFee) = _calculateSwap(self, tokenIndexFrom, tokenIndexTo, dx, balances);
    require(dy >= minDy, "dy < minDy");

    uint256 dyAdminFee = (dyFee * self.adminFee) /
      Constants.FEE_DENOMINATOR /
      self.tokenPrecisionMultipliers[tokenIndexTo];

    self.balances[tokenIndexFrom] = balances[tokenIndexFrom] + dx;
    self.balances[tokenIndexTo] = balances[tokenIndexTo] - dy - dyAdminFee;

    if (dyAdminFee != 0) {
      self.adminFees[tokenIndexTo] = self.adminFees[tokenIndexTo] + dyAdminFee;
    }

    emit TokenSwap(self.key, msg.sender, dx, dy, tokenIndexFrom, tokenIndexTo);

    return dy;
  }

  /**
   * @notice Should get exact amount out of AMM for asset put in
   */
  function swapInternalOut(
    Swap storage self,
    uint8 tokenIndexFrom,
    uint8 tokenIndexTo,
    uint256 dy,
    uint256 maxDx
  ) internal returns (uint256) {
    require(!self.disabled, "disabled pool");
    require(dy <= self.balances[tokenIndexTo], "more than pool balance");

    uint256 dx;
    uint256 dxFee;
    uint256[] memory balances = self.balances;
    (dx, dxFee) = _calculateSwapInv(self, tokenIndexFrom, tokenIndexTo, dy, balances);
    require(dx <= maxDx, "dx > maxDx");

    uint256 dxAdminFee = (dxFee * self.adminFee) /
      Constants.FEE_DENOMINATOR /
      self.tokenPrecisionMultipliers[tokenIndexFrom];

    self.balances[tokenIndexFrom] = balances[tokenIndexFrom] + dx - dxAdminFee;
    self.balances[tokenIndexTo] = balances[tokenIndexTo] - dy;

    if (dxAdminFee != 0) {
      self.adminFees[tokenIndexFrom] = self.adminFees[tokenIndexFrom] + dxAdminFee;
    }

    emit TokenSwap(self.key, msg.sender, dx, dy, tokenIndexFrom, tokenIndexTo);

    return dx;
  }

  /**
   * @notice Add liquidity to the pool
   * @param self Swap struct to read from and write to
   * @param amounts the amounts of each token to add, in their native precision
   * @param minToMint the minimum LP tokens adding this amount of liquidity
   * should mint, otherwise revert. Handy for front-running mitigation
   * allowed addresses. If the pool is not in the guarded launch phase, this parameter will be ignored.
   * @return amount of LP token user received
   */
  function addLiquidity(
    Swap storage self,
    uint256[] memory amounts,
    uint256 minToMint
  ) internal returns (uint256) {
    require(!self.disabled, "disabled pool");

    uint256 numTokens = self.pooledTokens.length;
    require(amounts.length == numTokens, "mismatch pooled tokens");

    // current state
    ManageLiquidityInfo memory v = ManageLiquidityInfo(
      0,
      0,
      0,
      _getAPrecise(self),
      self.lpToken,
      0,
      self.balances,
      self.tokenPrecisionMultipliers
    );
    v.totalSupply = v.lpToken.totalSupply();
    if (v.totalSupply != 0) {
      v.d0 = getD(_xp(v.balances, v.multipliers), v.preciseA);
    }

    uint256[] memory newBalances = new uint256[](numTokens);

    for (uint256 i; i < numTokens; ) {
      require(v.totalSupply != 0 || amounts[i] != 0, "!supply all tokens");

      // Transfer tokens first to see if a fee was charged on transfer
      if (amounts[i] != 0) {
        IERC20 token = self.pooledTokens[i];
        // Reverts for fee on transfer
        AssetLogic.handleIncomingAsset(address(token), amounts[i]);
      }

      newBalances[i] = v.balances[i] + amounts[i];

      unchecked {
        ++i;
      }
    }

    // invariant after change
    v.d1 = getD(_xp(newBalances, v.multipliers), v.preciseA);
    require(v.d1 > v.d0, "D should increase");

    // updated to reflect fees and calculate the user's LP tokens
    v.d2 = v.d1;
    uint256[] memory fees = new uint256[](numTokens);

    if (v.totalSupply != 0) {
      uint256 feePerToken = _feePerToken(self.swapFee, numTokens);
      for (uint256 i; i < numTokens; ) {
        uint256 idealBalance = (v.d1 * v.balances[i]) / v.d0;
        fees[i] = (feePerToken * (idealBalance.difference(newBalances[i]))) / Constants.FEE_DENOMINATOR;
        uint256 adminFee = (fees[i] * self.adminFee) / Constants.FEE_DENOMINATOR;
        self.balances[i] = newBalances[i] - adminFee;
        self.adminFees[i] = self.adminFees[i] + adminFee;
        newBalances[i] = newBalances[i] - fees[i];

        unchecked {
          ++i;
        }
      }
      v.d2 = getD(_xp(newBalances, v.multipliers), v.preciseA);
    } else {
      // the initial depositor doesn't pay fees
      self.balances = newBalances;
    }

    uint256 toMint;
    if (v.totalSupply == 0) {
      toMint = v.d1;
    } else {
      toMint = ((v.d2 - v.d0) * v.totalSupply) / v.d0;
    }

    require(toMint >= minToMint, "mint < min");

    // mint the user's LP tokens
    v.lpToken.mint(msg.sender, toMint);

    emit AddLiquidity(self.key, msg.sender, amounts, fees, v.d1, v.totalSupply + toMint);

    return toMint;
  }

  /**
   * @notice Burn LP tokens to remove liquidity from the pool.
   * @dev Liquidity can always be removed, even when the pool is paused.
   * @param self Swap struct to read from and write to
   * @param amount the amount of LP tokens to burn
   * @param minAmounts the minimum amounts of each token in the pool
   * acceptable for this burn. Useful as a front-running mitigation
   * @return amounts of tokens the user received
   */
  function removeLiquidity(
    Swap storage self,
    uint256 amount,
    uint256[] calldata minAmounts
  ) internal returns (uint256[] memory) {
    LPToken lpToken = self.lpToken;
    require(amount <= lpToken.balanceOf(msg.sender), ">LP.balanceOf");
    uint256 numTokens = self.pooledTokens.length;
    require(minAmounts.length == numTokens, "mismatch poolTokens");

    uint256[] memory balances = self.balances;
    uint256 totalSupply = lpToken.totalSupply();

    uint256[] memory amounts = _calculateRemoveLiquidity(balances, amount, totalSupply);

    uint256 numAmounts = amounts.length;
    for (uint256 i; i < numAmounts; ) {
      require(amounts[i] >= minAmounts[i], "amounts[i] < minAmounts[i]");
      self.balances[i] = balances[i] - amounts[i];
      AssetLogic.handleOutgoingAsset(address(self.pooledTokens[i]), msg.sender, amounts[i]);

      unchecked {
        ++i;
      }
    }

    lpToken.burnFrom(msg.sender, amount);

    emit RemoveLiquidity(self.key, msg.sender, amounts, totalSupply - amount);

    return amounts;
  }

  /**
   * @notice Remove liquidity from the pool all in one token.
   * @param self Swap struct to read from and write to
   * @param tokenAmount the amount of the lp tokens to burn
   * @param tokenIndex the index of the token you want to receive
   * @param minAmount the minimum amount to withdraw, otherwise revert
   * @return amount chosen token that user received
   */
  function removeLiquidityOneToken(
    Swap storage self,
    uint256 tokenAmount,
    uint8 tokenIndex,
    uint256 minAmount
  ) internal returns (uint256) {
    LPToken lpToken = self.lpToken;

    require(tokenAmount <= lpToken.balanceOf(msg.sender), ">LP.balanceOf");
    uint256 numTokens = self.pooledTokens.length;
    require(tokenIndex < numTokens, "not found");

    uint256 totalSupply = lpToken.totalSupply();

    (uint256 dy, uint256 dyFee) = _calculateWithdrawOneToken(self, tokenAmount, tokenIndex, totalSupply);

    require(dy >= minAmount, "dy < minAmount");

    uint256 adminFee = (dyFee * self.adminFee) / Constants.FEE_DENOMINATOR;
    self.balances[tokenIndex] = self.balances[tokenIndex] - (dy + adminFee);
    if (adminFee != 0) {
      self.adminFees[tokenIndex] = self.adminFees[tokenIndex] + adminFee;
    }
    lpToken.burnFrom(msg.sender, tokenAmount);
    AssetLogic.handleOutgoingAsset(address(self.pooledTokens[tokenIndex]), msg.sender, dy);

    emit RemoveLiquidityOne(self.key, msg.sender, tokenAmount, totalSupply, tokenIndex, dy);

    return dy;
  }

  /**
   * @notice Remove liquidity from the pool, weighted differently than the
   * pool's current balances.
   *
   * @param self Swap struct to read from and write to
   * @param amounts how much of each token to withdraw
   * @param maxBurnAmount the max LP token provider is willing to pay to
   * remove liquidity. Useful as a front-running mitigation.
   * @return actual amount of LP tokens burned in the withdrawal
   */
  function removeLiquidityImbalance(
    Swap storage self,
    uint256[] memory amounts,
    uint256 maxBurnAmount
  ) internal returns (uint256) {
    ManageLiquidityInfo memory v = ManageLiquidityInfo(
      0,
      0,
      0,
      _getAPrecise(self),
      self.lpToken,
      0,
      self.balances,
      self.tokenPrecisionMultipliers
    );
    v.totalSupply = v.lpToken.totalSupply();

    uint256 numTokens = self.pooledTokens.length;
    uint256 numAmounts = amounts.length;
    require(numAmounts == numTokens, "mismatch pool tokens");

    require(maxBurnAmount <= v.lpToken.balanceOf(msg.sender) && maxBurnAmount != 0, ">LP.balanceOf");

    uint256 feePerToken = _feePerToken(self.swapFee, numTokens);
    uint256[] memory fees = new uint256[](numTokens);
    {
      uint256[] memory balances1 = new uint256[](numTokens);
      v.d0 = getD(_xp(v.balances, v.multipliers), v.preciseA);
      for (uint256 i; i < numTokens; ) {
        require(v.balances[i] >= amounts[i], "withdraw more than available");

        unchecked {
          balances1[i] = v.balances[i] - amounts[i];
          ++i;
        }
      }
      v.d1 = getD(_xp(balances1, v.multipliers), v.preciseA);

      for (uint256 i; i < numTokens; ) {
        {
          uint256 idealBalance = (v.d1 * v.balances[i]) / v.d0;
          uint256 difference = idealBalance.difference(balances1[i]);
          fees[i] = (feePerToken * difference) / Constants.FEE_DENOMINATOR;
        }
        uint256 adminFee = (fees[i] * self.adminFee) / Constants.FEE_DENOMINATOR;
        self.balances[i] = balances1[i] - adminFee;
        self.adminFees[i] = self.adminFees[i] + adminFee;
        balances1[i] = balances1[i] - fees[i];

        unchecked {
          ++i;
        }
      }

      v.d2 = getD(_xp(balances1, v.multipliers), v.preciseA);
    }
    uint256 tokenAmount = ((v.d0 - v.d2) * v.totalSupply) / v.d0;
    require(tokenAmount != 0, "!zero amount");
    tokenAmount = tokenAmount + 1;

    require(tokenAmount <= maxBurnAmount, "tokenAmount > maxBurnAmount");

    v.lpToken.burnFrom(msg.sender, tokenAmount);

    for (uint256 i; i < numTokens; ) {
      AssetLogic.handleOutgoingAsset(address(self.pooledTokens[i]), msg.sender, amounts[i]);

      unchecked {
        ++i;
      }
    }

    emit RemoveLiquidityImbalance(self.key, msg.sender, amounts, fees, v.d1, v.totalSupply - tokenAmount);

    return tokenAmount;
  }

  /**
   * @notice withdraw all admin fees to a given address
   * @param self Swap struct to withdraw fees from
   * @param to Address to send the fees to
   */
  function withdrawAdminFees(Swap storage self, address to) internal {
    uint256 numTokens = self.pooledTokens.length;
    for (uint256 i; i < numTokens; ) {
      IERC20 token = self.pooledTokens[i];
      uint256 balance = self.adminFees[i];
      if (balance != 0) {
        delete self.adminFees[i];
        AssetLogic.handleOutgoingAsset(address(token), to, balance);
      }

      unchecked {
        ++i;
      }
    }
  }

  /**
   * @notice Sets the admin fee
   * @dev adminFee cannot be higher than 100% of the swap fee
   * @param self Swap struct to update
   * @param newAdminFee new admin fee to be applied on future transactions
   */
  function setAdminFee(Swap storage self, uint256 newAdminFee) internal {
    require(newAdminFee < Constants.MAX_ADMIN_FEE + 1, "too high");
    self.adminFee = newAdminFee;

    emit NewAdminFee(self.key, newAdminFee);
  }

  /**
   * @notice update the swap fee
   * @dev fee cannot be higher than 1% of each swap
   * @param self Swap struct to update
   * @param newSwapFee new swap fee to be applied on future transactions
   */
  function setSwapFee(Swap storage self, uint256 newSwapFee) internal {
    require(newSwapFee < Constants.MAX_SWAP_FEE + 1, "too high");
    self.swapFee = newSwapFee;

    emit NewSwapFee(self.key, newSwapFee);
  }

  /**
   * @notice Check if this stableswap pool exists and is valid (i.e. has been
   * initialized and tokens have been added).
   * @return bool true if this stableswap pool is valid, false if not.
   */
  function exists(Swap storage self) internal view returns (bool) {
    return !self.disabled && self.pooledTokens.length != 0;
  }
}

File 2 of 38 : TokenId.sol
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity 0.8.17;

// ============= Structs =============

// Tokens are identified by a TokenId:
// domain - 4 byte chain ID of the chain from which the token originates
// id - 32 byte identifier of the token address on the origin chain, in that chain's address format
struct TokenId {
  uint32 domain;
  bytes32 id;
}

File 2 of 38 : IConnectorManager.sol
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity 0.8.17;

import {IOutbox} from "./IOutbox.sol";

/**
 * @notice Each router extends the `XAppConnectionClient` contract. This contract
 * allows an admin to call `setXAppConnectionManager` to update the underlying
 * pointers to the messaging inboxes (Replicas) and outboxes (Homes).
 *
 * @dev This interface only contains the functions needed for the `XAppConnectionClient`
 * will interface with.
 */
interface IConnectorManager {
  /**
   * @notice Get the local inbox contract from the xAppConnectionManager
   * @return The local inbox contract
   * @dev The local inbox contract is a SpokeConnector with AMBs, and a
   * Home contract with nomad
   */
  function home() external view returns (IOutbox);

  /**
   * @notice Determine whether _potentialReplica is an enrolled Replica from the xAppConnectionManager
   * @return True if _potentialReplica is an enrolled Replica
   */
  function isReplica(address _potentialReplica) external view returns (bool);

  /**
   * @notice Get the local domain from the xAppConnectionManager
   * @return The local domain
   */
  function localDomain() external view returns (uint32);
}

File 2 of 38 : IOutbox.sol
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity 0.8.17;

/**
 * @notice Interface for all contracts sending messages originating on their
 * current domain.
 *
 * @dev These are the Home.sol interface methods used by the `Router`
 * and exposed via `home()` on the `XAppConnectionClient`
 */
interface IOutbox {
  /**
   * @notice Emitted when a new message is added to an outbound message merkle root
   * @param leafIndex Index of message's leaf in merkle tree
   * @param destinationAndNonce Destination and destination-specific
   * nonce combined in single field ((destination << 32) & nonce)
   * @param messageHash Hash of message; the leaf inserted to the Merkle tree for the message
   * @param committedRoot the latest notarized root submitted in the last signed Update
   * @param message Raw bytes of message
   */
  event Dispatch(
    bytes32 indexed messageHash,
    uint256 indexed leafIndex,
    uint64 indexed destinationAndNonce,
    bytes32 committedRoot,
    bytes message
  );

  /**
   * @notice Dispatch the message it to the destination domain & recipient
   * @dev Format the message, insert its hash into Merkle tree,
   * enqueue the new Merkle root, and emit `Dispatch` event with message information.
   * @param _destinationDomain Domain of destination chain
   * @param _recipientAddress Address of recipient on destination chain as bytes32
   * @param _messageBody Raw bytes content of message
   * @return bytes32 The leaf added to the tree
   */
  function dispatch(
    uint32 _destinationDomain,
    bytes32 _recipientAddress,
    bytes memory _messageBody
  ) external returns (bytes32, bytes memory);

  /**
   * @notice domain => next available nonce for the domain.
   */
  function nonces(uint32 domain) external returns (uint32);
}

File 2 of 38 : ExcessivelySafeCall.sol
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity 0.8.17;

// Taken from: https://github.com/nomad-xyz/ExcessivelySafeCall
// NOTE: There is a difference between npm latest and github main versions
// where the latest github version allows you to specify an ether value.

library ExcessivelySafeCall {
  uint256 constant LOW_28_MASK = 0x00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff;

  /// @notice Use when you _really_ really _really_ don't trust the called
  /// contract. This prevents the called contract from causing reversion of
  /// the caller in as many ways as we can.
  /// @dev The main difference between this and a solidity low-level call is
  /// that we limit the number of bytes that the callee can cause to be
  /// copied to caller memory. This prevents stupid things like malicious
  /// contracts returning 10,000,000 bytes causing a local OOG when copying
  /// to memory.
  /// @param _target The address to call
  /// @param _gas The amount of gas to forward to the remote contract
  /// @param _value The value in wei to send to the remote contract
  /// @param _maxCopy The maximum number of bytes of returndata to copy
  /// to memory.
  /// @param _calldata The data to send to the remote contract
  /// @return success and returndata, as `.call()`. Returndata is capped to
  /// `_maxCopy` bytes.
  function excessivelySafeCall(
    address _target,
    uint256 _gas,
    uint256 _value,
    uint16 _maxCopy,
    bytes memory _calldata
  ) internal returns (bool, bytes memory) {
    // set up for assembly call
    uint256 _toCopy;
    bool _success;
    bytes memory _returnData = new bytes(_maxCopy);
    // dispatch message to recipient
    // by assembly calling "handle" function
    // we call via assembly to avoid memcopying a very large returndata
    // returned by a malicious contract
    assembly {
      _success := call(
        _gas, // gas
        _target, // recipient
        _value, // ether value
        add(_calldata, 0x20), // inloc
        mload(_calldata), // inlen
        0, // outloc
        0 // outlen
      )
      // limit our copy to 256 bytes
      _toCopy := returndatasize()
      if gt(_toCopy, _maxCopy) {
        _toCopy := _maxCopy
      }
      // Store the length of the copied bytes
      mstore(_returnData, _toCopy)
      // copy the bytes from returndata[0:_toCopy]
      returndatacopy(add(_returnData, 0x20), 0, _toCopy)
    }
    return (_success, _returnData);
  }

  /// @notice Use when you _really_ really _really_ don't trust the called
  /// contract. This prevents the called contract from causing reversion of
  /// the caller in as many ways as we can.
  /// @dev The main difference between this and a solidity low-level call is
  /// that we limit the number of bytes that the callee can cause to be
  /// copied to caller memory. This prevents stupid things like malicious
  /// contracts returning 10,000,000 bytes causing a local OOG when copying
  /// to memory.
  /// @param _target The address to call
  /// @param _gas The amount of gas to forward to the remote contract
  /// @param _maxCopy The maximum number of bytes of returndata to copy
  /// to memory.
  /// @param _calldata The data to send to the remote contract
  /// @return success and returndata, as `.call()`. Returndata is capped to
  /// `_maxCopy` bytes.
  function excessivelySafeStaticCall(
    address _target,
    uint256 _gas,
    uint16 _maxCopy,
    bytes memory _calldata
  ) internal view returns (bool, bytes memory) {
    // set up for assembly call
    uint256 _toCopy;
    bool _success;
    bytes memory _returnData = new bytes(_maxCopy);
    // dispatch message to recipient
    // by assembly calling "handle" function
    // we call via assembly to avoid memcopying a very large returndata
    // returned by a malicious contract
    assembly {
      _success := staticcall(
        _gas, // gas
        _target, // recipient
        add(_calldata, 0x20), // inloc
        mload(_calldata), // inlen
        0, // outloc
        0 // outlen
      )
      // limit our copy to 256 bytes
      _toCopy := returndatasize()
      if gt(_toCopy, _maxCopy) {
        _toCopy := _maxCopy
      }
      // Store the length of the copied bytes
      mstore(_returnData, _toCopy)
      // copy the bytes from returndata[0:_toCopy]
      returndatacopy(add(_returnData, 0x20), 0, _toCopy)
    }
    return (_success, _returnData);
  }

  /**
   * @notice Swaps function selectors in encoded contract calls
   * @dev Allows reuse of encoded calldata for functions with identical
   * argument types but different names. It simply swaps out the first 4 bytes
   * for the new selector. This function modifies memory in place, and should
   * only be used with caution.
   * @param _newSelector The new 4-byte selector
   * @param _buf The encoded contract args
   */
  function swapSelector(bytes4 _newSelector, bytes memory _buf) internal pure {
    require(_buf.length > 4 - 1);
    uint256 _mask = LOW_28_MASK;
    assembly {
      // load the first word of
      let _word := mload(add(_buf, 0x20))
      // mask out the top 4 bytes
      // /x
      _word := and(_word, _mask)
      _word := or(_newSelector, _word)
      mstore(add(_buf, 0x20), _word)
    }
  }
}

File 2 of 38 : TypeCasts.sol
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity 0.8.17;

import {TypedMemView} from "./TypedMemView.sol";

library TypeCasts {
  using TypedMemView for bytes;
  using TypedMemView for bytes29;

  // alignment preserving cast
  function addressToBytes32(address _addr) internal pure returns (bytes32) {
    return bytes32(uint256(uint160(_addr)));
  }

  // alignment preserving cast
  function bytes32ToAddress(bytes32 _buf) internal pure returns (address) {
    return address(uint160(uint256(_buf)));
  }
}

File 2 of 38 : TypedMemView.sol
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity 0.8.17;

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

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

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

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

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

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

  // The null view
  bytes29 public constant NULL = hex"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff";
  uint256 constant LOW_12_MASK = 0xffffffffffffffffffffffff;
  uint256 constant TWENTY_SEVEN_BYTES = 8 * 27;
  uint256 private constant _27_BYTES_IN_BITS = 8 * 27; // <--- also used this named constant where ever 216 is used.
  uint256 private constant LOW_27_BYTES_MASK = 0xffffffffffffffffffffffffffffffffffffffffffffffffffffff; // (1 << _27_BYTES_IN_BITS) - 1;

  // ========== Custom Errors ===========

  error TypedMemView__assertType_typeAssertionFailed(uint256 actual, uint256 expected);
  error TypedMemView__index_overrun(uint256 loc, uint256 len, uint256 index, uint256 slice);
  error TypedMemView__index_indexMoreThan32Bytes();
  error TypedMemView__unsafeCopyTo_nullPointer();
  error TypedMemView__unsafeCopyTo_invalidPointer();
  error TypedMemView__unsafeCopyTo_identityOOG();
  error TypedMemView__assertValid_validityAssertionFailed();

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

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

  /**
   * @notice      Create a mask with the highest `_len` bits set.
   * @param _len  The length
   * @return      mask - The mask
   */
  function leftMask(uint8 _len) private pure returns (uint256 mask) {
    // ugly. redo without assembly?
    assembly {
      // solhint-disable-previous-line no-inline-assembly
      mask := sar(sub(_len, 1), 0x8000000000000000000000000000000000000000000000000000000000000000)
    }
  }

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

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

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

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

  /**
   * @notice          Require that a typed memory view be valid.
   * @dev             Returns the view for easy chaining.
   * @param memView   The view
   * @return          bytes29 - The validated view
   */
  function assertValid(bytes29 memView) internal pure returns (bytes29) {
    if (isNotValid(memView)) revert TypedMemView__assertValid_validityAssertionFailed();
    return memView;
  }

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

  /**
   * @notice          Require that a typed memory view has a specific type.
   * @dev             Returns the view for easy chaining.
   * @param memView   The view
   * @param _expected The expected type
   * @return          bytes29 - The view with validated type
   */
  function assertType(bytes29 memView, uint40 _expected) internal pure returns (bytes29) {
    if (!isType(memView, _expected)) {
      revert TypedMemView__assertType_typeAssertionFailed(uint256(typeOf(memView)), uint256(_expected));
    }
    return memView;
  }

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

  /**
   * @notice          Unsafe raw pointer construction. This should generally not be called
   *                  directly. Prefer `ref` wherever possible.
   * @dev             Unsafe raw pointer construction. This should generally not be called
   *                  directly. Prefer `ref` wherever possible.
   * @param _type     The type
   * @param _loc      The memory address
   * @param _len      The length
   * @return          newView - The new view with the specified type, location and length
   */
  function unsafeBuildUnchecked(
    uint256 _type,
    uint256 _loc,
    uint256 _len
  ) private pure returns (bytes29 newView) {
    uint256 _uint96Bits = 96;
    uint256 _emptyBits = 24;

    // Cast params to ensure input is of correct length
    uint96 len_ = uint96(_len);
    uint96 loc_ = uint96(_loc);
    require(len_ == _len && loc_ == _loc, "!truncated");

    assembly {
      // solium-disable-previous-line security/no-inline-assembly
      newView := shl(_uint96Bits, _type) // insert type
      newView := shl(_uint96Bits, or(newView, loc_)) // insert loc
      newView := shl(_emptyBits, or(newView, len_)) // empty bottom 3 bytes
    }
  }

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

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

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

    return build(newType, _loc, _len);
  }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  /**
   * @notice          Copy the view to a location, return an unsafe memory reference
   * @dev             Super Dangerous direct memory access.
   *
   *                  This reference can be overwritten if anything else modifies memory (!!!).
   *                  As such it MUST be consumed IMMEDIATELY.
   *                  This function is private to prevent unsafe usage by callers.
   * @param memView   The view
   * @param _newLoc   The new location
   * @return          written - the unsafe memory reference
   */
  function unsafeCopyTo(bytes29 memView, uint256 _newLoc) private view returns (bytes29 written) {
    if (isNull(memView)) revert TypedMemView__unsafeCopyTo_nullPointer();
    if (isNotValid(memView)) revert TypedMemView__unsafeCopyTo_invalidPointer();

    uint256 _len = len(memView);
    uint256 _oldLoc = loc(memView);

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

      // use the identity precompile to copy
      // guaranteed not to fail, so pop the success
      res := staticcall(gas(), 4, _oldLoc, _len, _newLoc, _len)
    }
    if (!res) revert TypedMemView__unsafeCopyTo_identityOOG();
    written = unsafeBuildUnchecked(typeOf(memView), _newLoc, _len);
  }

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

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

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

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

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

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

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

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

Contract Security Audit

Contract ABI

[{"inputs":[],"name":"AssetLogic__getConfig_notRegistered","type":"error"},{"inputs":[],"name":"AssetLogic__getTokenIndexFromStableSwapPool_notExist","type":"error"},{"inputs":[],"name":"AssetLogic__handleIncomingAsset_feeOnTransferNotSupported","type":"error"},{"inputs":[],"name":"AssetLogic__handleIncomingAsset_nativeAssetNotSupported","type":"error"},{"inputs":[],"name":"AssetLogic__handleOutgoingAsset_notNative","type":"error"},{"inputs":[],"name":"BaseConnextFacet__getAdoptedAsset_assetNotFound","type":"error"},{"inputs":[],"name":"BaseConnextFacet__getApprovedCanonicalId_notAllowlisted","type":"error"},{"inputs":[],"name":"BaseConnextFacet__nonReentrant_reentrantCall","type":"error"},{"inputs":[],"name":"BaseConnextFacet__nonXCallReentrant_reentrantCall","type":"error"},{"inputs":[],"name":"BaseConnextFacet__onlyOwnerOrAdmin_notOwnerOrAdmin","type":"error"},{"inputs":[],"name":"BaseConnextFacet__onlyOwnerOrRouter_notOwnerOrRouter","type":"error"},{"inputs":[],"name":"BaseConnextFacet__onlyOwnerOrWatcher_notOwnerOrWatcher","type":"error"},{"inputs":[],"name":"BaseConnextFacet__onlyOwner_notOwner","type":"error"},{"inputs":[],"name":"BaseConnextFacet__onlyProposed_notProposedOwner","type":"error"},{"inputs":[],"name":"BaseConnextFacet__whenNotPaused_paused","type":"error"},{"inputs":[],"name":"BridgeFacet__addRemote_invalidDomain","type":"error"},{"inputs":[],"name":"BridgeFacet__addRemote_invalidRouter","type":"error"},{"inputs":[],"name":"BridgeFacet__addSequencer_alreadyApproved","type":"error"},{"inputs":[],"name":"BridgeFacet__addSequencer_invalidSequencer","type":"error"},{"inputs":[],"name":"BridgeFacet__bumpTransfer_noRelayerVault","type":"error"},{"inputs":[],"name":"BridgeFacet__bumpTransfer_valueIsZero","type":"error"},{"inputs":[],"name":"BridgeFacet__excecute_insufficientGas","type":"error"},{"inputs":[],"name":"BridgeFacet__executePortalTransfer_insufficientAmountWithdrawn","type":"error"},{"inputs":[],"name":"BridgeFacet__execute_badFastLiquidityStatus","type":"error"},{"inputs":[],"name":"BridgeFacet__execute_externalCallFailed","type":"error"},{"inputs":[],"name":"BridgeFacet__execute_invalidRouterSignature","type":"error"},{"inputs":[],"name":"BridgeFacet__execute_invalidSequencerSignature","type":"error"},{"inputs":[],"name":"BridgeFacet__execute_maxRoutersExceeded","type":"error"},{"inputs":[],"name":"BridgeFacet__execute_notApprovedForPortals","type":"error"},{"inputs":[],"name":"BridgeFacet__execute_notReconciled","type":"error"},{"inputs":[],"name":"BridgeFacet__execute_notSupportedRouter","type":"error"},{"inputs":[],"name":"BridgeFacet__execute_notSupportedSequencer","type":"error"},{"inputs":[],"name":"BridgeFacet__execute_unapprovedSender","type":"error"},{"inputs":[],"name":"BridgeFacet__execute_wrongDomain","type":"error"},{"inputs":[],"name":"BridgeFacet__forceReceiveLocal_notDestination","type":"error"},{"inputs":[],"name":"BridgeFacet__forceUpdateSlippage_invalidSlippage","type":"error"},{"inputs":[],"name":"BridgeFacet__forceUpdateSlippage_notDestination","type":"error"},{"inputs":[],"name":"BridgeFacet__mustHaveRemote_destinationNotSupported","type":"error"},{"inputs":[],"name":"BridgeFacet__onlyDelegate_notDelegate","type":"error"},{"inputs":[],"name":"BridgeFacet__removeSequencer_notApproved","type":"error"},{"inputs":[],"name":"BridgeFacet__setXAppConnectionManager_domainsDontMatch","type":"error"},{"inputs":[],"name":"BridgeFacet__xcall_capReached","type":"error"},{"inputs":[],"name":"BridgeFacet__xcall_emptyTo","type":"error"},{"inputs":[],"name":"BridgeFacet__xcall_invalidSlippage","type":"error"},{"inputs":[],"name":"BridgeFacet__xcall_nativeAssetNotSupported","type":"error"},{"inputs":[],"name":"BridgeFacet_xcall__emptyLocalAsset","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"transferId","type":"bytes32"},{"indexed":true,"internalType":"address","name":"router","type":"address"},{"indexed":false,"internalType":"address","name":"asset","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"AavePortalMintUnbacked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"transferId","type":"bytes32"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"address","name":"asset","type":"address"},{"components":[{"components":[{"internalType":"uint32","name":"originDomain","type":"uint32"},{"internalType":"uint32","name":"destinationDomain","type":"uint32"},{"internalType":"uint32","name":"canonicalDomain","type":"uint32"},{"internalType":"address","name":"to","type":"address"},{"internalType":"address","name":"delegate","type":"address"},{"internalType":"bool","name":"receiveLocal","type":"bool"},{"internalType":"bytes","name":"callData","type":"bytes"},{"internalType":"uint256","name":"slippage","type":"uint256"},{"internalType":"address","name":"originSender","type":"address"},{"internalType":"uint256","name":"bridgedAmt","type":"uint256"},{"internalType":"uint256","name":"normalizedIn","type":"uint256"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"bytes32","name":"canonicalId","type":"bytes32"}],"internalType":"struct TransferInfo","name":"params","type":"tuple"},{"internalType":"address[]","name":"routers","type":"address[]"},{"internalType":"bytes[]","name":"routerSignatures","type":"bytes[]"},{"internalType":"address","name":"sequencer","type":"address"},{"internalType":"bytes","name":"sequencerSignature","type":"bytes"}],"indexed":false,"internalType":"struct ExecuteArgs","name":"args","type":"tuple"},{"indexed":false,"internalType":"address","name":"local","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"address","name":"caller","type":"address"}],"name":"Executed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"transferId","type":"bytes32"},{"indexed":false,"internalType":"bool","name":"success","type":"bool"},{"indexed":false,"internalType":"bytes","name":"returnData","type":"bytes"}],"name":"ExternalCalldataExecuted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"transferId","type":"bytes32"}],"name":"ForceReceiveLocal","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint32","name":"domain","type":"uint32"},{"indexed":false,"internalType":"address","name":"remote","type":"address"},{"indexed":false,"internalType":"address","name":"caller","type":"address"}],"name":"RemoteAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"sequencer","type":"address"},{"indexed":false,"internalType":"address","name":"caller","type":"address"}],"name":"SequencerAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"sequencer","type":"address"},{"indexed":false,"internalType":"address","name":"caller","type":"address"}],"name":"SequencerRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"transferId","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"slippage","type":"uint256"}],"name":"SlippageUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"transferId","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"increase","type":"uint256"},{"indexed":false,"internalType":"address","name":"asset","type":"address"},{"indexed":false,"internalType":"address","name":"caller","type":"address"}],"name":"TransferRelayerFeesIncreased","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"updated","type":"address"},{"indexed":false,"internalType":"address","name":"caller","type":"address"}],"name":"XAppConnectionManagerSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"transferId","type":"bytes32"},{"indexed":true,"internalType":"uint256","name":"nonce","type":"uint256"},{"indexed":true,"internalType":"bytes32","name":"messageHash","type":"bytes32"},{"components":[{"internalType":"uint32","name":"originDomain","type":"uint32"},{"internalType":"uint32","name":"destinationDomain","type":"uint32"},{"internalType":"uint32","name":"canonicalDomain","type":"uint32"},{"internalType":"address","name":"to","type":"address"},{"internalType":"address","name":"delegate","type":"address"},{"internalType":"bool","name":"receiveLocal","type":"bool"},{"internalType":"bytes","name":"callData","type":"bytes"},{"internalType":"uint256","name":"slippage","type":"uint256"},{"internalType":"address","name":"originSender","type":"address"},{"internalType":"uint256","name":"bridgedAmt","type":"uint256"},{"internalType":"uint256","name":"normalizedIn","type":"uint256"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"bytes32","name":"canonicalId","type":"bytes32"}],"indexed":false,"internalType":"struct TransferInfo","name":"params","type":"tuple"},{"indexed":false,"internalType":"address","name":"asset","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"address","name":"local","type":"address"},{"indexed":false,"internalType":"bytes","name":"messageBody","type":"bytes"}],"name":"XCalled","type":"event"},{"inputs":[{"internalType":"address","name":"_sequencer","type":"address"}],"name":"addSequencer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_sequencer","type":"address"}],"name":"approvedSequencers","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_transferId","type":"bytes32"}],"name":"bumpTransfer","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_transferId","type":"bytes32"},{"internalType":"address","name":"_relayerFeeAsset","type":"address"},{"internalType":"uint256","name":"_relayerFee","type":"uint256"}],"name":"bumpTransfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"domain","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"_domain","type":"uint32"},{"internalType":"bytes32","name":"_router","type":"bytes32"}],"name":"enrollRemoteRouter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"components":[{"internalType":"uint32","name":"originDomain","type":"uint32"},{"internalType":"uint32","name":"destinationDomain","type":"uint32"},{"internalType":"uint32","name":"canonicalDomain","type":"uint32"},{"internalType":"address","name":"to","type":"address"},{"internalType":"address","name":"delegate","type":"address"},{"internalType":"bool","name":"receiveLocal","type":"bool"},{"internalType":"bytes","name":"callData","type":"bytes"},{"internalType":"uint256","name":"slippage","type":"uint256"},{"internalType":"address","name":"originSender","type":"address"},{"internalType":"uint256","name":"bridgedAmt","type":"uint256"},{"internalType":"uint256","name":"normalizedIn","type":"uint256"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"bytes32","name":"canonicalId","type":"bytes32"}],"internalType":"struct TransferInfo","name":"params","type":"tuple"},{"internalType":"address[]","name":"routers","type":"address[]"},{"internalType":"bytes[]","name":"routerSignatures","type":"bytes[]"},{"internalType":"address","name":"sequencer","type":"address"},{"internalType":"bytes","name":"sequencerSignature","type":"bytes"}],"internalType":"struct ExecuteArgs","name":"_args","type":"tuple"}],"name":"execute","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint32","name":"originDomain","type":"uint32"},{"internalType":"uint32","name":"destinationDomain","type":"uint32"},{"internalType":"uint32","name":"canonicalDomain","type":"uint32"},{"internalType":"address","name":"to","type":"address"},{"internalType":"address","name":"delegate","type":"address"},{"internalType":"bool","name":"receiveLocal","type":"bool"},{"internalType":"bytes","name":"callData","type":"bytes"},{"internalType":"uint256","name":"slippage","type":"uint256"},{"internalType":"address","name":"originSender","type":"address"},{"internalType":"uint256","name":"bridgedAmt","type":"uint256"},{"internalType":"uint256","name":"normalizedIn","type":"uint256"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"bytes32","name":"canonicalId","type":"bytes32"}],"internalType":"struct TransferInfo","name":"_params","type":"tuple"}],"name":"forceReceiveLocal","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint32","name":"originDomain","type":"uint32"},{"internalType":"uint32","name":"destinationDomain","type":"uint32"},{"internalType":"uint32","name":"canonicalDomain","type":"uint32"},{"internalType":"address","name":"to","type":"address"},{"internalType":"address","name":"delegate","type":"address"},{"internalType":"bool","name":"receiveLocal","type":"bool"},{"internalType":"bytes","name":"callData","type":"bytes"},{"internalType":"uint256","name":"slippage","type":"uint256"},{"internalType":"address","name":"originSender","type":"address"},{"internalType":"uint256","name":"bridgedAmt","type":"uint256"},{"internalType":"uint256","name":"normalizedIn","type":"uint256"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"bytes32","name":"canonicalId","type":"bytes32"}],"internalType":"struct TransferInfo","name":"_params","type":"tuple"},{"internalType":"uint256","name":"_slippage","type":"uint256"}],"name":"forceUpdateSlippage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"nonce","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"_domain","type":"uint32"}],"name":"remote","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_sequencer","type":"address"}],"name":"removeSequencer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_transferId","type":"bytes32"}],"name":"routedTransfers","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_xAppConnectionManager","type":"address"}],"name":"setXAppConnectionManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_transferId","type":"bytes32"}],"name":"transferStatus","outputs":[{"internalType":"enum DestinationTransferStatus","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"xAppConnectionManager","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"_destination","type":"uint32"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"address","name":"_asset","type":"address"},{"internalType":"address","name":"_delegate","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"uint256","name":"_slippage","type":"uint256"},{"internalType":"bytes","name":"_callData","type":"bytes"}],"name":"xcall","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint32","name":"_destination","type":"uint32"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"address","name":"_asset","type":"address"},{"internalType":"address","name":"_delegate","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"uint256","name":"_slippage","type":"uint256"},{"internalType":"bytes","name":"_callData","type":"bytes"},{"internalType":"uint256","name":"_relayerFee","type":"uint256"}],"name":"xcall","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"_destination","type":"uint32"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"address","name":"_asset","type":"address"},{"internalType":"address","name":"_delegate","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"uint256","name":"_slippage","type":"uint256"},{"internalType":"bytes","name":"_callData","type":"bytes"},{"internalType":"uint256","name":"_relayerFee","type":"uint256"}],"name":"xcallIntoLocal","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"_destination","type":"uint32"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"address","name":"_asset","type":"address"},{"internalType":"address","name":"_delegate","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"uint256","name":"_slippage","type":"uint256"},{"internalType":"bytes","name":"_callData","type":"bytes"}],"name":"xcallIntoLocal","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"payable","type":"function"}]

608060405234801561001057600080fd5b50615188806100206000396000f3fe60806040526004361061011f5760003560e01c80636989ca7c116100a0578063affed0e011610064578063affed0e01461033e578063b49c53a714610353578063bfd7903014610373578063c2fb26a6146103b0578063cb8058ba146103d557600080fd5b80636989ca7c146102b85780638a336231146102d85780638aac16ba146102f857806391f5de791461030b57806393f18ac51461031e57600080fd5b806341bdc8b5116100e757806341bdc8b51461020a578063541267111461022a57806359efa1621461024a57806363e3e7d21461026a578063674dc9331461029857600080fd5b8063121cca3114610124578063159e041f146101615780631a8bc0e1146101aa5780632424401f146101d75780633339df96146101ec575b600080fd5b34801561013057600080fd5b5061014461013f3660046141b4565b6103f5565b6040516001600160a01b0390911681526020015b60405180910390f35b34801561016d57600080fd5b5061019a61017c3660046141f1565b6001600160a01b03166000908152601f602052604090205460ff1690565b6040519015158152602001610158565b3480156101b657600080fd5b506101ca6101c536600461420e565b610412565b6040516101589190614227565b6101ea6101e536600461420e565b61047e565b005b3480156101f857600080fd5b506020546001600160a01b0316610144565b34801561021657600080fd5b506101ea6102253660046141f1565b610507565b34801561023657600080fd5b506101ea61024536600461428d565b610664565b34801561025657600080fd5b506101ea6102653660046142d1565b610766565b34801561027657600080fd5b5061028a610285366004614309565b6107fb565b604051908152602001610158565b3480156102a457600080fd5b5061028a6102b3366004614384565b6109ba565b3480156102c457600080fd5b506101ea6102d33660046141f1565b610ae2565b3480156102e457600080fd5b506101ea6102f33660046141f1565b610bda565b61028a61030636600461442d565b610cf6565b61028a61031936600461442d565b610e1b565b34801561032a57600080fd5b5061028a610339366004614384565b610f2d565b34801561034a57600080fd5b5060035461028a565b34801561035f57600080fd5b506101ea61036e3660046144cd565b611041565b34801561037f57600080fd5b506103a361038e36600461420e565b60009081526008602052604090205460ff1690565b604051610158919061452d565b3480156103bc57600080fd5b5060045460405163ffffffff9091168152602001610158565b3480156103e157600080fd5b506101ea6103f0366004614540565b61115f565b63ffffffff81166000908152600f60205260408120545b92915050565b60008181526009602090815260409182902080548351818402810184019094528084526060939283018282801561047257602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610454575b50505050509050919050565b601654600119016104a257604051637ce54e2d60e11b815260040160405180910390fd5b6002601655601a54600160a01b900460ff16156104d257604051633ee5b89360e01b815260040160405180910390fd5b346000036104f3576040516348e7dc3f60e01b815260040160405180910390fd5b6104ff8160003461122c565b506001601655565b336105106112d2565b6001600160a01b03161415801561054b575060033360009081526014602052604090205460ff166003811115610548576105486144f9565b14155b1561056957604051637b32c26b60e01b815260040160405180910390fd5b600480546040805163234d8e3d60e21b81529051849363ffffffff909316926001600160a01b03851692638d3638f492818301926020928290030181865afa1580156105b9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105dd9190614574565b63ffffffff161461060057604051621ff66160e61b815260040160405180910390fd5b604080516001600160a01b03841681523360208201527fd6e53d12bb441b2201be4949c7f431b383623888df2abfeef43aaaf272802d50910160405180910390a1602080546001600160a01b0319166001600160a01b039290921691909117905550565b813361067660a08301608084016141f1565b6001600160a01b03161461069d5760405163e3613aff60e01b815260040160405180910390fd5b6127108211156106c057604051633345cd4360e11b815260040160405180910390fd5b60045463ffffffff166106d960408501602086016141b4565b63ffffffff16146106fd57604051632a8e462760e01b815260040160405180910390fd5b600061071061070b85614696565b611300565b6000818152600d6020526040908190208590555190915081907fb243c3cea6cd1bbfd64d5d0765f13734ca7b87fdf14e017391fe12a8891434ca906107589086815260200190565b60405180910390a250505050565b6016546001190161078a57604051637ce54e2d60e11b815260040160405180910390fd5b6002601655601a54600160a01b900460ff16156107ba57604051633ee5b89360e01b815260040160405180910390fd5b806000036107db576040516348e7dc3f60e01b815260040160405180910390fd5b6107e482611330565b50506107f183838361122c565b5050600160165550565b6016546000906001190161082257604051637ce54e2d60e11b815260040160405180910390fd5b6002601655601a54600160a01b900460ff161561085257604051633ee5b89360e01b815260040160405180910390fd5b60008061085e846113a6565b909250905060006001826003811115610879576108796144f9565b14610885576002610888565b60035b60008481526008602052604090208054919250829160ff191660018360038111156108b5576108b56144f9565b021790555060008080610910866108f46108cf8b8061478b565b61018001356108de8c8061478b565b6108ef9060608101906040016141b4565b611767565b6003876003811115610908576109086144f9565b14158b6117a9565b91945092509050600061093a8985858a60038a6003811115610934576109346144f9565b14611d36565b90506001600160a01b0383166109508a8061478b565b6109619060808101906060016141f1565b6001600160a01b0316887f0b07a8b0b083f8976b3c832b720632f49cb8ba1e7a99e1b145f51a47d3391cb78c8686336040516109a09493929190614908565b60405180910390a450506001601655509295945050505050565b601754600090600119016109e1576040516363b468cb60e11b815260040160405180910390fd5b6002601755604080516101a08101825260045463ffffffff90811682528c1660208083019190915260008284018190526001600160a01b03808e1660608501528b166080840152600160a08401528351601f88018390048302810183019094528684529260c08301918890889081908401838280828437600092018290525093855250505060208083018a905233604080850191909152606084018390526080840183905260a0840183905260c090930191909152815180830183526001600160a01b038d168082528183018c905283518085019094528352908201869052919250610ace918391611d7f565b60016017559b9a5050505050505050505050565b33610aeb6112d2565b6001600160a01b031614158015610b26575060033360009081526014602052604090205460ff166003811115610b2357610b236144f9565b14155b15610b4457604051637b32c26b60e01b815260040160405180910390fd5b6001600160a01b0381166000908152601f602052604090205460ff16610b7d57604051637840a40d60e01b815260040160405180910390fd5b6001600160a01b0381166000818152601f6020908152604091829020805460ff19169055815192835233908301527f4860b0a180d4b5969c2757493a999f05d0b22318320f154a02170aa239e24b1391015b60405180910390a150565b33610be36112d2565b6001600160a01b031614158015610c1e575060033360009081526014602052604090205460ff166003811115610c1b57610c1b6144f9565b14155b15610c3c57604051637b32c26b60e01b815260040160405180910390fd5b6001600160a01b038116610c63576040516332f9c08d60e11b815260040160405180910390fd5b6001600160a01b0381166000908152601f602052604090205460ff1615610c9d5760405163e2a4506360e01b815260040160405180910390fd5b6001600160a01b0381166000818152601f6020908152604091829020805460ff19166001179055815192835233908301527f3860a100215fe93b6b95ed1ae0870e538f85a73b30d073f63fefc60e08b0c1249101610bcf565b60175460009060011901610d1d576040516363b468cb60e11b815260040160405180910390fd5b6002601755604080516101a08101825260045463ffffffff90811682528b1660208083019190915260008284018190526001600160a01b03808d1660608501528a16608084015260a083018190528351601f87018390048302810183019094528584529260c083019187908790819084018382808284376000920182905250938552505050602080830189905233604080850191909152606084018390526080840183905260a0840183905260c0909301829052825180840184526001600160a01b038d1681528082018b905283518085019094529183523490830152919250610e08918391611d7f565b60016017559a9950505050505050505050565b60175460009060011901610e42576040516363b468cb60e11b815260040160405180910390fd5b6002601755604080516101a08101825260045463ffffffff90811682528b1660208083019190915260008284018190526001600160a01b03808d1660608501528a166080840152600160a08401528351601f87018390048302810183019094528584529260c083019187908790819084018382808284376000920182905250938552505050602080830189905233604080850191909152606084018390526080840183905260a0840183905260c0909301829052825180840184526001600160a01b038d1681528082018b905283518085019094529183523490830152919250610e08918391611d7f565b60175460009060011901610f54576040516363b468cb60e11b815260040160405180910390fd5b6002601755604080516101a08101825260045463ffffffff90811682528c1660208083019190915260008284018190526001600160a01b03808e1660608501528b16608084015260a083018190528351601f88018390048302810183019094528684529260c08301918890889081908401838280828437600092018290525093855250505060208083018a905233604080850191909152606084018390526080840183905260a0840183905260c090930191909152815180830183526001600160a01b038d168082528183018c905283518085019094528352908201869052919250610ace918391611d7f565b3361104a6112d2565b6001600160a01b031614158015611085575060033360009081526014602052604090205460ff166003811115611082576110826144f9565b14155b156110a357604051637b32c26b60e01b815260040160405180910390fd5b806110c157604051633bca644d60e11b815260040160405180910390fd5b63ffffffff821615806110de575060045463ffffffff8381169116145b156110fc57604051632892757b60e21b815260040160405180910390fd5b63ffffffff82166000818152600f602090815260409182902084905581519283526001600160a01b038416908301523382820152517fb07f562723347d6ea7f9f37b3b31f96b65104c3339d1c89e1b6fa88e2410b85c9181900360600190a15050565b803361117160a08301608084016141f1565b6001600160a01b0316146111985760405163e3613aff60e01b815260040160405180910390fd5b60045463ffffffff166111b160408401602085016141b4565b63ffffffff16146111d557604051630ada556f60e31b815260040160405180910390fd5b60006111e361070b84614696565b6000818152600e6020526040808220805460ff191660011790555191925082917f2510041334ede909998b9aefcca4300fc36c670dd00e1f7f0afffaca56adcb399190a2505050565b6002546001600160a01b03168061125657604051630bd7619560e41b815260040160405180910390fd5b6001600160a01b0383166112735761126e818361205b565b611288565b61127d838361217e565b6112888382846122cf565b604080518381526001600160a01b0385166020820152339181019190915284907ff90d3aafcedf55a0da208dd26d915e0ce1870ee9221586012487a0b366106f6590606001610758565b7fc8fcad8db84d3cc18b4c41d551ea0ee66dd599cde068d998e57d5e09332c1320546001600160a01b031690565b6000816040516020016113139190614c82565b604051602081830303815290604052805190602001209050919050565b60408051808201909152600080825260208201526000806113508461230e565b9050600061136682602001518360000151611767565b90506113718161232d565b60020154600160a01b900460ff1661139c5760405163a13f958f60e01b815260040160405180910390fd5b9094909350915050565b336000908152600b6020526040812054819060ff161580156113f457506113cd838061478b565b6113de9060a08101906080016141f1565b6001600160a01b0316336001600160a01b031614155b1561141257604051637c32a2b360e01b815260040160405180910390fd5b60045463ffffffff16611425848061478b565b6114369060408101906020016141b4565b63ffffffff161461145a5760405163b6bb322560e01b815260040160405180910390fd5b60006114696020850185614c95565b91506000905061148561147c868061478b565b61070b90614696565b60008181526008602052604090205490915060ff16821561172a57600c548311156114c3576040516313c06ef360e01b815260040160405180910390fd5b60008160038111156114d7576114d76144f9565b146114f557604051630caaeb0b60e11b815260040160405180910390fd5b601f60006115096080890160608a016141f1565b6001600160a01b0316815260208101919091526040016000205460ff1661154357604051631fa09b5360e31b815260040160405180910390fd5b611590826115546020890189614c95565b60405160200161156693929190614cde565b60408051601f19818403018152919052805160209091012061158b6080890189614cf8565b61236e565b6001600160a01b03166115a960808801606089016141f1565b6001600160a01b0316146115d057604051638579eca560e01b815260040160405180910390fd5b6040805160208082018590528183018690528251808303840181526060909201909252805191012060005b848110156117235761160b6123c0565b15801561166857506015600061162460208b018b614c95565b8481811061163457611634614d3e565b905060200201602081019061164991906141f1565b6001600160a01b0316815260208101919091526040016000205460ff16155b1561168657604051630e1eb7f760e31b815260040160405180910390fd5b6116b98261169760408b018b614c95565b848181106116a7576116a7614d3e565b905060200281019061158b9190614cf8565b6001600160a01b03166116cf60208a018a614c95565b838181106116df576116df614d3e565b90506020020160208101906116f491906141f1565b6001600160a01b03161461171b57604051633a1dd23360e21b815260040160405180910390fd5b6001016115fb565b505061175c565b600181600381111561173e5761173e6144f9565b1461175c57604051636320d2cf60e11b815260040160405180910390fd5b909590945092505050565b6000828260405160200161178b92919091825263ffffffff16602082015260400190565b60405160208183030381529060405280519060200120905092915050565b600080806117ba6020850185614c95565b60008981526009602052604090206117d392909161411f565b5060006117e0858061478b565b6117f19060608101906040016141b4565b63ffffffff16156118325761182f8761180a878061478b565b6101800135611819888061478b565b61182a9060608101906040016141b4565b6123e7565b90505b61183c858061478b565b610120013560000361185657600093509150819050611d2c565b6000611862868061478b565b6118739060c081019060a001614d54565b8061188c57506000898152600e602052604090205460ff165b9050600061189a878061478b565b610120013590508715611c3a5760006118b66020890189614c95565b91506118da90506118c7898061478b565b61012001356000600101546127106123f6565b915080600103611acb5782158015611951575081600a60006118ff60208c018c614c95565b600081811061191057611910614d3e565b905060200201602081019061192591906141f1565b6001600160a01b0390811682526020808301939093526040918201600090812091891681529252902054105b80156119675750601b546001600160a01b031615155b15611a36576015600061197d60208b018b614c95565b600081811061198e5761198e614d3e565b90506020020160208101906119a391906141f1565b6001600160a01b03168152602081019190915260400160002054610100900460ff166119e2576040516310c4e50960e01b815260040160405180910390fd5b600080611a238d8d866119f860208f018f614c95565b6000818110611a0957611a09614d3e565b9050602002016020810190611a1e91906141f1565b61240d565b9099509750949550611d2c945050505050565b81600a6000611a4860208c018c614c95565b6000818110611a5957611a59614d3e565b9050602002016020810190611a6e91906141f1565b6001600160a01b03166001600160a01b031681526020019081526020016000206000866001600160a01b03166001600160a01b031681526020019081526020016000206000828254611ac09190614d87565b90915550611c389050565b6000611ad78284614db0565b905060005b611ae7600184614d87565b811015611b845781600a6000611b0060208e018e614c95565b85818110611b1057611b10614d3e565b9050602002016020810190611b2591906141f1565b6001600160a01b03166001600160a01b031681526020019081526020016000206000886001600160a01b03166001600160a01b031681526020019081526020016000206000828254611b779190614d87565b9091555050600101611adc565b506000611b918385614dc4565b611b9b9083614dd8565b905080600a6000611baf60208e018e614c95565b611bba600189614d87565b818110611bc957611bc9614d3e565b9050602002016020810190611bde91906141f1565b6001600160a01b03166001600160a01b031681526020019081526020016000206000886001600160a01b03166001600160a01b031681526020019081526020016000206000828254611c309190614d87565b909155505050505b505b611c44878061478b565b611c559060608101906040016141b4565b60045463ffffffff9081169116148015611c7b57506000611c758a61232d565b60030154115b15611ca75760008981526007602052604081206004018054839290611ca1908490614d87565b90915550505b8115611cd25760008a8152600e60205260409020805460ff191690559450909250829150611d2c9050565b60008a8152600d602052604081208054908290559080611d1e8c8786868503611d0857611cff8e8061478b565b60e00135611d0a565b865b611d148f8061478b565b61014001356125cc565b909950975094955050505050505b9450945094915050565b6000611d5d84611d46888061478b565b611d579060808101906060016141f1565b876122cf565b611d7383868685611d6e8b8061478b565b612651565b50835b95945050505050565b601a54600090600160a01b900460ff1615611dad57604051633ee5b89360e01b815260040160405180910390fd5b82516000906001600160a01b0316158015611dcb5750602084015115155b15611de95760405163ae715ad360e01b815260040160405180910390fd5b611df685602001516127c5565b60608601519091506001600160a01b0316611e235760405162845fdd60e41b815260040160405180910390fd5b6127108560e001511115611e4a5760405163388d723160e11b815260040160405180910390fd5b6040805180820190915260008082526020820181905290819086516000906001600160a01b031615611ff1576000611e858960000151611330565b90935090506000611e958261232d565b84518c5163ffffffff9081169116149350905082611ebd5780546001600160a01b0316611ec3565b60208401515b95506001600160a01b038616611eec5760405163965a46c160e01b815260040160405180910390fd5b6003810154838015611efe5750600081115b15611f4f5760008b602001518360040154611f199190614dd8565b905081811115611f3c576040516348ba8bf160e01b815260040160405180910390fd5b6000848152600760205260409020600401555b50835163ffffffff1660408c01526020808501516101808d01528a015115611fee57611f838a600001518b6020015161217e565b611f9c828b60000151888d602001518f60e001516127fe565b6101208c01528951611fe7906001600160a01b03888116911614611fce576001820154600160a01b900460ff16611fdb565b8154600160a01b900460ff165b60128c60200151612881565b6101408c01525b50505b6003805490600061200183614deb565b909155506101608a015261201489611300565b60208801519093501561203457612034838860000151896020015161122c565b61204c838a8a600001518b6020015189878a886128ec565b509093505050505b9392505050565b804710156120b05760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a20696e73756666696369656e742062616c616e636500000060448201526064015b60405180910390fd5b6000826001600160a01b03168260405160006040518083038185875af1925050503d80600081146120fd576040519150601f19603f3d011682016040523d82523d6000602084013e612102565b606091505b50509050806121795760405162461bcd60e51b815260206004820152603a60248201527f416464726573733a20756e61626c6520746f2073656e642076616c75652c207260448201527f6563697069656e74206d6179206861766520726576657274656400000000000060648201526084016120a7565b505050565b8060000361218a575050565b6001600160a01b0382166121b157604051632a38b13360e01b815260040160405180910390fd5b6040516370a0823160e01b815230600482015282906000906001600160a01b038316906370a0823190602401602060405180830381865afa1580156121fa573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061221e9190614e04565b90506122356001600160a01b038316333086612ad3565b6040516370a0823160e01b8152306004820152839082906001600160a01b038516906370a0823190602401602060405180830381865afa15801561227d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122a19190614e04565b6122ab9190614d87565b146122c957604051630e40773560e21b815260040160405180910390fd5b50505050565b806000036122dc57505050565b6001600160a01b03831661230357604051633a48ca7b60e11b815260040160405180910390fd5b612179838383612b3e565b604080518082019091526000808252602082015261040c826000612b6e565b6000818152600760205260408120600180820154839291600160a01b90910460ff1610156120545760405163618cca3f60e11b815260040160405180910390fd5b60006123b861237c85612c66565b84848080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250612ca192505050565b949350505050565b6000806123cb6112d2565b6001600160a01b031614806123e2575060125460ff165b905090565b60006123b88484846000612cc5565b6000816124038486614e1d565b6123b89190614db0565b600080600061241b86612d00565b601b546040516369a933a560e01b81526001600160a01b03808416600483015260248201899052306044830152600060648301529293509116906369a933a590608401600060405180830381600087803b15801561247857600080fd5b505af115801561248c573d6000803e3d6000fd5b5050601b54604051631a4ca37b60e21b81526001600160a01b038581166004830152602482018a90523060448301526000945090911691506369328dec906064016020604051808303816000875af11580156124ec573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125109190614e04565b9050858110156125335760405163407559a360e11b815260040160405180910390fd5b6000888152601d60205260409020869055601c5461271090612556908890614e1d565b6125609190614db0565b6000898152601e60209081526040918290209290925580516001600160a01b038581168252928101899052918716918a917f2d3ba0fa5be2ef8cb1ec8920a07a6cbccc2397b18ca3e70f48ea695500b8f218910160405180910390a35084925090505b94509492505050565b60008060006125da8861232d565b60018101549091506001600160a01b0390811690881681036126025786935091506126479050565b866000036126165786935091506126479050565b6126408989838a61263b60128860010160149054906101000a900460ff168c8e612d39565b612d85565b9350935050505b9550959350505050565b7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47061267f60c0830183614cf8565b60405161268d929190614e34565b604051809103902003156127be576000806127546126b160808501606086016141f1565b6127105a6126bf9190614d87565b600061010063fd614f4160e01b8c8c8c8c6126db5760006126ed565b6126ed6101208d016101008e016141f1565b6126fa60208e018e6141b4565b61270760c08f018f614cf8565b60405160240161271d9796959493929190614e44565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152612ec4565b9150915083158015612764575081155b15612781576040516264cdd360e41b815260040160405180910390fd5b867fb1a4ab59facaedd6d3a71da3902e0a1fa5b99750c0e20cd878334378a41cb33583836040516127b3929190614e97565b60405180910390a250505b5050505050565b63ffffffff81166000908152600f6020526040902054806127f957604051630c3bcd4960e41b815260040160405180910390fd5b919050565b60008260000361281057506000611d76565b846001600160a01b0316846001600160a01b031603612830575081611d76565b600061283b8761232d565b905060006128748888888861263b8760010160149054906101000a900460ff168860000160149054906101000a900460ff168c8c612d39565b5098975050505050505050565b60008260ff168460ff1603612897575080612054565b60008360ff168560ff1610156128cd576128b18585614eb2565b6128bc90600a614faf565b6128c69084614e1d565b90506123b8565b6128d78486614eb2565b6128e290600a614faf565b611d769084614db0565b610120870151801561295e578161295e57604051632770a7eb60e21b8152306004820152602481018290526001600160a01b03841690639dc29fac90604401600060405180830381600087803b15801561294557600080fd5b505af1158015612959573d6000803e3d6000fd5b505050505b6000846000015185602001516003848d604051602001612982959493929190614fbe565b60408051601f1981840301815282825260208054639fa92f9d60e01b8552925191945060009384936001600160a01b031692639fa92f9d926004808401939192918290030181865afa1580156129dc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a009190614ffd565b6001600160a01b031663fa31de018c602001518a866040518463ffffffff1660e01b8152600401612a339392919061501a565b6000604051808303816000875af1158015612a52573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052612a7a919081019061503f565b91509150818b61016001518d7fed8e6ba697dd65259e5ce532ac08ff06d1a3607bcec58f8f0937fe36a5666c548e8e8e8c88604051612abd9594939291906150c1565b60405180910390a4505050505050505050505050565b6040516001600160a01b03808516602483015283166044820152606481018290526122c99085906323b872dd60e01b906084015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152612f4e565b6040516001600160a01b03831660248201526044810182905261217990849063a9059cbb60e01b90606401612b07565b604080518082019091526000808252602082015260408051808201909152600080825260208201526001600160a01b038416612bab57905061040c565b506001600160a01b03831660009081526005830160209081526040918290208251808401909352805463ffffffff168084526001909101549183019190915215612bf657905061040c565b612c008484613020565b15612c2657600483015463ffffffff1681526001600160a01b0384166020820152612054565b506001600160a01b03831660009081526006830160209081526040918290208251808401909352805463ffffffff16835260010154908201529392505050565b6040517f19457468657265756d205369676e6564204d6573736167653a0a3332000000006020820152603c8101829052600090605c01611313565b6000806000612cb0858561305f565b91509150612cbd816130a4565b509392505050565b600481015460009063ffffffff90811690841603612ce45750826123b8565b612ced8561232d565b546001600160a01b031695945050505050565b600080612d0c8361232d565b600101546001600160a01b031690508061040c57604051630558a50760e31b815260040160405180910390fd5b600082600003612d4b575060006123b8565b6000612710612d5a8482614d87565b612d649086614e1d565b612d6e9190614db0565b9050612d7b868683612881565b9695505050505050565b600085815260186020526040812081908190612da0816131f1565b15612dd257612dc6612db28a8a61320e565b612dbc8b8a61320e565b839190898961328e565b87935093505050612647565b6000612ddd8a61232d565b600201546001600160a01b0390811691508990612dfe908216836000613597565b612e126001600160a01b038216838a6136ac565b6001600160a01b03821663d460f0a2898c8c8b612e31610e1042614dd8565b6040516001600160e01b031960e088901b16815260048101959095526001600160a01b0393841660248601529290911660448401526064830152608482015260a4016020604051808303816000875af1158015612e92573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612eb69190614e04565b899550955050505050612647565b6000606060008060008661ffff166001600160401b03811115612ee957612ee9614591565b6040519080825280601f01601f191660200182016040528015612f13576020820181803683370190505b5090506000808751602089018b8e8ef191503d925086831115612f34578692505b828152826000602083013e90999098509650505050505050565b6000612fa3826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b031661375e9092919063ffffffff16565b8051909150156121795780806020019051810190612fc19190615110565b6121795760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b60648201526084016120a7565b6001600160a01b038216600090815260068201602052604081205463ffffffff161561304e5750600061040c565b50506001600160a01b03163b151590565b60008082516041036130955760208301516040840151606085015160001a6130898782858561376d565b9450945050505061309d565b506000905060025b9250929050565b60008160048111156130b8576130b86144f9565b036130c05750565b60018160048111156130d4576130d46144f9565b036131215760405162461bcd60e51b815260206004820152601860248201527f45434453413a20696e76616c6964207369676e6174757265000000000000000060448201526064016120a7565b6002816004811115613135576131356144f9565b036131825760405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e6774680060448201526064016120a7565b6003816004811115613196576131966144f9565b036131ee5760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c604482015261756560f01b60648201526084016120a7565b50565b600c81015460009060ff1615801561040c57505060080154151590565b60008281526019602090815260408083206001600160a01b0385168085529083528184205486855260189093529083206008018054849360ff169291908390811061325b5761325b614d3e565b6000918252602090912001546001600160a01b0316146123b85760405163054e442960e41b815260040160405180910390fd5b600c85015460009060ff16156132d65760405162461bcd60e51b815260206004820152600d60248201526c191a5cd8589b1959081c1bdbdb609a1b60448201526064016120a7565b85600a018560ff16815481106132ee576132ee614d3e565b90600052602060002001548311156133415760405162461bcd60e51b81526020600482015260166024820152756d6f7265207468616e20706f6f6c2062616c616e636560501b60448201526064016120a7565b600080600088600a0180548060200260200160405190810160405280929190818152602001828054801561339457602002820191906000526020600020905b815481526020019060010190808311613380575b505050505090506133a8898989898561382e565b9093509150848310156133ea5760405162461bcd60e51b815260206004820152600a6024820152696479203c206d696e447960b01b60448201526064016120a7565b6000896009018860ff168154811061340457613404614d3e565b90600052602060002001546402540be4008b60060154856134259190614e1d565b61342f9190614db0565b6134399190614db0565b905086828a60ff168151811061345157613451614d3e565b60200260200101516134639190614dd8565b8a600a018a60ff168154811061347b5761347b614d3e565b90600052602060002001819055508084838a60ff16815181106134a0576134a0614d3e565b60200260200101516134b29190614d87565b6134bc9190614d87565b8a600a018960ff16815481106134d4576134d4614d3e565b600091825260209091200155801561353957808a600b018960ff16815481106134ff576134ff614d3e565b90600052602060002001546135149190614dd8565b8a600b018960ff168154811061352c5761352c614d3e565b6000918252602090912001555b8954604080518981526020810187905260ff8c8116828401528b16606082015290513392917f28d4cf2d5709da3b474b5f05cfd7083faffd601f9500d1f8439b8a13ec7df320919081900360800190a3509198975050505050505050565b8015806136115750604051636eb1769f60e11b81523060048201526001600160a01b03838116602483015284169063dd62ed3e90604401602060405180830381865afa1580156135eb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061360f9190614e04565b155b61367c5760405162461bcd60e51b815260206004820152603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527520746f206e6f6e2d7a65726f20616c6c6f77616e636560501b60648201526084016120a7565b6040516001600160a01b03831660248201526044810182905261217990849063095ea7b360e01b90606401612b07565b604051636eb1769f60e11b81523060048201526001600160a01b038381166024830152600091839186169063dd62ed3e90604401602060405180830381865afa1580156136fd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137219190614e04565b61372b9190614dd8565b6040516001600160a01b0385166024820152604481018290529091506122c990859063095ea7b360e01b90606401612b07565b60606123b884846000856139f4565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a08311156137a457506000905060036125c3565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa1580156137f8573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b038116613821576000600192509250506125c3565b9660009650945050505050565b60008060008760090180548060200260200160405190810160405280929190818152602001828054801561388157602002820191906000526020600020905b81548152602001906001019080831161386d575b5050505050905060006138948583613acf565b905080518860ff161080156138ac575080518760ff16105b6138ed5760405162461bcd60e51b8152602060048201526012602482015271696e646578206f7574206f662072616e676560701b60448201526064016120a7565b6000818960ff168151811061390457613904614d3e565b6020026020010151838a60ff168151811061392157613921614d3e565b6020026020010151886139349190614e1d565b61393e9190614dd8565b9050600061395761394e8c613bd9565b8b8b8587613be4565b9050600181848b60ff168151811061397157613971614d3e565b60200260200101516139839190614d87565b61398d9190614d87565b95506402540be4008b60050154876139a59190614e1d565b6139af9190614db0565b9450838960ff16815181106139c6576139c6614d3e565b602002602001015185876139da9190614d87565b6139e49190614db0565b9550505050509550959350505050565b606082471015613a555760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b60648201526084016120a7565b600080866001600160a01b03168587604051613a71919061512d565b60006040518083038185875af1925050503d8060008114613aae576040519150601f19603f3d011682016040523d82523d6000602084013e613ab3565b606091505b5091509150613ac487838387613e4b565b979650505050505050565b81518151606091908114613b1c5760405162461bcd60e51b81526020600482015260146024820152736d69736d61746368206d756c7469706c6965727360601b60448201526064016120a7565b6000816001600160401b03811115613b3657613b36614591565b604051908082528060200260200182016040528015613b5f578160200160208202803683370190505b50905060005b82811015613bd057848181518110613b7f57613b7f614d3e565b6020026020010151868281518110613b9957613b99614d3e565b6020026020010151613bab9190614e1d565b828281518110613bbd57613bbd614d3e565b6020908102919091010152600101613b65565b50949350505050565b600061040c82613ec4565b805160009060ff80861690871603613c3e5760405162461bcd60e51b815260206004820152601760248201527f636f6d7061726520746f6b656e20746f20697473656c6600000000000000000060448201526064016120a7565b808660ff16108015613c525750808560ff16105b613c905760405162461bcd60e51b815260206004820152600f60248201526e1d1bdad95b881b9bdd08199bdd5b99608a1b60448201526064016120a7565b6000613c9c8489613f0d565b905080600080613cac8b86614e1d565b90506000805b86811015613d33578b60ff168103613ccc57899150613cff565b8a60ff168114613cf757888181518110613ce857613ce8614d3e565b60200260200101519150613cff565b600101613cb2565b613d098285614dd8565b9350613d158783614e1d565b613d1f8787614e1d565b613d299190614db0565b9450600101613cb2565b50613d3e8683614e1d565b6064613d4a8787614e1d565b613d549190614e1d565b613d5e9190614db0565b9350600082613d6e606488614e1d565b613d789190614db0565b613d829085614dd8565b9050600086815b610100811015613e02578192508884836002613da59190614e1d565b613daf9190614dd8565b613db99190614d87565b88613dc48480614e1d565b613dce9190614dd8565b613dd89190614db0565b9150613de482846140ba565b15613dfa57509850611d76975050505050505050565b600101613d89565b5060405162461bcd60e51b815260206004820152601e60248201527f417070726f78696d6174696f6e20646964206e6f7420636f6e7665726765000060448201526064016120a7565b60608315613eba578251600003613eb3576001600160a01b0385163b613eb35760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016120a7565b50816123b8565b6123b883836140d0565b600481015460028201546001830154909190808314801590613ee557508142105b15613f06576000846003015490508083038142038502428503840201049350505b5050919050565b815160009081805b82811015613f4c57858181518110613f2f57613f2f614d3e565b602002602001015182613f429190614dd8565b9150600101613f15565b5080600003613f605760009250505061040c565b60008181613f6e8588614e1d565b905060005b61010081101561407b578260005b87811015613fcc57878b8281518110613f9c57613f9c614d3e565b6020026020010151613fae9190614e1d565b613fb88684614e1d565b613fc29190614db0565b9150600101613f81565b5083945080876001613fde9190614dd8565b613fe89190614e1d565b606485613ff58287614d87565b613fff9190614e1d565b6140099190614db0565b6140139190614dd8565b8461401e8984614e1d565b606461402a8a88614e1d565b6140349190614db0565b61403e9190614dd8565b6140489190614e1d565b6140529190614db0565b935061405e84866140ba565b15614072578397505050505050505061040c565b50600101613f73565b5060405162461bcd60e51b81526020600482015260136024820152724420646f6573206e6f7420636f6e766572676560681b60448201526064016120a7565b600060026140c884846140fa565b109392505050565b8151156140e05781518083602001fd5b8060405162461bcd60e51b81526004016120a7919061513f565b6000818311156141155761410e8284614d87565b905061040c565b6120548383614d87565b828054828255906000526020600020908101928215614172579160200282015b828111156141725781546001600160a01b0319166001600160a01b0384351617825560209092019160019091019061413f565b5061417e929150614182565b5090565b5b8082111561417e5760008155600101614183565b63ffffffff811681146131ee57600080fd5b80356127f981614197565b6000602082840312156141c657600080fd5b813561205481614197565b6001600160a01b03811681146131ee57600080fd5b80356127f9816141d1565b60006020828403121561420357600080fd5b8135612054816141d1565b60006020828403121561422057600080fd5b5035919050565b6020808252825182820181905260009190848201906040850190845b818110156142685783516001600160a01b031683529284019291840191600101614243565b50909695505050505050565b60006101a0828403121561428757600080fd5b50919050565b600080604083850312156142a057600080fd5b82356001600160401b038111156142b657600080fd5b6142c285828601614274565b95602094909401359450505050565b6000806000606084860312156142e657600080fd5b8335925060208401356142f8816141d1565b929592945050506040919091013590565b60006020828403121561431b57600080fd5b81356001600160401b0381111561433157600080fd5b820160a0818503121561205457600080fd5b60008083601f84011261435557600080fd5b5081356001600160401b0381111561436c57600080fd5b60208301915083602082850101111561309d57600080fd5b60008060008060008060008060006101008a8c0312156143a357600080fd5b89356143ae81614197565b985060208a01356143be816141d1565b975060408a01356143ce816141d1565b965060608a01356143de816141d1565b955060808a0135945060a08a0135935060c08a01356001600160401b0381111561440757600080fd5b6144138c828d01614343565b9a9d999c50979a9699959894979660e00135949350505050565b60008060008060008060008060e0898b03121561444957600080fd5b883561445481614197565b97506020890135614464816141d1565b96506040890135614474816141d1565b95506060890135614484816141d1565b94506080890135935060a0890135925060c08901356001600160401b038111156144ad57600080fd5b6144b98b828c01614343565b999c989b5096995094979396929594505050565b600080604083850312156144e057600080fd5b82356144eb81614197565b946020939093013593505050565b634e487b7160e01b600052602160045260246000fd5b600481106131ee57634e487b7160e01b600052602160045260246000fd5b6020810161453a8361450f565b91905290565b60006020828403121561455257600080fd5b81356001600160401b0381111561456857600080fd5b6123b884828501614274565b60006020828403121561458657600080fd5b815161205481614197565b634e487b7160e01b600052604160045260246000fd5b6040516101a081016001600160401b03811182821017156145ca576145ca614591565b60405290565b604051601f8201601f191681016001600160401b03811182821017156145f8576145f8614591565b604052919050565b80151581146131ee57600080fd5b80356127f981614600565b60006001600160401b0382111561463257614632614591565b50601f01601f191660200190565b600082601f83011261465157600080fd5b813561466461465f82614619565b6145d0565b81815284602083860101111561467957600080fd5b816020850160208301376000918101602001919091529392505050565b60006101a082360312156146a957600080fd5b6146b16145a7565b6146ba836141a9565b81526146c8602084016141a9565b60208201526146d9604084016141a9565b60408201526146ea606084016141e6565b60608201526146fb608084016141e6565b608082015261470c60a0840161460e565b60a082015260c08301356001600160401b0381111561472a57600080fd5b61473636828601614640565b60c08301525060e083013560e08201526101006147548185016141e6565b9082015261012083810135908201526101408084013590820152610160808401359082015261018092830135928101929092525090565b6000823561019e198336030181126147a257600080fd5b9190910192915050565b6000808335601e198436030181126147c357600080fd5b83016020810192503590506001600160401b038111156147e257600080fd5b80360382131561309d57600080fd5b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b6000808335601e1984360301811261483157600080fd5b83016020810192503590506001600160401b0381111561485057600080fd5b8060051b360382131561309d57600080fd5b8183526000602080850194508260005b858110156148a0578135614885816141d1565b6001600160a01b031687529582019590820190600101614872565b509495945050505050565b81835260006020808501808196508560051b810191508460005b878110156148fb5782840389526148dc82886147ac565b6148e78682846147f1565b9a87019a95505050908401906001016148c5565b5091979650505050505050565b608081526000610120863561019e1988360301811261492657600080fd5b60a06080850152870161494882850161493e836141a9565b63ffffffff169052565b614954602082016141a9565b6101406149688187018363ffffffff169052565b614974604084016141a9565b915061016061498a8188018463ffffffff169052565b614996606085016141e6565b92506101806149af818901856001600160a01b03169052565b6149bb608086016141e6565b93506101a06149d4818a01866001600160a01b03169052565b6149e060a0870161460e565b8015156101c08b015294506149f860c08701876147ac565b9550816101e08b0152614a106102c08b0187836147f1565b9550505060e0850135610200890152610100614a2d8187016141e6565b6001600160a01b03166102208a0152858701356102408a0152838601356102608a0152828601356102808a0152818601356102a08a0152614a7160208e018e61481a565b97509550607f199350838986030160a08a0152614a8f858888614862565b9650614a9e60408e018e61481a565b96509450838988030160c08a0152614ab78787876148ab565b9650614ac560608e016141e6565b6001600160a01b03811660e08b01529550614ae360808e018e6147ac565b965094508389880301818a015250505050614aff8383836147f1565b9350505050614b1960208301866001600160a01b03169052565b836040830152611d7660608301846001600160a01b03169052565b60005b83811015614b4f578181015183820152602001614b37565b50506000910152565b60008151808452614b70816020860160208601614b34565b601f01601f19169290920160200192915050565b805163ffffffff16825260006101a06020830151614baa602086018263ffffffff169052565b506040830151614bc2604086018263ffffffff169052565b506060830151614bdd60608601826001600160a01b03169052565b506080830151614bf860808601826001600160a01b03169052565b5060a0830151614c0c60a086018215159052565b5060c08301518160c0860152614c2482860182614b58565b91505060e083015160e085015261010080840151614c4c828701826001600160a01b03169052565b50506101208381015190850152610140808401519085015261016080840151908501526101809283015192909301919091525090565b6020815260006120546020830184614b84565b6000808335601e19843603018112614cac57600080fd5b8301803591506001600160401b03821115614cc657600080fd5b6020019150600581901b360382131561309d57600080fd5b838152604060208201526000611d76604083018486614862565b6000808335601e19843603018112614d0f57600080fd5b8301803591506001600160401b03821115614d2957600080fd5b60200191503681900382131561309d57600080fd5b634e487b7160e01b600052603260045260246000fd5b600060208284031215614d6657600080fd5b813561205481614600565b634e487b7160e01b600052601160045260246000fd5b8181038181111561040c5761040c614d71565b634e487b7160e01b600052601260045260246000fd5b600082614dbf57614dbf614d9a565b500490565b600082614dd357614dd3614d9a565b500690565b8082018082111561040c5761040c614d71565b600060018201614dfd57614dfd614d71565b5060010190565b600060208284031215614e1657600080fd5b5051919050565b808202811582820484141761040c5761040c614d71565b8183823760009101908152919050565b878152602081018790526001600160a01b0386811660408301528516606082015263ffffffff8416608082015260c060a08201819052600090614e8a90830184866147f1565b9998505050505050505050565b82151581526040602082015260006123b86040830184614b58565b60ff828116828216039081111561040c5761040c614d71565b600181815b80851115614f06578160001904821115614eec57614eec614d71565b80851615614ef957918102915b93841c9390800290614ed0565b509250929050565b600082614f1d5750600161040c565b81614f2a5750600061040c565b8160018114614f405760028114614f4a57614f66565b600191505061040c565b60ff841115614f5b57614f5b614d71565b50506001821b61040c565b5060208310610133831016604e8410600b8410161715614f89575081810a61040c565b614f938383614ecb565b8060001904821115614fa757614fa7614d71565b029392505050565b600061205460ff841683614f0e565b63ffffffff60e01b8660e01b168152846004820152614fdc8461450f565b60f89390931b60248401526025830191909152604582015260650192915050565b60006020828403121561500f57600080fd5b8151612054816141d1565b63ffffffff84168152826020820152606060408201526000611d766060830184614b58565b6000806040838503121561505257600080fd5b8251915060208301516001600160401b0381111561506f57600080fd5b8301601f8101851361508057600080fd5b805161508e61465f82614619565b8181528660208385010111156150a357600080fd5b6150b4826020830160208601614b34565b8093505050509250929050565b60a0815260006150d460a0830188614b84565b6001600160a01b038781166020850152604084018790528516606084015282810360808401526151048185614b58565b98975050505050505050565b60006020828403121561512257600080fd5b815161205481614600565b600082516147a2818460208701614b34565b6020815260006120546020830184614b5856fea264697066735822122083a85792fc0e29cd905f805175da0fd17a6a6073b7d3197f199a713d8d3885bf64736f6c63430008110033

Deployed Bytecode

0x60806040526004361061011f5760003560e01c80636989ca7c116100a0578063affed0e011610064578063affed0e01461033e578063b49c53a714610353578063bfd7903014610373578063c2fb26a6146103b0578063cb8058ba146103d557600080fd5b80636989ca7c146102b85780638a336231146102d85780638aac16ba146102f857806391f5de791461030b57806393f18ac51461031e57600080fd5b806341bdc8b5116100e757806341bdc8b51461020a578063541267111461022a57806359efa1621461024a57806363e3e7d21461026a578063674dc9331461029857600080fd5b8063121cca3114610124578063159e041f146101615780631a8bc0e1146101aa5780632424401f146101d75780633339df96146101ec575b600080fd5b34801561013057600080fd5b5061014461013f3660046141b4565b6103f5565b6040516001600160a01b0390911681526020015b60405180910390f35b34801561016d57600080fd5b5061019a61017c3660046141f1565b6001600160a01b03166000908152601f602052604090205460ff1690565b6040519015158152602001610158565b3480156101b657600080fd5b506101ca6101c536600461420e565b610412565b6040516101589190614227565b6101ea6101e536600461420e565b61047e565b005b3480156101f857600080fd5b506020546001600160a01b0316610144565b34801561021657600080fd5b506101ea6102253660046141f1565b610507565b34801561023657600080fd5b506101ea61024536600461428d565b610664565b34801561025657600080fd5b506101ea6102653660046142d1565b610766565b34801561027657600080fd5b5061028a610285366004614309565b6107fb565b604051908152602001610158565b3480156102a457600080fd5b5061028a6102b3366004614384565b6109ba565b3480156102c457600080fd5b506101ea6102d33660046141f1565b610ae2565b3480156102e457600080fd5b506101ea6102f33660046141f1565b610bda565b61028a61030636600461442d565b610cf6565b61028a61031936600461442d565b610e1b565b34801561032a57600080fd5b5061028a610339366004614384565b610f2d565b34801561034a57600080fd5b5060035461028a565b34801561035f57600080fd5b506101ea61036e3660046144cd565b611041565b34801561037f57600080fd5b506103a361038e36600461420e565b60009081526008602052604090205460ff1690565b604051610158919061452d565b3480156103bc57600080fd5b5060045460405163ffffffff9091168152602001610158565b3480156103e157600080fd5b506101ea6103f0366004614540565b61115f565b63ffffffff81166000908152600f60205260408120545b92915050565b60008181526009602090815260409182902080548351818402810184019094528084526060939283018282801561047257602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610454575b50505050509050919050565b601654600119016104a257604051637ce54e2d60e11b815260040160405180910390fd5b6002601655601a54600160a01b900460ff16156104d257604051633ee5b89360e01b815260040160405180910390fd5b346000036104f3576040516348e7dc3f60e01b815260040160405180910390fd5b6104ff8160003461122c565b506001601655565b336105106112d2565b6001600160a01b03161415801561054b575060033360009081526014602052604090205460ff166003811115610548576105486144f9565b14155b1561056957604051637b32c26b60e01b815260040160405180910390fd5b600480546040805163234d8e3d60e21b81529051849363ffffffff909316926001600160a01b03851692638d3638f492818301926020928290030181865afa1580156105b9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105dd9190614574565b63ffffffff161461060057604051621ff66160e61b815260040160405180910390fd5b604080516001600160a01b03841681523360208201527fd6e53d12bb441b2201be4949c7f431b383623888df2abfeef43aaaf272802d50910160405180910390a1602080546001600160a01b0319166001600160a01b039290921691909117905550565b813361067660a08301608084016141f1565b6001600160a01b03161461069d5760405163e3613aff60e01b815260040160405180910390fd5b6127108211156106c057604051633345cd4360e11b815260040160405180910390fd5b60045463ffffffff166106d960408501602086016141b4565b63ffffffff16146106fd57604051632a8e462760e01b815260040160405180910390fd5b600061071061070b85614696565b611300565b6000818152600d6020526040908190208590555190915081907fb243c3cea6cd1bbfd64d5d0765f13734ca7b87fdf14e017391fe12a8891434ca906107589086815260200190565b60405180910390a250505050565b6016546001190161078a57604051637ce54e2d60e11b815260040160405180910390fd5b6002601655601a54600160a01b900460ff16156107ba57604051633ee5b89360e01b815260040160405180910390fd5b806000036107db576040516348e7dc3f60e01b815260040160405180910390fd5b6107e482611330565b50506107f183838361122c565b5050600160165550565b6016546000906001190161082257604051637ce54e2d60e11b815260040160405180910390fd5b6002601655601a54600160a01b900460ff161561085257604051633ee5b89360e01b815260040160405180910390fd5b60008061085e846113a6565b909250905060006001826003811115610879576108796144f9565b14610885576002610888565b60035b60008481526008602052604090208054919250829160ff191660018360038111156108b5576108b56144f9565b021790555060008080610910866108f46108cf8b8061478b565b61018001356108de8c8061478b565b6108ef9060608101906040016141b4565b611767565b6003876003811115610908576109086144f9565b14158b6117a9565b91945092509050600061093a8985858a60038a6003811115610934576109346144f9565b14611d36565b90506001600160a01b0383166109508a8061478b565b6109619060808101906060016141f1565b6001600160a01b0316887f0b07a8b0b083f8976b3c832b720632f49cb8ba1e7a99e1b145f51a47d3391cb78c8686336040516109a09493929190614908565b60405180910390a450506001601655509295945050505050565b601754600090600119016109e1576040516363b468cb60e11b815260040160405180910390fd5b6002601755604080516101a08101825260045463ffffffff90811682528c1660208083019190915260008284018190526001600160a01b03808e1660608501528b166080840152600160a08401528351601f88018390048302810183019094528684529260c08301918890889081908401838280828437600092018290525093855250505060208083018a905233604080850191909152606084018390526080840183905260a0840183905260c090930191909152815180830183526001600160a01b038d168082528183018c905283518085019094528352908201869052919250610ace918391611d7f565b60016017559b9a5050505050505050505050565b33610aeb6112d2565b6001600160a01b031614158015610b26575060033360009081526014602052604090205460ff166003811115610b2357610b236144f9565b14155b15610b4457604051637b32c26b60e01b815260040160405180910390fd5b6001600160a01b0381166000908152601f602052604090205460ff16610b7d57604051637840a40d60e01b815260040160405180910390fd5b6001600160a01b0381166000818152601f6020908152604091829020805460ff19169055815192835233908301527f4860b0a180d4b5969c2757493a999f05d0b22318320f154a02170aa239e24b1391015b60405180910390a150565b33610be36112d2565b6001600160a01b031614158015610c1e575060033360009081526014602052604090205460ff166003811115610c1b57610c1b6144f9565b14155b15610c3c57604051637b32c26b60e01b815260040160405180910390fd5b6001600160a01b038116610c63576040516332f9c08d60e11b815260040160405180910390fd5b6001600160a01b0381166000908152601f602052604090205460ff1615610c9d5760405163e2a4506360e01b815260040160405180910390fd5b6001600160a01b0381166000818152601f6020908152604091829020805460ff19166001179055815192835233908301527f3860a100215fe93b6b95ed1ae0870e538f85a73b30d073f63fefc60e08b0c1249101610bcf565b60175460009060011901610d1d576040516363b468cb60e11b815260040160405180910390fd5b6002601755604080516101a08101825260045463ffffffff90811682528b1660208083019190915260008284018190526001600160a01b03808d1660608501528a16608084015260a083018190528351601f87018390048302810183019094528584529260c083019187908790819084018382808284376000920182905250938552505050602080830189905233604080850191909152606084018390526080840183905260a0840183905260c0909301829052825180840184526001600160a01b038d1681528082018b905283518085019094529183523490830152919250610e08918391611d7f565b60016017559a9950505050505050505050565b60175460009060011901610e42576040516363b468cb60e11b815260040160405180910390fd5b6002601755604080516101a08101825260045463ffffffff90811682528b1660208083019190915260008284018190526001600160a01b03808d1660608501528a166080840152600160a08401528351601f87018390048302810183019094528584529260c083019187908790819084018382808284376000920182905250938552505050602080830189905233604080850191909152606084018390526080840183905260a0840183905260c0909301829052825180840184526001600160a01b038d1681528082018b905283518085019094529183523490830152919250610e08918391611d7f565b60175460009060011901610f54576040516363b468cb60e11b815260040160405180910390fd5b6002601755604080516101a08101825260045463ffffffff90811682528c1660208083019190915260008284018190526001600160a01b03808e1660608501528b16608084015260a083018190528351601f88018390048302810183019094528684529260c08301918890889081908401838280828437600092018290525093855250505060208083018a905233604080850191909152606084018390526080840183905260a0840183905260c090930191909152815180830183526001600160a01b038d168082528183018c905283518085019094528352908201869052919250610ace918391611d7f565b3361104a6112d2565b6001600160a01b031614158015611085575060033360009081526014602052604090205460ff166003811115611082576110826144f9565b14155b156110a357604051637b32c26b60e01b815260040160405180910390fd5b806110c157604051633bca644d60e11b815260040160405180910390fd5b63ffffffff821615806110de575060045463ffffffff8381169116145b156110fc57604051632892757b60e21b815260040160405180910390fd5b63ffffffff82166000818152600f602090815260409182902084905581519283526001600160a01b038416908301523382820152517fb07f562723347d6ea7f9f37b3b31f96b65104c3339d1c89e1b6fa88e2410b85c9181900360600190a15050565b803361117160a08301608084016141f1565b6001600160a01b0316146111985760405163e3613aff60e01b815260040160405180910390fd5b60045463ffffffff166111b160408401602085016141b4565b63ffffffff16146111d557604051630ada556f60e31b815260040160405180910390fd5b60006111e361070b84614696565b6000818152600e6020526040808220805460ff191660011790555191925082917f2510041334ede909998b9aefcca4300fc36c670dd00e1f7f0afffaca56adcb399190a2505050565b6002546001600160a01b03168061125657604051630bd7619560e41b815260040160405180910390fd5b6001600160a01b0383166112735761126e818361205b565b611288565b61127d838361217e565b6112888382846122cf565b604080518381526001600160a01b0385166020820152339181019190915284907ff90d3aafcedf55a0da208dd26d915e0ce1870ee9221586012487a0b366106f6590606001610758565b7fc8fcad8db84d3cc18b4c41d551ea0ee66dd599cde068d998e57d5e09332c1320546001600160a01b031690565b6000816040516020016113139190614c82565b604051602081830303815290604052805190602001209050919050565b60408051808201909152600080825260208201526000806113508461230e565b9050600061136682602001518360000151611767565b90506113718161232d565b60020154600160a01b900460ff1661139c5760405163a13f958f60e01b815260040160405180910390fd5b9094909350915050565b336000908152600b6020526040812054819060ff161580156113f457506113cd838061478b565b6113de9060a08101906080016141f1565b6001600160a01b0316336001600160a01b031614155b1561141257604051637c32a2b360e01b815260040160405180910390fd5b60045463ffffffff16611425848061478b565b6114369060408101906020016141b4565b63ffffffff161461145a5760405163b6bb322560e01b815260040160405180910390fd5b60006114696020850185614c95565b91506000905061148561147c868061478b565b61070b90614696565b60008181526008602052604090205490915060ff16821561172a57600c548311156114c3576040516313c06ef360e01b815260040160405180910390fd5b60008160038111156114d7576114d76144f9565b146114f557604051630caaeb0b60e11b815260040160405180910390fd5b601f60006115096080890160608a016141f1565b6001600160a01b0316815260208101919091526040016000205460ff1661154357604051631fa09b5360e31b815260040160405180910390fd5b611590826115546020890189614c95565b60405160200161156693929190614cde565b60408051601f19818403018152919052805160209091012061158b6080890189614cf8565b61236e565b6001600160a01b03166115a960808801606089016141f1565b6001600160a01b0316146115d057604051638579eca560e01b815260040160405180910390fd5b6040805160208082018590528183018690528251808303840181526060909201909252805191012060005b848110156117235761160b6123c0565b15801561166857506015600061162460208b018b614c95565b8481811061163457611634614d3e565b905060200201602081019061164991906141f1565b6001600160a01b0316815260208101919091526040016000205460ff16155b1561168657604051630e1eb7f760e31b815260040160405180910390fd5b6116b98261169760408b018b614c95565b848181106116a7576116a7614d3e565b905060200281019061158b9190614cf8565b6001600160a01b03166116cf60208a018a614c95565b838181106116df576116df614d3e565b90506020020160208101906116f491906141f1565b6001600160a01b03161461171b57604051633a1dd23360e21b815260040160405180910390fd5b6001016115fb565b505061175c565b600181600381111561173e5761173e6144f9565b1461175c57604051636320d2cf60e11b815260040160405180910390fd5b909590945092505050565b6000828260405160200161178b92919091825263ffffffff16602082015260400190565b60405160208183030381529060405280519060200120905092915050565b600080806117ba6020850185614c95565b60008981526009602052604090206117d392909161411f565b5060006117e0858061478b565b6117f19060608101906040016141b4565b63ffffffff16156118325761182f8761180a878061478b565b6101800135611819888061478b565b61182a9060608101906040016141b4565b6123e7565b90505b61183c858061478b565b610120013560000361185657600093509150819050611d2c565b6000611862868061478b565b6118739060c081019060a001614d54565b8061188c57506000898152600e602052604090205460ff165b9050600061189a878061478b565b610120013590508715611c3a5760006118b66020890189614c95565b91506118da90506118c7898061478b565b61012001356000600101546127106123f6565b915080600103611acb5782158015611951575081600a60006118ff60208c018c614c95565b600081811061191057611910614d3e565b905060200201602081019061192591906141f1565b6001600160a01b0390811682526020808301939093526040918201600090812091891681529252902054105b80156119675750601b546001600160a01b031615155b15611a36576015600061197d60208b018b614c95565b600081811061198e5761198e614d3e565b90506020020160208101906119a391906141f1565b6001600160a01b03168152602081019190915260400160002054610100900460ff166119e2576040516310c4e50960e01b815260040160405180910390fd5b600080611a238d8d866119f860208f018f614c95565b6000818110611a0957611a09614d3e565b9050602002016020810190611a1e91906141f1565b61240d565b9099509750949550611d2c945050505050565b81600a6000611a4860208c018c614c95565b6000818110611a5957611a59614d3e565b9050602002016020810190611a6e91906141f1565b6001600160a01b03166001600160a01b031681526020019081526020016000206000866001600160a01b03166001600160a01b031681526020019081526020016000206000828254611ac09190614d87565b90915550611c389050565b6000611ad78284614db0565b905060005b611ae7600184614d87565b811015611b845781600a6000611b0060208e018e614c95565b85818110611b1057611b10614d3e565b9050602002016020810190611b2591906141f1565b6001600160a01b03166001600160a01b031681526020019081526020016000206000886001600160a01b03166001600160a01b031681526020019081526020016000206000828254611b779190614d87565b9091555050600101611adc565b506000611b918385614dc4565b611b9b9083614dd8565b905080600a6000611baf60208e018e614c95565b611bba600189614d87565b818110611bc957611bc9614d3e565b9050602002016020810190611bde91906141f1565b6001600160a01b03166001600160a01b031681526020019081526020016000206000886001600160a01b03166001600160a01b031681526020019081526020016000206000828254611c309190614d87565b909155505050505b505b611c44878061478b565b611c559060608101906040016141b4565b60045463ffffffff9081169116148015611c7b57506000611c758a61232d565b60030154115b15611ca75760008981526007602052604081206004018054839290611ca1908490614d87565b90915550505b8115611cd25760008a8152600e60205260409020805460ff191690559450909250829150611d2c9050565b60008a8152600d602052604081208054908290559080611d1e8c8786868503611d0857611cff8e8061478b565b60e00135611d0a565b865b611d148f8061478b565b61014001356125cc565b909950975094955050505050505b9450945094915050565b6000611d5d84611d46888061478b565b611d579060808101906060016141f1565b876122cf565b611d7383868685611d6e8b8061478b565b612651565b50835b95945050505050565b601a54600090600160a01b900460ff1615611dad57604051633ee5b89360e01b815260040160405180910390fd5b82516000906001600160a01b0316158015611dcb5750602084015115155b15611de95760405163ae715ad360e01b815260040160405180910390fd5b611df685602001516127c5565b60608601519091506001600160a01b0316611e235760405162845fdd60e41b815260040160405180910390fd5b6127108560e001511115611e4a5760405163388d723160e11b815260040160405180910390fd5b6040805180820190915260008082526020820181905290819086516000906001600160a01b031615611ff1576000611e858960000151611330565b90935090506000611e958261232d565b84518c5163ffffffff9081169116149350905082611ebd5780546001600160a01b0316611ec3565b60208401515b95506001600160a01b038616611eec5760405163965a46c160e01b815260040160405180910390fd5b6003810154838015611efe5750600081115b15611f4f5760008b602001518360040154611f199190614dd8565b905081811115611f3c576040516348ba8bf160e01b815260040160405180910390fd5b6000848152600760205260409020600401555b50835163ffffffff1660408c01526020808501516101808d01528a015115611fee57611f838a600001518b6020015161217e565b611f9c828b60000151888d602001518f60e001516127fe565b6101208c01528951611fe7906001600160a01b03888116911614611fce576001820154600160a01b900460ff16611fdb565b8154600160a01b900460ff165b60128c60200151612881565b6101408c01525b50505b6003805490600061200183614deb565b909155506101608a015261201489611300565b60208801519093501561203457612034838860000151896020015161122c565b61204c838a8a600001518b6020015189878a886128ec565b509093505050505b9392505050565b804710156120b05760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a20696e73756666696369656e742062616c616e636500000060448201526064015b60405180910390fd5b6000826001600160a01b03168260405160006040518083038185875af1925050503d80600081146120fd576040519150601f19603f3d011682016040523d82523d6000602084013e612102565b606091505b50509050806121795760405162461bcd60e51b815260206004820152603a60248201527f416464726573733a20756e61626c6520746f2073656e642076616c75652c207260448201527f6563697069656e74206d6179206861766520726576657274656400000000000060648201526084016120a7565b505050565b8060000361218a575050565b6001600160a01b0382166121b157604051632a38b13360e01b815260040160405180910390fd5b6040516370a0823160e01b815230600482015282906000906001600160a01b038316906370a0823190602401602060405180830381865afa1580156121fa573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061221e9190614e04565b90506122356001600160a01b038316333086612ad3565b6040516370a0823160e01b8152306004820152839082906001600160a01b038516906370a0823190602401602060405180830381865afa15801561227d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122a19190614e04565b6122ab9190614d87565b146122c957604051630e40773560e21b815260040160405180910390fd5b50505050565b806000036122dc57505050565b6001600160a01b03831661230357604051633a48ca7b60e11b815260040160405180910390fd5b612179838383612b3e565b604080518082019091526000808252602082015261040c826000612b6e565b6000818152600760205260408120600180820154839291600160a01b90910460ff1610156120545760405163618cca3f60e11b815260040160405180910390fd5b60006123b861237c85612c66565b84848080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250612ca192505050565b949350505050565b6000806123cb6112d2565b6001600160a01b031614806123e2575060125460ff165b905090565b60006123b88484846000612cc5565b6000816124038486614e1d565b6123b89190614db0565b600080600061241b86612d00565b601b546040516369a933a560e01b81526001600160a01b03808416600483015260248201899052306044830152600060648301529293509116906369a933a590608401600060405180830381600087803b15801561247857600080fd5b505af115801561248c573d6000803e3d6000fd5b5050601b54604051631a4ca37b60e21b81526001600160a01b038581166004830152602482018a90523060448301526000945090911691506369328dec906064016020604051808303816000875af11580156124ec573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125109190614e04565b9050858110156125335760405163407559a360e11b815260040160405180910390fd5b6000888152601d60205260409020869055601c5461271090612556908890614e1d565b6125609190614db0565b6000898152601e60209081526040918290209290925580516001600160a01b038581168252928101899052918716918a917f2d3ba0fa5be2ef8cb1ec8920a07a6cbccc2397b18ca3e70f48ea695500b8f218910160405180910390a35084925090505b94509492505050565b60008060006125da8861232d565b60018101549091506001600160a01b0390811690881681036126025786935091506126479050565b866000036126165786935091506126479050565b6126408989838a61263b60128860010160149054906101000a900460ff168c8e612d39565b612d85565b9350935050505b9550959350505050565b7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47061267f60c0830183614cf8565b60405161268d929190614e34565b604051809103902003156127be576000806127546126b160808501606086016141f1565b6127105a6126bf9190614d87565b600061010063fd614f4160e01b8c8c8c8c6126db5760006126ed565b6126ed6101208d016101008e016141f1565b6126fa60208e018e6141b4565b61270760c08f018f614cf8565b60405160240161271d9796959493929190614e44565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152612ec4565b9150915083158015612764575081155b15612781576040516264cdd360e41b815260040160405180910390fd5b867fb1a4ab59facaedd6d3a71da3902e0a1fa5b99750c0e20cd878334378a41cb33583836040516127b3929190614e97565b60405180910390a250505b5050505050565b63ffffffff81166000908152600f6020526040902054806127f957604051630c3bcd4960e41b815260040160405180910390fd5b919050565b60008260000361281057506000611d76565b846001600160a01b0316846001600160a01b031603612830575081611d76565b600061283b8761232d565b905060006128748888888861263b8760010160149054906101000a900460ff168860000160149054906101000a900460ff168c8c612d39565b5098975050505050505050565b60008260ff168460ff1603612897575080612054565b60008360ff168560ff1610156128cd576128b18585614eb2565b6128bc90600a614faf565b6128c69084614e1d565b90506123b8565b6128d78486614eb2565b6128e290600a614faf565b611d769084614db0565b610120870151801561295e578161295e57604051632770a7eb60e21b8152306004820152602481018290526001600160a01b03841690639dc29fac90604401600060405180830381600087803b15801561294557600080fd5b505af1158015612959573d6000803e3d6000fd5b505050505b6000846000015185602001516003848d604051602001612982959493929190614fbe565b60408051601f1981840301815282825260208054639fa92f9d60e01b8552925191945060009384936001600160a01b031692639fa92f9d926004808401939192918290030181865afa1580156129dc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a009190614ffd565b6001600160a01b031663fa31de018c602001518a866040518463ffffffff1660e01b8152600401612a339392919061501a565b6000604051808303816000875af1158015612a52573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052612a7a919081019061503f565b91509150818b61016001518d7fed8e6ba697dd65259e5ce532ac08ff06d1a3607bcec58f8f0937fe36a5666c548e8e8e8c88604051612abd9594939291906150c1565b60405180910390a4505050505050505050505050565b6040516001600160a01b03808516602483015283166044820152606481018290526122c99085906323b872dd60e01b906084015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152612f4e565b6040516001600160a01b03831660248201526044810182905261217990849063a9059cbb60e01b90606401612b07565b604080518082019091526000808252602082015260408051808201909152600080825260208201526001600160a01b038416612bab57905061040c565b506001600160a01b03831660009081526005830160209081526040918290208251808401909352805463ffffffff168084526001909101549183019190915215612bf657905061040c565b612c008484613020565b15612c2657600483015463ffffffff1681526001600160a01b0384166020820152612054565b506001600160a01b03831660009081526006830160209081526040918290208251808401909352805463ffffffff16835260010154908201529392505050565b6040517f19457468657265756d205369676e6564204d6573736167653a0a3332000000006020820152603c8101829052600090605c01611313565b6000806000612cb0858561305f565b91509150612cbd816130a4565b509392505050565b600481015460009063ffffffff90811690841603612ce45750826123b8565b612ced8561232d565b546001600160a01b031695945050505050565b600080612d0c8361232d565b600101546001600160a01b031690508061040c57604051630558a50760e31b815260040160405180910390fd5b600082600003612d4b575060006123b8565b6000612710612d5a8482614d87565b612d649086614e1d565b612d6e9190614db0565b9050612d7b868683612881565b9695505050505050565b600085815260186020526040812081908190612da0816131f1565b15612dd257612dc6612db28a8a61320e565b612dbc8b8a61320e565b839190898961328e565b87935093505050612647565b6000612ddd8a61232d565b600201546001600160a01b0390811691508990612dfe908216836000613597565b612e126001600160a01b038216838a6136ac565b6001600160a01b03821663d460f0a2898c8c8b612e31610e1042614dd8565b6040516001600160e01b031960e088901b16815260048101959095526001600160a01b0393841660248601529290911660448401526064830152608482015260a4016020604051808303816000875af1158015612e92573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612eb69190614e04565b899550955050505050612647565b6000606060008060008661ffff166001600160401b03811115612ee957612ee9614591565b6040519080825280601f01601f191660200182016040528015612f13576020820181803683370190505b5090506000808751602089018b8e8ef191503d925086831115612f34578692505b828152826000602083013e90999098509650505050505050565b6000612fa3826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b031661375e9092919063ffffffff16565b8051909150156121795780806020019051810190612fc19190615110565b6121795760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b60648201526084016120a7565b6001600160a01b038216600090815260068201602052604081205463ffffffff161561304e5750600061040c565b50506001600160a01b03163b151590565b60008082516041036130955760208301516040840151606085015160001a6130898782858561376d565b9450945050505061309d565b506000905060025b9250929050565b60008160048111156130b8576130b86144f9565b036130c05750565b60018160048111156130d4576130d46144f9565b036131215760405162461bcd60e51b815260206004820152601860248201527f45434453413a20696e76616c6964207369676e6174757265000000000000000060448201526064016120a7565b6002816004811115613135576131356144f9565b036131825760405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e6774680060448201526064016120a7565b6003816004811115613196576131966144f9565b036131ee5760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c604482015261756560f01b60648201526084016120a7565b50565b600c81015460009060ff1615801561040c57505060080154151590565b60008281526019602090815260408083206001600160a01b0385168085529083528184205486855260189093529083206008018054849360ff169291908390811061325b5761325b614d3e565b6000918252602090912001546001600160a01b0316146123b85760405163054e442960e41b815260040160405180910390fd5b600c85015460009060ff16156132d65760405162461bcd60e51b815260206004820152600d60248201526c191a5cd8589b1959081c1bdbdb609a1b60448201526064016120a7565b85600a018560ff16815481106132ee576132ee614d3e565b90600052602060002001548311156133415760405162461bcd60e51b81526020600482015260166024820152756d6f7265207468616e20706f6f6c2062616c616e636560501b60448201526064016120a7565b600080600088600a0180548060200260200160405190810160405280929190818152602001828054801561339457602002820191906000526020600020905b815481526020019060010190808311613380575b505050505090506133a8898989898561382e565b9093509150848310156133ea5760405162461bcd60e51b815260206004820152600a6024820152696479203c206d696e447960b01b60448201526064016120a7565b6000896009018860ff168154811061340457613404614d3e565b90600052602060002001546402540be4008b60060154856134259190614e1d565b61342f9190614db0565b6134399190614db0565b905086828a60ff168151811061345157613451614d3e565b60200260200101516134639190614dd8565b8a600a018a60ff168154811061347b5761347b614d3e565b90600052602060002001819055508084838a60ff16815181106134a0576134a0614d3e565b60200260200101516134b29190614d87565b6134bc9190614d87565b8a600a018960ff16815481106134d4576134d4614d3e565b600091825260209091200155801561353957808a600b018960ff16815481106134ff576134ff614d3e565b90600052602060002001546135149190614dd8565b8a600b018960ff168154811061352c5761352c614d3e565b6000918252602090912001555b8954604080518981526020810187905260ff8c8116828401528b16606082015290513392917f28d4cf2d5709da3b474b5f05cfd7083faffd601f9500d1f8439b8a13ec7df320919081900360800190a3509198975050505050505050565b8015806136115750604051636eb1769f60e11b81523060048201526001600160a01b03838116602483015284169063dd62ed3e90604401602060405180830381865afa1580156135eb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061360f9190614e04565b155b61367c5760405162461bcd60e51b815260206004820152603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527520746f206e6f6e2d7a65726f20616c6c6f77616e636560501b60648201526084016120a7565b6040516001600160a01b03831660248201526044810182905261217990849063095ea7b360e01b90606401612b07565b604051636eb1769f60e11b81523060048201526001600160a01b038381166024830152600091839186169063dd62ed3e90604401602060405180830381865afa1580156136fd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137219190614e04565b61372b9190614dd8565b6040516001600160a01b0385166024820152604481018290529091506122c990859063095ea7b360e01b90606401612b07565b60606123b884846000856139f4565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a08311156137a457506000905060036125c3565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa1580156137f8573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b038116613821576000600192509250506125c3565b9660009650945050505050565b60008060008760090180548060200260200160405190810160405280929190818152602001828054801561388157602002820191906000526020600020905b81548152602001906001019080831161386d575b5050505050905060006138948583613acf565b905080518860ff161080156138ac575080518760ff16105b6138ed5760405162461bcd60e51b8152602060048201526012602482015271696e646578206f7574206f662072616e676560701b60448201526064016120a7565b6000818960ff168151811061390457613904614d3e565b6020026020010151838a60ff168151811061392157613921614d3e565b6020026020010151886139349190614e1d565b61393e9190614dd8565b9050600061395761394e8c613bd9565b8b8b8587613be4565b9050600181848b60ff168151811061397157613971614d3e565b60200260200101516139839190614d87565b61398d9190614d87565b95506402540be4008b60050154876139a59190614e1d565b6139af9190614db0565b9450838960ff16815181106139c6576139c6614d3e565b602002602001015185876139da9190614d87565b6139e49190614db0565b9550505050509550959350505050565b606082471015613a555760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b60648201526084016120a7565b600080866001600160a01b03168587604051613a71919061512d565b60006040518083038185875af1925050503d8060008114613aae576040519150601f19603f3d011682016040523d82523d6000602084013e613ab3565b606091505b5091509150613ac487838387613e4b565b979650505050505050565b81518151606091908114613b1c5760405162461bcd60e51b81526020600482015260146024820152736d69736d61746368206d756c7469706c6965727360601b60448201526064016120a7565b6000816001600160401b03811115613b3657613b36614591565b604051908082528060200260200182016040528015613b5f578160200160208202803683370190505b50905060005b82811015613bd057848181518110613b7f57613b7f614d3e565b6020026020010151868281518110613b9957613b99614d3e565b6020026020010151613bab9190614e1d565b828281518110613bbd57613bbd614d3e565b6020908102919091010152600101613b65565b50949350505050565b600061040c82613ec4565b805160009060ff80861690871603613c3e5760405162461bcd60e51b815260206004820152601760248201527f636f6d7061726520746f6b656e20746f20697473656c6600000000000000000060448201526064016120a7565b808660ff16108015613c525750808560ff16105b613c905760405162461bcd60e51b815260206004820152600f60248201526e1d1bdad95b881b9bdd08199bdd5b99608a1b60448201526064016120a7565b6000613c9c8489613f0d565b905080600080613cac8b86614e1d565b90506000805b86811015613d33578b60ff168103613ccc57899150613cff565b8a60ff168114613cf757888181518110613ce857613ce8614d3e565b60200260200101519150613cff565b600101613cb2565b613d098285614dd8565b9350613d158783614e1d565b613d1f8787614e1d565b613d299190614db0565b9450600101613cb2565b50613d3e8683614e1d565b6064613d4a8787614e1d565b613d549190614e1d565b613d5e9190614db0565b9350600082613d6e606488614e1d565b613d789190614db0565b613d829085614dd8565b9050600086815b610100811015613e02578192508884836002613da59190614e1d565b613daf9190614dd8565b613db99190614d87565b88613dc48480614e1d565b613dce9190614dd8565b613dd89190614db0565b9150613de482846140ba565b15613dfa57509850611d76975050505050505050565b600101613d89565b5060405162461bcd60e51b815260206004820152601e60248201527f417070726f78696d6174696f6e20646964206e6f7420636f6e7665726765000060448201526064016120a7565b60608315613eba578251600003613eb3576001600160a01b0385163b613eb35760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016120a7565b50816123b8565b6123b883836140d0565b600481015460028201546001830154909190808314801590613ee557508142105b15613f06576000846003015490508083038142038502428503840201049350505b5050919050565b815160009081805b82811015613f4c57858181518110613f2f57613f2f614d3e565b602002602001015182613f429190614dd8565b9150600101613f15565b5080600003613f605760009250505061040c565b60008181613f6e8588614e1d565b905060005b61010081101561407b578260005b87811015613fcc57878b8281518110613f9c57613f9c614d3e565b6020026020010151613fae9190614e1d565b613fb88684614e1d565b613fc29190614db0565b9150600101613f81565b5083945080876001613fde9190614dd8565b613fe89190614e1d565b606485613ff58287614d87565b613fff9190614e1d565b6140099190614db0565b6140139190614dd8565b8461401e8984614e1d565b606461402a8a88614e1d565b6140349190614db0565b61403e9190614dd8565b6140489190614e1d565b6140529190614db0565b935061405e84866140ba565b15614072578397505050505050505061040c565b50600101613f73565b5060405162461bcd60e51b81526020600482015260136024820152724420646f6573206e6f7420636f6e766572676560681b60448201526064016120a7565b600060026140c884846140fa565b109392505050565b8151156140e05781518083602001fd5b8060405162461bcd60e51b81526004016120a7919061513f565b6000818311156141155761410e8284614d87565b905061040c565b6120548383614d87565b828054828255906000526020600020908101928215614172579160200282015b828111156141725781546001600160a01b0319166001600160a01b0384351617825560209092019160019091019061413f565b5061417e929150614182565b5090565b5b8082111561417e5760008155600101614183565b63ffffffff811681146131ee57600080fd5b80356127f981614197565b6000602082840312156141c657600080fd5b813561205481614197565b6001600160a01b03811681146131ee57600080fd5b80356127f9816141d1565b60006020828403121561420357600080fd5b8135612054816141d1565b60006020828403121561422057600080fd5b5035919050565b6020808252825182820181905260009190848201906040850190845b818110156142685783516001600160a01b031683529284019291840191600101614243565b50909695505050505050565b60006101a0828403121561428757600080fd5b50919050565b600080604083850312156142a057600080fd5b82356001600160401b038111156142b657600080fd5b6142c285828601614274565b95602094909401359450505050565b6000806000606084860312156142e657600080fd5b8335925060208401356142f8816141d1565b929592945050506040919091013590565b60006020828403121561431b57600080fd5b81356001600160401b0381111561433157600080fd5b820160a0818503121561205457600080fd5b60008083601f84011261435557600080fd5b5081356001600160401b0381111561436c57600080fd5b60208301915083602082850101111561309d57600080fd5b60008060008060008060008060006101008a8c0312156143a357600080fd5b89356143ae81614197565b985060208a01356143be816141d1565b975060408a01356143ce816141d1565b965060608a01356143de816141d1565b955060808a0135945060a08a0135935060c08a01356001600160401b0381111561440757600080fd5b6144138c828d01614343565b9a9d999c50979a9699959894979660e00135949350505050565b60008060008060008060008060e0898b03121561444957600080fd5b883561445481614197565b97506020890135614464816141d1565b96506040890135614474816141d1565b95506060890135614484816141d1565b94506080890135935060a0890135925060c08901356001600160401b038111156144ad57600080fd5b6144b98b828c01614343565b999c989b5096995094979396929594505050565b600080604083850312156144e057600080fd5b82356144eb81614197565b946020939093013593505050565b634e487b7160e01b600052602160045260246000fd5b600481106131ee57634e487b7160e01b600052602160045260246000fd5b6020810161453a8361450f565b91905290565b60006020828403121561455257600080fd5b81356001600160401b0381111561456857600080fd5b6123b884828501614274565b60006020828403121561458657600080fd5b815161205481614197565b634e487b7160e01b600052604160045260246000fd5b6040516101a081016001600160401b03811182821017156145ca576145ca614591565b60405290565b604051601f8201601f191681016001600160401b03811182821017156145f8576145f8614591565b604052919050565b80151581146131ee57600080fd5b80356127f981614600565b60006001600160401b0382111561463257614632614591565b50601f01601f191660200190565b600082601f83011261465157600080fd5b813561466461465f82614619565b6145d0565b81815284602083860101111561467957600080fd5b816020850160208301376000918101602001919091529392505050565b60006101a082360312156146a957600080fd5b6146b16145a7565b6146ba836141a9565b81526146c8602084016141a9565b60208201526146d9604084016141a9565b60408201526146ea606084016141e6565b60608201526146fb608084016141e6565b608082015261470c60a0840161460e565b60a082015260c08301356001600160401b0381111561472a57600080fd5b61473636828601614640565b60c08301525060e083013560e08201526101006147548185016141e6565b9082015261012083810135908201526101408084013590820152610160808401359082015261018092830135928101929092525090565b6000823561019e198336030181126147a257600080fd5b9190910192915050565b6000808335601e198436030181126147c357600080fd5b83016020810192503590506001600160401b038111156147e257600080fd5b80360382131561309d57600080fd5b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b6000808335601e1984360301811261483157600080fd5b83016020810192503590506001600160401b0381111561485057600080fd5b8060051b360382131561309d57600080fd5b8183526000602080850194508260005b858110156148a0578135614885816141d1565b6001600160a01b031687529582019590820190600101614872565b509495945050505050565b81835260006020808501808196508560051b810191508460005b878110156148fb5782840389526148dc82886147ac565b6148e78682846147f1565b9a87019a95505050908401906001016148c5565b5091979650505050505050565b608081526000610120863561019e1988360301811261492657600080fd5b60a06080850152870161494882850161493e836141a9565b63ffffffff169052565b614954602082016141a9565b6101406149688187018363ffffffff169052565b614974604084016141a9565b915061016061498a8188018463ffffffff169052565b614996606085016141e6565b92506101806149af818901856001600160a01b03169052565b6149bb608086016141e6565b93506101a06149d4818a01866001600160a01b03169052565b6149e060a0870161460e565b8015156101c08b015294506149f860c08701876147ac565b9550816101e08b0152614a106102c08b0187836147f1565b9550505060e0850135610200890152610100614a2d8187016141e6565b6001600160a01b03166102208a0152858701356102408a0152838601356102608a0152828601356102808a0152818601356102a08a0152614a7160208e018e61481a565b97509550607f199350838986030160a08a0152614a8f858888614862565b9650614a9e60408e018e61481a565b96509450838988030160c08a0152614ab78787876148ab565b9650614ac560608e016141e6565b6001600160a01b03811660e08b01529550614ae360808e018e6147ac565b965094508389880301818a015250505050614aff8383836147f1565b9350505050614b1960208301866001600160a01b03169052565b836040830152611d7660608301846001600160a01b03169052565b60005b83811015614b4f578181015183820152602001614b37565b50506000910152565b60008151808452614b70816020860160208601614b34565b601f01601f19169290920160200192915050565b805163ffffffff16825260006101a06020830151614baa602086018263ffffffff169052565b506040830151614bc2604086018263ffffffff169052565b506060830151614bdd60608601826001600160a01b03169052565b506080830151614bf860808601826001600160a01b03169052565b5060a0830151614c0c60a086018215159052565b5060c08301518160c0860152614c2482860182614b58565b91505060e083015160e085015261010080840151614c4c828701826001600160a01b03169052565b50506101208381015190850152610140808401519085015261016080840151908501526101809283015192909301919091525090565b6020815260006120546020830184614b84565b6000808335601e19843603018112614cac57600080fd5b8301803591506001600160401b03821115614cc657600080fd5b6020019150600581901b360382131561309d57600080fd5b838152604060208201526000611d76604083018486614862565b6000808335601e19843603018112614d0f57600080fd5b8301803591506001600160401b03821115614d2957600080fd5b60200191503681900382131561309d57600080fd5b634e487b7160e01b600052603260045260246000fd5b600060208284031215614d6657600080fd5b813561205481614600565b634e487b7160e01b600052601160045260246000fd5b8181038181111561040c5761040c614d71565b634e487b7160e01b600052601260045260246000fd5b600082614dbf57614dbf614d9a565b500490565b600082614dd357614dd3614d9a565b500690565b8082018082111561040c5761040c614d71565b600060018201614dfd57614dfd614d71565b5060010190565b600060208284031215614e1657600080fd5b5051919050565b808202811582820484141761040c5761040c614d71565b8183823760009101908152919050565b878152602081018790526001600160a01b0386811660408301528516606082015263ffffffff8416608082015260c060a08201819052600090614e8a90830184866147f1565b9998505050505050505050565b82151581526040602082015260006123b86040830184614b58565b60ff828116828216039081111561040c5761040c614d71565b600181815b80851115614f06578160001904821115614eec57614eec614d71565b80851615614ef957918102915b93841c9390800290614ed0565b509250929050565b600082614f1d5750600161040c565b81614f2a5750600061040c565b8160018114614f405760028114614f4a57614f66565b600191505061040c565b60ff841115614f5b57614f5b614d71565b50506001821b61040c565b5060208310610133831016604e8410600b8410161715614f89575081810a61040c565b614f938383614ecb565b8060001904821115614fa757614fa7614d71565b029392505050565b600061205460ff841683614f0e565b63ffffffff60e01b8660e01b168152846004820152614fdc8461450f565b60f89390931b60248401526025830191909152604582015260650192915050565b60006020828403121561500f57600080fd5b8151612054816141d1565b63ffffffff84168152826020820152606060408201526000611d766060830184614b58565b6000806040838503121561505257600080fd5b8251915060208301516001600160401b0381111561506f57600080fd5b8301601f8101851361508057600080fd5b805161508e61465f82614619565b8181528660208385010111156150a357600080fd5b6150b4826020830160208601614b34565b8093505050509250929050565b60a0815260006150d460a0830188614b84565b6001600160a01b038781166020850152604084018790528516606084015282810360808401526151048185614b58565b98975050505050505050565b60006020828403121561512257600080fd5b815161205481614600565b600082516147a2818460208701614b34565b6020815260006120546020830184614b5856fea264697066735822122083a85792fc0e29cd905f805175da0fd17a6a6073b7d3197f199a713d8d3885bf64736f6c63430008110033

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
[ 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.