creation_sourcecode: // SPDX-License-Identifier: MIT
  pragma solidity ^0.8.17;
  
  /**
   * @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);
  }
  /**
   * @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);
  }
  /**
   * @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);
          }
      }
  }
  
  /**
   * @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");
          }
      }
  }
  
  pragma experimental ABIEncoderV2;
  
  library LzLib {
      // LayerZero communication
      struct CallParams {
          address payable refundAddress;
          address zroPaymentAddress;
      }
  
      //---------------------------------------------------------------------------
      // Address type handling
  
      struct AirdropParams {
          uint airdropAmount;
          bytes32 airdropAddress;
      }
  
      function buildAdapterParams(LzLib.AirdropParams memory _airdropParams, uint _uaGasLimit) internal pure returns (bytes memory adapterParams) {
          if (_airdropParams.airdropAmount == 0 && _airdropParams.airdropAddress == bytes32(0x0)) {
              adapterParams = buildDefaultAdapterParams(_uaGasLimit);
          } else {
              adapterParams = buildAirdropAdapterParams(_uaGasLimit, _airdropParams);
          }
      }
  
      // Build Adapter Params
      function buildDefaultAdapterParams(uint _uaGas) internal pure returns (bytes memory) {
          // txType 1
          // bytes  [2       32      ]
          // fields [txType  extraGas]
          return abi.encodePacked(uint16(1), _uaGas);
      }
  
      function buildAirdropAdapterParams(uint _uaGas, AirdropParams memory _params) internal pure returns (bytes memory) {
          require(_params.airdropAmount > 0, "Airdrop amount must be greater than 0");
          require(_params.airdropAddress != bytes32(0x0), "Airdrop address must be set");
  
          // txType 2
          // bytes  [2       32        32            bytes[]         ]
          // fields [txType  extraGas  dstNativeAmt  dstNativeAddress]
          return abi.encodePacked(uint16(2), _uaGas, _params.airdropAmount, _params.airdropAddress);
      }
  
      function getGasLimit(bytes memory _adapterParams) internal pure returns (uint gasLimit) {
          require(_adapterParams.length == 34 || _adapterParams.length > 66, "Invalid adapterParams");
          assembly {
              gasLimit := mload(add(_adapterParams, 34))
          }
      }
  
      // Decode Adapter Params
      function decodeAdapterParams(bytes memory _adapterParams) internal pure returns (uint16 txType, uint uaGas, uint airdropAmount, address payable airdropAddress) {
          require(_adapterParams.length == 34 || _adapterParams.length > 66, "Invalid adapterParams");
          assembly {
              txType := mload(add(_adapterParams, 2))
              uaGas := mload(add(_adapterParams, 34))
          }
          require(txType == 1 || txType == 2, "Unsupported txType");
          require(uaGas > 0, "Gas too low");
  
          if (txType == 2) {
              assembly {
                  airdropAmount := mload(add(_adapterParams, 66))
                  airdropAddress := mload(add(_adapterParams, 86))
              }
          }
      }
  
      //---------------------------------------------------------------------------
      // Address type handling
      function bytes32ToAddress(bytes32 _bytes32Address) internal pure returns (address _address) {
          return address(uint160(uint(_bytes32Address)));
      }
  
      function addressToBytes32(address _address) internal pure returns (bytes32 _bytes32Address) {
          return bytes32(uint(uint160(_address)));
      }
  }
  
  /**
   * @dev Contract module that helps prevent reentrant calls to a function.
   *
   * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
   * available, which can be applied to functions to make sure there are no nested
   * (reentrant) calls to them.
   *
   * Note that because there is a single `nonReentrant` guard, functions marked as
   * `nonReentrant` may not call one another. This can be worked around by making
   * those functions `private`, and then adding `external` `nonReentrant` entry
   * points to them.
   *
   * TIP: If you would like to learn more about reentrancy and alternative ways
   * to protect against it, check out our blog post
   * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
   */
  abstract contract ReentrancyGuard {
      // Booleans are more expensive than uint256 or any type that takes up a full
      // word because each write operation emits an extra SLOAD to first read the
      // slot's contents, replace the bits taken up by the boolean, and then write
      // back. This is the compiler's defense against contract upgrades and
      // pointer aliasing, and it cannot be disabled.
  
      // The values being non-zero value makes deployment a bit more expensive,
      // but in exchange the refund on every call to nonReentrant will be lower in
      // amount. Since refunds are capped to a percentage of the total
      // transaction's gas, it is best to keep them low in cases like this one, to
      // increase the likelihood of the full refund coming into effect.
      uint256 private constant _NOT_ENTERED = 1;
      uint256 private constant _ENTERED = 2;
  
      uint256 private _status;
  
      constructor() {
          _status = _NOT_ENTERED;
      }
  
      /**
       * @dev Prevents a contract from calling itself, directly or indirectly.
       * Calling a `nonReentrant` function from another `nonReentrant`
       * function is not supported. It is possible to prevent this from happening
       * by making the `nonReentrant` function external, and making it call a
       * `private` function that does the actual work.
       */
      modifier nonReentrant() {
          _nonReentrantBefore();
          _;
          _nonReentrantAfter();
      }
  
      function _nonReentrantBefore() private {
          // On the first call to nonReentrant, _status will be _NOT_ENTERED
          require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
  
          // Any calls to nonReentrant after this point will fail
          _status = _ENTERED;
      }
  
      function _nonReentrantAfter() private {
          // By storing the original value once again, a refund is triggered (see
          // https://eips.ethereum.org/EIPS/eip-2200)
          _status = _NOT_ENTERED;
      }
  }
  /**
   * @dev Provides information about the current execution context, including the
   * sender of the transaction and its data. While these are generally available
   * via msg.sender and msg.data, they should not be accessed in such a direct
   * manner, since when dealing with meta-transactions the account sending and
   * paying for execution may not be the actual sender (as far as an application
   * is concerned).
   *
   * This contract is only required for intermediate, library-like contracts.
   */
  abstract contract Context {
      function _msgSender() internal view virtual returns (address) {
          return msg.sender;
      }
  
      function _msgData() internal view virtual returns (bytes calldata) {
          return msg.data;
      }
  }
  /**
   * @dev Contract module which provides a basic access control mechanism, where
   * there is an account (an owner) that can be granted exclusive access to
   * specific functions.
   *
   * By default, the owner account will be the one that deploys the contract. This
   * can later be changed with {transferOwnership}.
   *
   * This module is used through inheritance. It will make available the modifier
   * `onlyOwner`, which can be applied to your functions to restrict their use to
   * the owner.
   */
  abstract contract Ownable is Context {
      address private _owner;
  
      event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
  
      /**
       * @dev Initializes the contract setting the deployer as the initial owner.
       */
      constructor() {
          _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);
      }
  }
  interface ILayerZeroReceiver {
      // @notice LayerZero endpoint will invoke this function to deliver the message on the destination
      // @param _srcChainId - the source endpoint identifier
      // @param _srcAddress - the source sending contract address from the source chain
      // @param _nonce - the ordered message nonce
      // @param _payload - the signed payload is the UA bytes has encoded to be sent
      function lzReceive(uint16 _srcChainId, bytes calldata _srcAddress, uint64 _nonce, bytes calldata _payload) external;
  }
  interface ILayerZeroUserApplicationConfig {
      // @notice set the configuration of the LayerZero messaging library of the specified version
      // @param _version - messaging library version
      // @param _chainId - the chainId for the pending config change
      // @param _configType - type of configuration. every messaging library has its own convention.
      // @param _config - configuration in the bytes. can encode arbitrary content.
      function setConfig(uint16 _version, uint16 _chainId, uint _configType, bytes calldata _config) external;
  
      // @notice set the send() LayerZero messaging library version to _version
      // @param _version - new messaging library version
      function setSendVersion(uint16 _version) external;
  
      // @notice set the lzReceive() LayerZero messaging library version to _version
      // @param _version - new messaging library version
      function setReceiveVersion(uint16 _version) external;
  
      // @notice Only when the UA needs to resume the message flow in blocking mode and clear the stored payload
      // @param _srcChainId - the chainId of the source chain
      // @param _srcAddress - the contract address of the source contract at the source chain
      function forceResumeReceive(uint16 _srcChainId, bytes calldata _srcAddress) external;
  }
  interface ILayerZeroEndpoint is ILayerZeroUserApplicationConfig {
      // @notice send a LayerZero message to the specified address at a LayerZero endpoint.
      // @param _dstChainId - the destination chain identifier
      // @param _destination - the address on destination chain (in bytes). address length/format may vary by chains
      // @param _payload - a custom bytes payload to send to the destination contract
      // @param _refundAddress - if the source transaction is cheaper than the amount of value passed, refund the additional amount to this address
      // @param _zroPaymentAddress - the address of the ZRO token holder who would pay for the transaction
      // @param _adapterParams - parameters for custom functionality. e.g. receive airdropped native gas from the relayer on destination
      function send(uint16 _dstChainId, bytes calldata _destination, bytes calldata _payload, address payable _refundAddress, address _zroPaymentAddress, bytes calldata _adapterParams) external payable;
  
      // @notice used by the messaging library to publish verified payload
      // @param _srcChainId - the source chain identifier
      // @param _srcAddress - the source contract (as bytes) at the source chain
      // @param _dstAddress - the address on destination chain
      // @param _nonce - the unbound message ordering nonce
      // @param _gasLimit - the gas limit for external contract execution
      // @param _payload - verified payload to send to the destination contract
      function receivePayload(uint16 _srcChainId, bytes calldata _srcAddress, address _dstAddress, uint64 _nonce, uint _gasLimit, bytes calldata _payload) external;
  
      // @notice get the inboundNonce of a lzApp from a source chain which could be EVM or non-EVM chain
      // @param _srcChainId - the source chain identifier
      // @param _srcAddress - the source chain contract address
      function getInboundNonce(uint16 _srcChainId, bytes calldata _srcAddress) external view returns (uint64);
  
      // @notice get the outboundNonce from this source chain which, consequently, is always an EVM
      // @param _srcAddress - the source chain contract address
      function getOutboundNonce(uint16 _dstChainId, address _srcAddress) external view returns (uint64);
  
      // @notice gets a quote in source native gas, for the amount that send() requires to pay for message delivery
      // @param _dstChainId - the destination chain identifier
      // @param _userApplication - the user app address on this EVM chain
      // @param _payload - the custom message to send over LayerZero
      // @param _payInZRO - if false, user app pays the protocol fee in native token
      // @param _adapterParam - parameters for the adapter service, e.g. send some dust native token to dstChain
      function estimateFees(uint16 _dstChainId, address _userApplication, bytes calldata _payload, bool _payInZRO, bytes calldata _adapterParam) external view returns (uint nativeFee, uint zroFee);
  
      // @notice get this Endpoint's immutable source identifier
      function getChainId() external view returns (uint16);
  
      // @notice the interface to retry failed message on this Endpoint destination
      // @param _srcChainId - the source chain identifier
      // @param _srcAddress - the source chain contract address
      // @param _payload - the payload to be retried
      function retryPayload(uint16 _srcChainId, bytes calldata _srcAddress, bytes calldata _payload) external;
  
      // @notice query if any STORED payload (message blocking) at the endpoint.
      // @param _srcChainId - the source chain identifier
      // @param _srcAddress - the source chain contract address
      function hasStoredPayload(uint16 _srcChainId, bytes calldata _srcAddress) external view returns (bool);
  
      // @notice query if the _libraryAddress is valid for sending msgs.
      // @param _userApplication - the user app address on this EVM chain
      function getSendLibraryAddress(address _userApplication) external view returns (address);
  
      // @notice query if the _libraryAddress is valid for receiving msgs.
      // @param _userApplication - the user app address on this EVM chain
      function getReceiveLibraryAddress(address _userApplication) external view returns (address);
  
      // @notice query if the non-reentrancy guard for send() is on
      // @return true if the guard is on. false otherwise
      function isSendingPayload() external view returns (bool);
  
      // @notice query if the non-reentrancy guard for receive() is on
      // @return true if the guard is on. false otherwise
      function isReceivingPayload() external view returns (bool);
  
      // @notice get the configuration of the LayerZero messaging library of the specified version
      // @param _version - messaging library version
      // @param _chainId - the chainId for the pending config change
      // @param _userApplication - the contract address of the user application
      // @param _configType - type of configuration. every messaging library has its own convention.
      function getConfig(uint16 _version, uint16 _chainId, address _userApplication, uint _configType) external view returns (bytes memory);
  
      // @notice get the send() LayerZero messaging library version
      // @param _userApplication - the contract address of the user application
      function getSendVersion(address _userApplication) external view returns (uint16);
  
      // @notice get the lzReceive() LayerZero messaging library version
      // @param _userApplication - the contract address of the user application
      function getReceiveVersion(address _userApplication) external view returns (uint16);
  }
  library BytesLib {
      function concat(
          bytes memory _preBytes,
          bytes memory _postBytes
      )
      internal
      pure
      returns (bytes memory)
      {
          bytes memory tempBytes;
  
          assembly {
          // Get a location of some free memory and store it in tempBytes as
          // Solidity does for memory variables.
              tempBytes := mload(0x40)
  
          // Store the length of the first bytes array at the beginning of
          // the memory for tempBytes.
              let length := mload(_preBytes)
              mstore(tempBytes, length)
  
          // Maintain a memory counter for the current write location in the
          // temp bytes array by adding the 32 bytes for the array length to
          // the starting location.
              let mc := add(tempBytes, 0x20)
          // Stop copying when the memory counter reaches the length of the
          // first bytes array.
              let end := add(mc, length)
  
              for {
              // Initialize a copy counter to the start of the _preBytes data,
              // 32 bytes into its memory.
                  let cc := add(_preBytes, 0x20)
              } lt(mc, end) {
              // Increase both counters by 32 bytes each iteration.
                  mc := add(mc, 0x20)
                  cc := add(cc, 0x20)
              } {
              // Write the _preBytes data into the tempBytes memory 32 bytes
              // at a time.
                  mstore(mc, mload(cc))
              }
  
          // Add the length of _postBytes to the current length of tempBytes
          // and store it as the new length in the first 32 bytes of the
          // tempBytes memory.
              length := mload(_postBytes)
              mstore(tempBytes, add(length, mload(tempBytes)))
  
          // Move the memory counter back from a multiple of 0x20 to the
          // actual end of the _preBytes data.
              mc := end
          // Stop copying when the memory counter reaches the new combined
          // length of the arrays.
              end := add(mc, length)
  
              for {
                  let cc := add(_postBytes, 0x20)
              } lt(mc, end) {
                  mc := add(mc, 0x20)
                  cc := add(cc, 0x20)
              } {
                  mstore(mc, mload(cc))
              }
  
          // Update the free-memory pointer by padding our last write location
          // to 32 bytes: add 31 bytes to the end of tempBytes to move to the
          // next 32 byte block, then round down to the nearest multiple of
          // 32. If the sum of the length of the two arrays is zero then add
          // one before rounding down to leave a blank 32 bytes (the length block with 0).
              mstore(0x40, and(
              add(add(end, iszero(add(length, mload(_preBytes)))), 31),
              not(31) // Round down to the nearest 32 bytes.
              ))
          }
  
          return tempBytes;
      }
  
      function concatStorage(bytes storage _preBytes, bytes memory _postBytes) internal {
          assembly {
          // Read the first 32 bytes of _preBytes storage, which is the length
          // of the array. (We don't need to use the offset into the slot
          // because arrays use the entire slot.)
              let fslot := sload(_preBytes.slot)
          // Arrays of 31 bytes or less have an even value in their slot,
          // while longer arrays have an odd value. The actual length is
          // the slot divided by two for odd values, and the lowest order
          // byte divided by two for even values.
          // If the slot is even, bitwise and the slot with 255 and divide by
          // two to get the length. If the slot is odd, bitwise and the slot
          // with -1 and divide by two.
              let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2)
              let mlength := mload(_postBytes)
              let newlength := add(slength, mlength)
          // slength can contain both the length and contents of the array
          // if length < 32 bytes so let's prepare for that
          // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage
              switch add(lt(slength, 32), lt(newlength, 32))
              case 2 {
              // Since the new array still fits in the slot, we just need to
              // update the contents of the slot.
              // uint256(bytes_storage) = uint256(bytes_storage) + uint256(bytes_memory) + new_length
                  sstore(
                  _preBytes.slot,
                  // all the modifications to the slot are inside this
                  // next block
                  add(
                  // we can just add to the slot contents because the
                  // bytes we want to change are the LSBs
                  fslot,
                  add(
                  mul(
                  div(
                  // load the bytes from memory
                  mload(add(_postBytes, 0x20)),
                  // zero all bytes to the right
                  exp(0x100, sub(32, mlength))
                  ),
                  // and now shift left the number of bytes to
                  // leave space for the length in the slot
                  exp(0x100, sub(32, newlength))
                  ),
                  // increase length by the double of the memory
                  // bytes length
                  mul(mlength, 2)
                  )
                  )
                  )
              }
              case 1 {
              // The stored value fits in the slot, but the combined value
              // will exceed it.
              // get the keccak hash to get the contents of the array
                  mstore(0x0, _preBytes.slot)
                  let sc := add(keccak256(0x0, 0x20), div(slength, 32))
  
              // save new length
                  sstore(_preBytes.slot, add(mul(newlength, 2), 1))
  
              // The contents of the _postBytes array start 32 bytes into
              // the structure. Our first read should obtain the `submod`
              // bytes that can fit into the unused space in the last word
              // of the stored array. To get this, we read 32 bytes starting
              // from `submod`, so the data we read overlaps with the array
              // contents by `submod` bytes. Masking the lowest-order
              // `submod` bytes allows us to add that value directly to the
              // stored value.
  
                  let submod := sub(32, slength)
                  let mc := add(_postBytes, submod)
                  let end := add(_postBytes, mlength)
                  let mask := sub(exp(0x100, submod), 1)
  
                  sstore(
                  sc,
                  add(
                  and(
                  fslot,
                  0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00
                  ),
                  and(mload(mc), mask)
                  )
                  )
  
                  for {
                      mc := add(mc, 0x20)
                      sc := add(sc, 1)
                  } lt(mc, end) {
                      sc := add(sc, 1)
                      mc := add(mc, 0x20)
                  } {
                      sstore(sc, mload(mc))
                  }
  
                  mask := exp(0x100, sub(mc, end))
  
                  sstore(sc, mul(div(mload(mc), mask), mask))
              }
              default {
              // get the keccak hash to get the contents of the array
                  mstore(0x0, _preBytes.slot)
              // Start copying to the last used word of the stored array.
                  let sc := add(keccak256(0x0, 0x20), div(slength, 32))
  
              // save new length
                  sstore(_preBytes.slot, add(mul(newlength, 2), 1))
  
              // Copy over the first `submod` bytes of the new data as in
              // case 1 above.
                  let slengthmod := mod(slength, 32)
                  let mlengthmod := mod(mlength, 32)
                  let submod := sub(32, slengthmod)
                  let mc := add(_postBytes, submod)
                  let end := add(_postBytes, mlength)
                  let mask := sub(exp(0x100, submod), 1)
  
                  sstore(sc, add(sload(sc), and(mload(mc), mask)))
  
                  for {
                      sc := add(sc, 1)
                      mc := add(mc, 0x20)
                  } lt(mc, end) {
                      sc := add(sc, 1)
                      mc := add(mc, 0x20)
                  } {
                      sstore(sc, mload(mc))
                  }
  
                  mask := exp(0x100, sub(mc, end))
  
                  sstore(sc, mul(div(mload(mc), mask), mask))
              }
          }
      }
  
      function slice(
          bytes memory _bytes,
          uint256 _start,
          uint256 _length
      )
      internal
      pure
      returns (bytes memory)
      {
          require(_length + 31 >= _length, "slice_overflow");
          require(_bytes.length >= _start + _length, "slice_outOfBounds");
  
          bytes memory tempBytes;
  
          assembly {
              switch iszero(_length)
              case 0 {
              // Get a location of some free memory and store it in tempBytes as
              // Solidity does for memory variables.
                  tempBytes := mload(0x40)
  
              // The first word of the slice result is potentially a partial
              // word read from the original array. To read it, we calculate
              // the length of that partial word and start copying that many
              // bytes into the array. The first word we copy will start with
              // data we don't care about, but the last `lengthmod` bytes will
              // land at the beginning of the contents of the new array. When
              // we're done copying, we overwrite the full first word with
              // the actual length of the slice.
                  let lengthmod := and(_length, 31)
  
              // The multiplication in the next line is necessary
              // because when slicing multiples of 32 bytes (lengthmod == 0)
              // the following copy loop was copying the origin's length
              // and then ending prematurely not copying everything it should.
                  let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod)))
                  let end := add(mc, _length)
  
                  for {
                  // The multiplication in the next line has the same exact purpose
                  // as the one above.
                      let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start)
                  } lt(mc, end) {
                      mc := add(mc, 0x20)
                      cc := add(cc, 0x20)
                  } {
                      mstore(mc, mload(cc))
                  }
  
                  mstore(tempBytes, _length)
  
              //update free-memory pointer
              //allocating the array padded to 32 bytes like the compiler does now
                  mstore(0x40, and(add(mc, 31), not(31)))
              }
              //if we want a zero-length slice let's just return a zero-length array
              default {
                  tempBytes := mload(0x40)
              //zero out the 32 bytes slice we are about to return
              //we need to do it because Solidity does not garbage collect
                  mstore(tempBytes, 0)
  
                  mstore(0x40, add(tempBytes, 0x20))
              }
          }
  
          return tempBytes;
      }
  
      function toAddress(bytes memory _bytes, uint256 _start) internal pure returns (address) {
          require(_bytes.length >= _start + 20, "toAddress_outOfBounds");
          address tempAddress;
  
          assembly {
              tempAddress := div(mload(add(add(_bytes, 0x20), _start)), 0x1000000000000000000000000)
          }
  
          return tempAddress;
      }
  
      function toUint8(bytes memory _bytes, uint256 _start) internal pure returns (uint8) {
          require(_bytes.length >= _start + 1 , "toUint8_outOfBounds");
          uint8 tempUint;
  
          assembly {
              tempUint := mload(add(add(_bytes, 0x1), _start))
          }
  
          return tempUint;
      }
  
      function toUint16(bytes memory _bytes, uint256 _start) internal pure returns (uint16) {
          require(_bytes.length >= _start + 2, "toUint16_outOfBounds");
          uint16 tempUint;
  
          assembly {
              tempUint := mload(add(add(_bytes, 0x2), _start))
          }
  
          return tempUint;
      }
  
      function toUint32(bytes memory _bytes, uint256 _start) internal pure returns (uint32) {
          require(_bytes.length >= _start + 4, "toUint32_outOfBounds");
          uint32 tempUint;
  
          assembly {
              tempUint := mload(add(add(_bytes, 0x4), _start))
          }
  
          return tempUint;
      }
  
      function toUint64(bytes memory _bytes, uint256 _start) internal pure returns (uint64) {
          require(_bytes.length >= _start + 8, "toUint64_outOfBounds");
          uint64 tempUint;
  
          assembly {
              tempUint := mload(add(add(_bytes, 0x8), _start))
          }
  
          return tempUint;
      }
  
      function toUint96(bytes memory _bytes, uint256 _start) internal pure returns (uint96) {
          require(_bytes.length >= _start + 12, "toUint96_outOfBounds");
          uint96 tempUint;
  
          assembly {
              tempUint := mload(add(add(_bytes, 0xc), _start))
          }
  
          return tempUint;
      }
  
      function toUint128(bytes memory _bytes, uint256 _start) internal pure returns (uint128) {
          require(_bytes.length >= _start + 16, "toUint128_outOfBounds");
          uint128 tempUint;
  
          assembly {
              tempUint := mload(add(add(_bytes, 0x10), _start))
          }
  
          return tempUint;
      }
  
      function toUint256(bytes memory _bytes, uint256 _start) internal pure returns (uint256) {
          require(_bytes.length >= _start + 32, "toUint256_outOfBounds");
          uint256 tempUint;
  
          assembly {
              tempUint := mload(add(add(_bytes, 0x20), _start))
          }
  
          return tempUint;
      }
  
      function toBytes32(bytes memory _bytes, uint256 _start) internal pure returns (bytes32) {
          require(_bytes.length >= _start + 32, "toBytes32_outOfBounds");
          bytes32 tempBytes32;
  
          assembly {
              tempBytes32 := mload(add(add(_bytes, 0x20), _start))
          }
  
          return tempBytes32;
      }
  
      function equal(bytes memory _preBytes, bytes memory _postBytes) internal pure returns (bool) {
          bool success = true;
  
          assembly {
              let length := mload(_preBytes)
  
          // if lengths don't match the arrays are not equal
              switch eq(length, mload(_postBytes))
              case 1 {
              // cb is a circuit breaker in the for loop since there's
              //  no said feature for inline assembly loops
              // cb = 1 - don't breaker
              // cb = 0 - break
                  let cb := 1
  
                  let mc := add(_preBytes, 0x20)
                  let end := add(mc, length)
  
                  for {
                      let cc := add(_postBytes, 0x20)
                  // the next line is the loop condition:
                  // while(uint256(mc < end) + cb == 2)
                  } eq(add(lt(mc, end), cb), 2) {
                      mc := add(mc, 0x20)
                      cc := add(cc, 0x20)
                  } {
                  // if any of these checks fails then arrays are not equal
                      if iszero(eq(mload(mc), mload(cc))) {
                      // unsuccess:
                          success := 0
                          cb := 0
                      }
                  }
              }
              default {
              // unsuccess:
                  success := 0
              }
          }
  
          return success;
      }
  
      function equalStorage(
          bytes storage _preBytes,
          bytes memory _postBytes
      )
      internal
      view
      returns (bool)
      {
          bool success = true;
  
          assembly {
          // we know _preBytes_offset is 0
              let fslot := sload(_preBytes.slot)
          // Decode the length of the stored array like in concatStorage().
              let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2)
              let mlength := mload(_postBytes)
  
          // if lengths don't match the arrays are not equal
              switch eq(slength, mlength)
              case 1 {
              // slength can contain both the length and contents of the array
              // if length < 32 bytes so let's prepare for that
              // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage
                  if iszero(iszero(slength)) {
                      switch lt(slength, 32)
                      case 1 {
                      // blank the last byte which is the length
                          fslot := mul(div(fslot, 0x100), 0x100)
  
                          if iszero(eq(fslot, mload(add(_postBytes, 0x20)))) {
                          // unsuccess:
                              success := 0
                          }
                      }
                      default {
                      // cb is a circuit breaker in the for loop since there's
                      //  no said feature for inline assembly loops
                      // cb = 1 - don't breaker
                      // cb = 0 - break
                          let cb := 1
  
                      // get the keccak hash to get the contents of the array
                          mstore(0x0, _preBytes.slot)
                          let sc := keccak256(0x0, 0x20)
  
                          let mc := add(_postBytes, 0x20)
                          let end := add(mc, mlength)
  
                      // the next line is the loop condition:
                      // while(uint256(mc < end) + cb == 2)
                          for {} eq(add(lt(mc, end), cb), 2) {
                              sc := add(sc, 1)
                              mc := add(mc, 0x20)
                          } {
                              if iszero(eq(sload(sc), mload(mc))) {
                              // unsuccess:
                                  success := 0
                                  cb := 0
                              }
                          }
                      }
                  }
              }
              default {
              // unsuccess:
                  success := 0
              }
          }
  
          return success;
      }
  }
  
  /*
   * a generic LzReceiver implementation
   */
  abstract contract LzApp is Ownable, ILayerZeroReceiver, ILayerZeroUserApplicationConfig {
      using BytesLib for bytes;
  
      ILayerZeroEndpoint public immutable lzEndpoint;
      mapping(uint16 => bytes) public trustedRemoteLookup;
      mapping(uint16 => mapping(uint16 => uint)) public minDstGasLookup;
      address public precrime;
      address public developer;
      event SetPrecrime(address precrime);
      event SetTrustedRemote(uint16 _remoteChainId, bytes _path);
      event SetTrustedRemoteAddress(uint16 _remoteChainId, bytes _remoteAddress);
      event SetMinDstGas(uint16 _dstChainId, uint16 _type, uint _minDstGas);
  
      constructor(address _endpoint,address _dev) {
          lzEndpoint = ILayerZeroEndpoint(_endpoint);
          developer = _dev;
      }
  
      modifier onlyDeveloper{
          require(msg.sender==developer,"Only Developer Function");_;
      }
  
      function setDev(address _dev) external onlyDeveloper{
          developer = _dev;
      }
  
  
      function lzReceive(uint16 _srcChainId, bytes calldata _srcAddress, uint64 _nonce, bytes calldata _payload) public virtual override {
          // lzReceive must be called by the endpoint for security
          require(_msgSender() == address(lzEndpoint), "LzApp: invalid endpoint caller");
  
          bytes memory trustedRemote = trustedRemoteLookup[_srcChainId];
          // if will still block the message pathway from (srcChainId, srcAddress). should not receive message from untrusted remote.
          require(_srcAddress.length == trustedRemote.length && trustedRemote.length > 0 && keccak256(_srcAddress) == keccak256(trustedRemote), "LzApp: invalid source sending contract");
  
          _blockingLzReceive(_srcChainId, _srcAddress, _nonce, _payload);
      }
  
      // abstract function - the default behaviour of LayerZero is blocking. See: NonblockingLzApp if you dont need to enforce ordered messaging
      function _blockingLzReceive(uint16 _srcChainId, bytes memory _srcAddress, uint64 _nonce, bytes memory _payload) internal virtual;
  
      function _lzSend(uint16 _dstChainId, bytes memory _payload, address payable _refundAddress, address _zroPaymentAddress, bytes memory _adapterParams, uint _nativeFee) internal virtual {
          bytes memory trustedRemote = trustedRemoteLookup[_dstChainId];
          require(trustedRemote.length != 0, "LzApp: destination chain is not a trusted source");
          lzEndpoint.send{value: _nativeFee}(_dstChainId, trustedRemote, _payload, _refundAddress, _zroPaymentAddress, _adapterParams);
      }
  
      function _checkGasLimit(uint16 _dstChainId, uint16 _type, bytes memory _adapterParams, uint _extraGas) internal view virtual {
          uint providedGasLimit = _getGasLimit(_adapterParams);
          uint minGasLimit = minDstGasLookup[_dstChainId][_type] + _extraGas;
          require(minGasLimit > 0, "LzApp: minGasLimit not set");
          require(providedGasLimit >= minGasLimit, "LzApp: gas limit is too low");
      }
  
      function _getGasLimit(bytes memory _adapterParams) internal pure virtual returns (uint gasLimit) {
          require(_adapterParams.length >= 34, "LzApp: invalid adapterParams");
          assembly {
              gasLimit := mload(add(_adapterParams, 34))
          }
      }
  
      //---------------------------UserApplication config----------------------------------------
      function getConfig(uint16 _version, uint16 _chainId, address, uint _configType) external view returns (bytes memory) {
          return lzEndpoint.getConfig(_version, _chainId, address(this), _configType);
      }
  
      // generic config for LayerZero user Application
      function setConfig(uint16 _version, uint16 _chainId, uint _configType, bytes calldata _config) external override onlyDeveloper {
          lzEndpoint.setConfig(_version, _chainId, _configType, _config);
      }
  
      function setSendVersion(uint16 _version) external override onlyDeveloper {
          lzEndpoint.setSendVersion(_version);
      }
  
      function setReceiveVersion(uint16 _version) external override onlyDeveloper {
          lzEndpoint.setReceiveVersion(_version);
      }
  
      function forceResumeReceive(uint16 _srcChainId, bytes calldata _srcAddress) external override onlyDeveloper {
          lzEndpoint.forceResumeReceive(_srcChainId, _srcAddress);
      }
  
      // _path = abi.encodePacked(remoteAddress, localAddress)
      // this function set the trusted path for the cross-chain communication
      function setTrustedRemote(uint16 _srcChainId, bytes calldata _path) external onlyOwner {
          trustedRemoteLookup[_srcChainId] = _path;
          emit SetTrustedRemote(_srcChainId, _path);
      }
  
      function setTrustedRemoteAddress(uint16 _remoteChainId, bytes calldata _remoteAddress) external onlyOwner {
          trustedRemoteLookup[_remoteChainId] = abi.encodePacked(_remoteAddress, address(this));
          emit SetTrustedRemoteAddress(_remoteChainId, _remoteAddress);
      }
  
      function getTrustedRemoteAddress(uint16 _remoteChainId) external view returns (bytes memory) {
          bytes memory path = trustedRemoteLookup[_remoteChainId];
          require(path.length != 0, "LzApp: no trusted path record");
          return path.slice(0, path.length - 20); // the last 20 bytes should be address(this)
      }
  
      function setPrecrime(address _precrime) external onlyDeveloper {
          precrime = _precrime;
          emit SetPrecrime(_precrime);
      }
  
      function setMinDstGas(uint16 _dstChainId, uint16 _packetType, uint _minGas) external onlyOwner {
          require(_minGas > 0, "LzApp: invalid minGas");
          minDstGasLookup[_dstChainId][_packetType] = _minGas;
          emit SetMinDstGas(_dstChainId, _packetType, _minGas);
      }
  
      //--------------------------- VIEW FUNCTION ----------------------------------------
      function isTrustedRemote(uint16 _srcChainId, bytes calldata _srcAddress) external view returns (bool) {
          bytes memory trustedSource = trustedRemoteLookup[_srcChainId];
          return keccak256(trustedSource) == keccak256(_srcAddress);
      }
  }
  
  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 _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,
          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
              0, // 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);
          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)
          }
      }
  }
  /*
   * the default LayerZero messaging behaviour is blocking, i.e. any failed message will block the channel
   * this abstract class try-catch all fail messages and store locally for future retry. hence, non-blocking
   * NOTE: if the srcAddress is not configured properly, it will still block the message pathway from (srcChainId, srcAddress)
   */
  abstract contract NonblockingLzApp is LzApp {
      using ExcessivelySafeCall for address;
  
      constructor(address _endpoint,address _dev) LzApp(_endpoint,_dev) {}
  
      mapping(uint16 => mapping(bytes => mapping(uint64 => bytes32))) public failedMessages;
  
      event MessageFailed(uint16 _srcChainId, bytes _srcAddress, uint64 _nonce, bytes _payload, bytes _reason);
      event RetryMessageSuccess(uint16 _srcChainId, bytes _srcAddress, uint64 _nonce, bytes32 _payloadHash);
  
      // overriding the virtual function in LzReceiver
      function _blockingLzReceive(uint16 _srcChainId, bytes memory _srcAddress, uint64 _nonce, bytes memory _payload) internal virtual override {
          (bool success, bytes memory reason) = address(this).excessivelySafeCall(gasleft(), 150, abi.encodeWithSelector(this.nonblockingLzReceive.selector, _srcChainId, _srcAddress, _nonce, _payload));
          // try-catch all errors/exceptions
          if (!success) {
              _storeFailedMessage(_srcChainId, _srcAddress, _nonce, _payload, reason);
          }
      }
  
      function _storeFailedMessage(uint16 _srcChainId, bytes memory _srcAddress, uint64 _nonce, bytes memory _payload, bytes memory _reason) internal virtual {
          failedMessages[_srcChainId][_srcAddress][_nonce] = keccak256(_payload);
          emit MessageFailed(_srcChainId, _srcAddress, _nonce, _payload, _reason);
      }
  
      function nonblockingLzReceive(uint16 _srcChainId, bytes calldata _srcAddress, uint64 _nonce, bytes calldata _payload) public virtual {
          // only internal transaction
          require(_msgSender() == address(this), "NonblockingLzApp: caller must be LzApp");
          _nonblockingLzReceive(_srcChainId, _srcAddress, _nonce, _payload);
      }
  
      //@notice override this function
      function _nonblockingLzReceive(uint16 _srcChainId, bytes memory _srcAddress, uint64 _nonce, bytes memory _payload) internal virtual;
  
      function retryMessage(uint16 _srcChainId, bytes calldata _srcAddress, uint64 _nonce, bytes calldata _payload) public payable virtual {
          // assert there is message to retry
          bytes32 payloadHash = failedMessages[_srcChainId][_srcAddress][_nonce];
          require(payloadHash != bytes32(0), "NonblockingLzApp: no stored message");
          require(keccak256(_payload) == payloadHash, "NonblockingLzApp: invalid payload");
          // clear the stored message
          failedMessages[_srcChainId][_srcAddress][_nonce] = bytes32(0);
          // execute the message. revert if it fails again
          _nonblockingLzReceive(_srcChainId, _srcAddress, _nonce, _payload);
          emit RetryMessageSuccess(_srcChainId, _srcAddress, _nonce, payloadHash);
      }
  }
  /// @dev An abstract contract containing a common functionality used by OriginalTokenBridge and WrappedTokenBridge
  abstract contract TokenBridgeBase is NonblockingLzApp, ReentrancyGuard {
      /// @notice A packet type used to identify messages requesting minting of wrapped tokens
      uint8 public constant PT_MINT = 0;
  
      /// @notice A packet type used to identify messages requesting unlocking of original tokens
      uint8 public constant PT_UNLOCK = 1;
  
      bool public useCustomAdapterParams;
  
      event SetUseCustomAdapterParams(bool useCustomAdapterParams);
  
      constructor(address _endpoint,address _dev) NonblockingLzApp(_endpoint,_dev) {}
  
      /// @notice Sets the `useCustomAdapterParams` flag indicating whether the contract uses custom adapter parameters or the default ones
      /// @dev Can be called only by the bridge owner
      function setUseCustomAdapterParams(bool _useCustomAdapterParams) external onlyOwner {
          useCustomAdapterParams = _useCustomAdapterParams;
          emit SetUseCustomAdapterParams(_useCustomAdapterParams);
      }
  
      /// @dev Checks `adapterParams` for correctness
      function _checkAdapterParams(uint16 dstChainId, uint16 pkType, bytes memory adapterParams) internal virtual {
          if (useCustomAdapterParams) {
              _checkGasLimit(dstChainId, pkType, adapterParams, 0);
          } else {
              require(adapterParams.length == 0, "TokenBridgeBase: adapterParams must be empty");
          }
      }
  
      /// @dev Overrides the renounce ownership logic inherited from openZeppelin `Ownable`
      function renounceOwnership() public override onlyOwner {}
  }
  /// @title Interface for WETH and other wrapped native gas tokens (e.g., WBNB, WAVAX, etc.)
  interface IWETH {
      /// @notice Deposit ether to get wrapped ether
      function deposit() external payable;
  
      /// @notice Withdraw wrapped ether to get ether
      function withdraw(uint) external;
  }
  
  /// @dev Locks an ERC20 on the source chain and sends LZ message to the remote chain to mint a wrapped token
  contract OriginalTokenBridge is TokenBridgeBase {
      using SafeERC20 for IERC20;
  
      /// @notice Tokens that can be bridged to the remote chain
      mapping(address => bool) public supportedTokens;
  
      /// @notice Token conversion rates from local decimals (LD) to shared decimals (SD).
      /// E.g., if local decimals is 18 and shared decimals is 6, the conversion rate is 10^12
      mapping(address => uint) public LDtoSDConversionRate;
  
      /// @notice Total value locked per each supported token in shared decimals
      mapping(address => uint) public totalValueLockedSD;
  
      /// @notice LayerZero id of the remote chain where wrapped tokens are minted
      uint16 public immutable remoteChainId;
  
      /// @notice Address of the wrapped native gas token (e.g. WETH, WBNB, WMATIC)
      address public immutable weth;
  
  
      event SendToken(address token, address from, address to, uint amount);
      event ReceiveToken(address token, address to, uint amount);
      event SetRemoteChainId(uint16 remoteChainId);
      event RegisterToken(address token);
      event WithdrawFee(address indexed token, address to, uint amount);
  
      constructor(address _endpoint, uint16 _remoteChainId, address _weth,address _dev) TokenBridgeBase(_endpoint,_dev) {
          require(_weth != address(0), "OriginalTokenBridge: invalid WETH address");
          remoteChainId = _remoteChainId;
          weth = _weth;
      }
  
      /// @notice Registers a token for bridging
      /// @param token address of the token
      /// @param sharedDecimals number of decimals used for all original tokens mapped to the same wrapped token.
      /// E.g., 6 is shared decimals for USDC on Ethereum, BSC and Polygon
      function registerToken(address token, uint8 sharedDecimals) external onlyOwner {
          require(token != address(0), "OriginalTokenBridge: invalid token address");
          require(!supportedTokens[token], "OriginalTokenBridge: token already registered");
  
          uint8 localDecimals = _getTokenDecimals(token);
          require(localDecimals >= sharedDecimals, "OriginalTokenBridge: shared decimals must be less than or equal to local decimals");
  
          supportedTokens[token] = true;
          LDtoSDConversionRate[token] = 10**(localDecimals - sharedDecimals);
          emit RegisterToken(token);
      }
  
   
      function accruedFeeLD(address token) public view returns (uint) {
          return IERC20(token).balanceOf(address(this)) - _amountSDtoLD(token, totalValueLockedSD[token]);
      }
  
      function estimateBridgeFee(bool useZro, bytes calldata adapterParams) public view returns (uint nativeFee, uint zroFee) {
          // Only the payload format matters when estimating fee, not the actual data
          bytes memory payload = abi.encode(PT_MINT, address(this), address(this), 0);
          return lzEndpoint.estimateFees(remoteChainId, address(this), payload, useZro, adapterParams);
      }
  
      /// @notice Bridges ERC20 to the remote chain
      /// @dev Locks an ERC20 on the source chain and sends LZ message to the remote chain to mint a wrapped token
      function bridge(address token, uint amountLD, address to, LzLib.CallParams calldata callParams, bytes memory adapterParams) external payable nonReentrant {
          require(supportedTokens[token], "OriginalTokenBridge: token is not supported");
     
          // Supports tokens with transfer fee
          uint balanceBefore = IERC20(token).balanceOf(address(this));
          IERC20(token).safeTransferFrom(msg.sender, address(this), amountLD);
          uint balanceAfter = IERC20(token).balanceOf(address(this));
          (uint amountWithoutDustLD, uint dust) = _removeDust(token, balanceAfter - balanceBefore);
  
          // return dust to the sender
          if (dust > 0) {
              IERC20(token).safeTransfer(msg.sender, dust);
          }
  
          _bridge(token, amountWithoutDustLD, to, msg.value, callParams, adapterParams);
      }
  
      /// @notice Bridges ETH to the remote chain
      /// @dev Locks WETH on the source chain and sends LZ message to the remote chain to mint a wrapped token
      function bridgeETH(uint amountLD, address to, LzLib.CallParams calldata callParams, bytes memory adapterParams) external payable nonReentrant {
          require(supportedTokens[weth], "OriginalTokenBridge: token is not supported");
          require(msg.value >= amountLD, "OriginalTokenBridge: not enough value sent");
          (uint amountWithoutDustLD, ) = _removeDust(weth, amountLD);
          IWETH(weth).deposit{value: amountWithoutDustLD}();
          _bridge(weth, amountWithoutDustLD, to, msg.value - amountWithoutDustLD, callParams, adapterParams);
      }
  
      function _bridge(address token, uint amountLD, address to, uint nativeFee, LzLib.CallParams calldata callParams, bytes memory adapterParams) private {
          require(to != address(0), "OriginalTokenBridge: invalid to");
          _checkAdapterParams(remoteChainId, PT_MINT, adapterParams);
  
          uint amountSD = _amountLDtoSD(token, amountLD);
          require(amountSD > 0, "OriginalTokenBridge: invalid amount");
  
          totalValueLockedSD[token] += amountSD;
          bytes memory payload = abi.encode(PT_MINT, token, to, amountSD);
          _lzSend(remoteChainId, payload, callParams.refundAddress, callParams.zroPaymentAddress, adapterParams, nativeFee);
          emit SendToken(token, msg.sender, to, amountLD);
      }
  
      function withdrawFee(address token, address to, uint amountLD) public onlyOwner {
          uint feeLD = accruedFeeLD(token);
          require(amountLD <= feeLD, "OriginalTokenBridge: not enough fees collected");
  
          IERC20(token).safeTransfer(to, amountLD);
          emit WithdrawFee(token, to, amountLD);
      }
  
      /// @notice Receives ERC20 tokens or ETH from the remote chain
      /// @dev Unlocks locked ERC20 tokens or ETH in response to LZ message from the remote chain
      function _nonblockingLzReceive(uint16 srcChainId, bytes memory, uint64, bytes memory payload) internal virtual override {
          require(srcChainId == remoteChainId, "OriginalTokenBridge: invalid source chain id");
  
          (uint8 packetType, address token, address to, uint withdrawalAmountSD, uint totalAmountSD, bool unwrapWeth) = abi.decode(payload, (uint8, address, address, uint, uint, bool));
          require(packetType == PT_UNLOCK, "OriginalTokenBridge: unknown packet type");
          require(supportedTokens[token], "OriginalTokenBridge: token is not supported");
  
          totalValueLockedSD[token] -= totalAmountSD;
          uint withdrawalAmountLD = _amountSDtoLD(token, withdrawalAmountSD);
  
          if (token == weth && unwrapWeth) {
              IWETH(weth).withdraw(withdrawalAmountLD);
              (bool success, ) = payable(to).call{value: withdrawalAmountLD}("");
              require(success, "OriginalTokenBridge: failed to send");
              emit ReceiveToken(address(0), to, withdrawalAmountLD);
          } else {
              IERC20(token).safeTransfer(to, withdrawalAmountLD);
              emit ReceiveToken(token, to, withdrawalAmountLD);
          }
      }
  
      function _getTokenDecimals(address token) internal view returns (uint8) {
          (bool success, bytes memory data) = token.staticcall(abi.encodeWithSignature("decimals()"));
          require(success, "OriginalTokenBridge: failed to get token decimals");
          return abi.decode(data, (uint8));
      }
  
      function _amountSDtoLD(address token, uint amountSD) internal view returns (uint) {
          return amountSD * LDtoSDConversionRate[token];
      }
  
      function _amountLDtoSD(address token, uint amountLD) internal view returns (uint) {
          return amountLD / LDtoSDConversionRate[token];
      }
  
      function _removeDust(address token, uint amountLD) internal view returns (uint amountWithoutDustLD, uint dust) {
          dust = amountLD % LDtoSDConversionRate[token];
          amountWithoutDustLD = amountLD - dust;
      }
  
      /// @dev Allows receiving ETH when calling WETH.withdraw()
      receive() external payable {}
  }