ETH Price: $2,647.17 (-0.52%)

Transaction Decoder

Block:
17558002 at Jun-25-2023 05:33:11 PM +UTC
Transaction Fee:
0.0008589735162632 ETH $2.27
Gas Used:
69,472 Gas / 12.364312475 Gwei

Emitted Events:

330 Registry.ExecutionCompleted( middlewareID=0, bridgeID=14, inputAmount=2000000000000000 )
331 GasMovr.Deposit( destinationReceiver=[Sender] 0xbacf50b1d4b1709d13532fe59d3c368b51c89611, amount=2000000000000000, destinationChainId=42161 )

Account State Difference:

  Address   Before After State Difference Code
0xb584D4bE...165204599
(Socket: GasMovr)
5.307942753239639563 Eth5.309942753239639563 Eth0.002
0xBaCf50B1...b51c89611
0.003178363906226449 Eth
Nonce: 8
0.000319390389963249 Eth
Nonce: 9
0.0028589735162632
154.071899605666479202 Eth154.071903322467804322 Eth0.00000371680132512

Execution Trace

ETH 0.002 Registry.outboundTransferTo( _userRequest=[{name:receiverAddress, type:address, order:1, indexed:false, value:0xBaCf50B1d4B1709D13532FE59D3C368b51c89611, valueString:0xBaCf50B1d4B1709D13532FE59D3C368b51c89611}, {name:toChainId, type:uint256, order:2, indexed:false, value:42161, valueString:42161}, {name:amount, type:uint256, order:3, indexed:false, value:2000000000000000, valueString:2000000000000000}, {name:middlewareRequest, type:tuple, order:4, indexed:false, value:[{name:id, type:uint256, order:1, indexed:false, value:0, valueString:0}, {name:optionalNativeAmount, type:uint256, order:2, indexed:false, value:0, valueString:0}, {name:inputToken, type:address, order:3, indexed:false, value:0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE, valueString:0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE}, {name:data, type:bytes, order:4, indexed:false, value:0x0000000000000000000000000000000000000000000000000000000000000000, valueString:0x0000000000000000000000000000000000000000000000000000000000000000}], valueString:[{name:id, type:uint256, order:1, indexed:false, value:0, valueString:0}, {name:optionalNativeAmount, type:uint256, order:2, indexed:false, value:0, valueString:0}, {name:inputToken, type:address, order:3, indexed:false, value:0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE, valueString:0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE}, {name:data, type:bytes, order:4, indexed:false, value:0x0000000000000000000000000000000000000000000000000000000000000000, valueString:0x0000000000000000000000000000000000000000000000000000000000000000}]}, {name:bridgeRequest, type:tuple, order:5, indexed:false, value:[{name:id, type:uint256, order:1, indexed:false, value:14, valueString:14}, {name:optionalNativeAmount, type:uint256, order:2, indexed:false, value:0, valueString:0}, {name:inputToken, type:address, order:3, indexed:false, value:0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE, valueString:0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE}, {name:data, type:bytes, order:4, indexed:false, value:0x0000000000000000000000000000000000000000000000000000000000000000, valueString:0x0000000000000000000000000000000000000000000000000000000000000000}], valueString:[{name:id, type:uint256, order:1, indexed:false, value:14, valueString:14}, {name:optionalNativeAmount, type:uint256, order:2, indexed:false, value:0, valueString:0}, {name:inputToken, type:address, order:3, indexed:false, value:0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE, valueString:0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE}, {name:data, type:bytes, order:4, indexed:false, value:0x0000000000000000000000000000000000000000000000000000000000000000, valueString:0x0000000000000000000000000000000000000000000000000000000000000000}]}] )
  • ETH 0.002 RefuelBridgeImpl.outboundTransferTo( 2000000000000000, 0xBaCf50B1d4B1709D13532FE59D3C368b51c89611, _receiverAddress=0xBaCf50B1d4B1709D13532FE59D3C368b51c89611, 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE, _toChainId=42161, 0x0000000000000000000000000000000000000000000000000000000000000000 )
    • ETH 0.002 GasMovr.depositNativeToken( destinationChainId=42161, _to=0xBaCf50B1d4B1709D13532FE59D3C368b51c89611 )
      outboundTransferTo[Registry (ln:107)]
      File 1 of 3: Registry
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.4;
      import "@openzeppelin/contracts/access/Ownable.sol";
      import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
      import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
      import "./helpers/errors.sol";
      import "./ImplBase.sol";
      import "./MiddlewareImplBase.sol";
      /**
      // @title Movr Regisrtry Contract.
      // @notice This is the main contract that is called using fund movr.
      // This contains all the bridge and middleware ids. 
      // RouteIds signify which bridge to be used. 
      // Middleware Id signifies which aggregator will be used for swapping if required. 
      */
      contract Registry is Ownable {
          using SafeERC20 for IERC20;
          address private constant NATIVE_TOKEN_ADDRESS =
              address(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE);
          ///@notice RouteData stores information for a route
          struct RouteData {
              address route;
              bool isEnabled;
              bool isMiddleware;
          }
          RouteData[] public routes;
          modifier onlyExistingRoute(uint256 _routeId) {
              require(
                  routes[_routeId].route != address(0),
                  MovrErrors.ROUTE_NOT_FOUND
              );
              _;
          }
          constructor(address _owner) Ownable() {
              // first route is for direct bridging
              routes.push(RouteData(NATIVE_TOKEN_ADDRESS, true, true));
              transferOwnership(_owner);
          }
          // Function to receive Ether. msg.data must be empty
          receive() external payable {}
          //
          // Events
          //
          event NewRouteAdded(
              uint256 routeID,
              address route,
              bool isEnabled,
              bool isMiddleware
          );
          event RouteDisabled(uint256 routeID);
          event ExecutionCompleted(
              uint256 middlewareID,
              uint256 bridgeID,
              uint256 inputAmount
          );
          /**
          // @param id route id of middleware to be used
          // @param optionalNativeAmount is the amount of native asset that the route requires 
          // @param inputToken token address which will be swapped to
          // BridgeRequest inputToken 
          // @param data to be used by middleware
          */
          struct MiddlewareRequest {
              uint256 id;
              uint256 optionalNativeAmount;
              address inputToken;
              bytes data;
          }
          /**
          // @param id route id of bridge to be used
          // @param optionalNativeAmount optinal native amount, to be used
          // when bridge needs native token along with ERC20    
          // @param inputToken token addresss which will be bridged 
          // @param data bridgeData to be used by bridge
          */
          struct BridgeRequest {
              uint256 id;
              uint256 optionalNativeAmount;
              address inputToken;
              bytes data;
          }
          /**
          // @param receiverAddress Recipient address to recieve funds on destination chain
          // @param toChainId Destination ChainId
          // @param amount amount to be swapped if middlewareId is 0  it will be
          // the amount to be bridged
          // @param middlewareRequest middleware Requestdata
          // @param bridgeRequest bridge request data
          */
          struct UserRequest {
              address receiverAddress;
              uint256 toChainId;
              uint256 amount;
              MiddlewareRequest middlewareRequest;
              BridgeRequest bridgeRequest;
          }
          /**
          // @notice function responsible for calling the respective implementation 
          // depending on the bridge to be used
          // If the middlewareId is 0 then no swap is required,
          // we can directly bridge the source token to wherever required,
          // else, we first call the Swap Impl Base for swapping to the required 
          // token and then start the bridging
          // @dev It is required for isMiddleWare to be true for route 0 as it is a special case
          // @param _userRequest calldata follows the input data struct
          */
          function outboundTransferTo(UserRequest calldata _userRequest)
              external
              payable
          {
              require(_userRequest.amount != 0, MovrErrors.INVALID_AMT);
              // make sure bridge ID is not 0
              require(
                  _userRequest.bridgeRequest.id != 0,
                  MovrErrors.INVALID_BRIDGE_ID
              );
              // make sure bridge input is provided
              require(
                  _userRequest.bridgeRequest.inputToken != address(0),
                  MovrErrors.ADDRESS_0_PROVIDED
              );
              // load middleware info and validate
              RouteData memory middlewareInfo = routes[
                  _userRequest.middlewareRequest.id
              ];
              require(
                  middlewareInfo.route != address(0) &&
                      middlewareInfo.isEnabled &&
                      middlewareInfo.isMiddleware,
                  MovrErrors.ROUTE_NOT_ALLOWED
              );
              // load bridge info and validate
              RouteData memory bridgeInfo = routes[_userRequest.bridgeRequest.id];
              require(
                  bridgeInfo.route != address(0) &&
                      bridgeInfo.isEnabled &&
                      !bridgeInfo.isMiddleware,
                  MovrErrors.ROUTE_NOT_ALLOWED
              );
              emit ExecutionCompleted(
                  _userRequest.middlewareRequest.id,
                  _userRequest.bridgeRequest.id,
                  _userRequest.amount
              );
              // if middlewareID is 0 it means we dont want to perform a action before bridging
              // and directly want to move for bridging
              if (_userRequest.middlewareRequest.id == 0) {
                  // perform the bridging
                  ImplBase(bridgeInfo.route).outboundTransferTo{value: msg.value}(
                      _userRequest.amount,
                      msg.sender,
                      _userRequest.receiverAddress,
                      _userRequest.bridgeRequest.inputToken,
                      _userRequest.toChainId,
                      _userRequest.bridgeRequest.data
                  );
                  return;
              }
              // we first perform an action using the middleware
              // we determine if the input asset is a native asset, if yes we pass
              // the amount as value, else we pass the optionalNativeAmount
              uint256 _amountOut = MiddlewareImplBase(middlewareInfo.route)
                  .performAction{
                  value: _userRequest.middlewareRequest.inputToken ==
                      NATIVE_TOKEN_ADDRESS
                      ? _userRequest.amount +
                          _userRequest.middlewareRequest.optionalNativeAmount
                      : _userRequest.middlewareRequest.optionalNativeAmount
              }(
                  msg.sender,
                  _userRequest.middlewareRequest.inputToken,
                  _userRequest.amount,
                  address(this),
                  _userRequest.middlewareRequest.data
              );
              // we mutate this variable if the input asset to bridge Impl is NATIVE
              uint256 nativeInput = _userRequest.bridgeRequest.optionalNativeAmount;
              // if the input asset is ERC20, we need to grant the bridge implementation approval
              if (_userRequest.bridgeRequest.inputToken != NATIVE_TOKEN_ADDRESS) {
                  IERC20(_userRequest.bridgeRequest.inputToken).safeIncreaseAllowance(
                          bridgeInfo.route,
                          _amountOut
                      );
              } else {
                  // if the input asset is native we need to set it as value
                  nativeInput =
                      _amountOut +
                      _userRequest.bridgeRequest.optionalNativeAmount;
              }
              // send off to bridge
              ImplBase(bridgeInfo.route).outboundTransferTo{value: nativeInput}(
                  _amountOut,
                  address(this),
                  _userRequest.receiverAddress,
                  _userRequest.bridgeRequest.inputToken,
                  _userRequest.toChainId,
                  _userRequest.bridgeRequest.data
              );
          }
          //
          // Route management functions
          //
          /// @notice add routes to the registry.
          function addRoutes(RouteData[] calldata _routes)
              external
              onlyOwner
              returns (uint256[] memory)
          {
              require(_routes.length != 0, MovrErrors.EMPTY_INPUT);
              uint256[] memory _routeIds = new uint256[](_routes.length);
              for (uint256 i = 0; i < _routes.length; i++) {
                  require(
                      _routes[i].route != address(0),
                      MovrErrors.ADDRESS_0_PROVIDED
                  );
                  routes.push(_routes[i]);
                  _routeIds[i] = routes.length - 1;
                  emit NewRouteAdded(
                      i,
                      _routes[i].route,
                      _routes[i].isEnabled,
                      _routes[i].isMiddleware
                  );
              }
              return _routeIds;
          }
          ///@notice disables the route  if required.
          function disableRoute(uint256 _routeId)
              external
              onlyOwner
              onlyExistingRoute(_routeId)
          {
              routes[_routeId].isEnabled = false;
              emit RouteDisabled(_routeId);
          }
          function rescueFunds(
              address _token,
              address _receiverAddress,
              uint256 _amount
          ) external onlyOwner {
              IERC20(_token).safeTransfer(_receiverAddress, _amount);
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.0;
      import "../utils/Context.sol";
      /**
       * @dev Contract module which provides a basic access control mechanism, where
       * there is an account (an owner) that can be granted exclusive access to
       * specific functions.
       *
       * By default, the owner account will be the one that deploys the contract. This
       * can later be changed with {transferOwnership}.
       *
       * This module is used through inheritance. It will make available the modifier
       * `onlyOwner`, which can be applied to your functions to restrict their use to
       * the owner.
       */
      abstract contract Ownable is Context {
          address private _owner;
          event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
          /**
           * @dev Initializes the contract setting the deployer as the initial owner.
           */
          constructor () {
              address msgSender = _msgSender();
              _owner = msgSender;
              emit OwnershipTransferred(address(0), msgSender);
          }
          /**
           * @dev Returns the address of the current owner.
           */
          function owner() public view virtual returns (address) {
              return _owner;
          }
          /**
           * @dev Throws if called by any account other than the owner.
           */
          modifier onlyOwner() {
              require(owner() == _msgSender(), "Ownable: caller is not the owner");
              _;
          }
          /**
           * @dev Leaves the contract without owner. It will not be possible to call
           * `onlyOwner` functions anymore. Can only be called by the current owner.
           *
           * NOTE: Renouncing ownership will leave the contract without an owner,
           * thereby removing any functionality that is only available to the owner.
           */
          function renounceOwnership() public virtual onlyOwner {
              emit OwnershipTransferred(_owner, address(0));
              _owner = 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");
              emit OwnershipTransferred(_owner, newOwner);
              _owner = newOwner;
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.0;
      /**
       * @dev Interface of the ERC20 standard as defined in the EIP.
       */
      interface IERC20 {
          /**
           * @dev Returns the amount of tokens in existence.
           */
          function totalSupply() external view returns (uint256);
          /**
           * @dev Returns the amount of tokens owned by `account`.
           */
          function balanceOf(address account) external view returns (uint256);
          /**
           * @dev Moves `amount` tokens from the caller's account to `recipient`.
           *
           * Returns a boolean value indicating whether the operation succeeded.
           *
           * Emits a {Transfer} event.
           */
          function transfer(address recipient, uint256 amount) external returns (bool);
          /**
           * @dev Returns the remaining number of tokens that `spender` will be
           * allowed to spend on behalf of `owner` through {transferFrom}. This is
           * zero by default.
           *
           * This value changes when {approve} or {transferFrom} are called.
           */
          function allowance(address owner, address spender) external view returns (uint256);
          /**
           * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
           *
           * Returns a boolean value indicating whether the operation succeeded.
           *
           * IMPORTANT: Beware that changing an allowance with this method brings the risk
           * that someone may use both the old and the new allowance by unfortunate
           * transaction ordering. One possible solution to mitigate this race
           * condition is to first reduce the spender's allowance to 0 and set the
           * desired value afterwards:
           * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
           *
           * Emits an {Approval} event.
           */
          function approve(address spender, uint256 amount) external returns (bool);
          /**
           * @dev Moves `amount` tokens from `sender` to `recipient` using the
           * allowance mechanism. `amount` is then deducted from the caller's
           * allowance.
           *
           * Returns a boolean value indicating whether the operation succeeded.
           *
           * Emits a {Transfer} event.
           */
          function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
          /**
           * @dev Emitted when `value` tokens are moved from one account (`from`) to
           * another (`to`).
           *
           * Note that `value` may be zero.
           */
          event Transfer(address indexed from, address indexed to, uint256 value);
          /**
           * @dev Emitted when the allowance of a `spender` for an `owner` is set by
           * a call to {approve}. `value` is the new allowance.
           */
          event Approval(address indexed owner, address indexed spender, uint256 value);
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.0;
      import "../IERC20.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'
              // solhint-disable-next-line max-line-length
              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));
              }
          }
          /**
           * @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
                  // solhint-disable-next-line max-line-length
                  require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
              }
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity >=0.8.0;
      library MovrErrors {
          string internal constant ADDRESS_0_PROVIDED = "ADDRESS_0_PROVIDED";
          string internal constant EMPTY_INPUT = "EMPTY_INPUT";
          string internal constant LENGTH_MISMATCH = "LENGTH_MISMATCH";
          string internal constant INVALID_VALUE = "INVALID_VALUE";
          string internal constant INVALID_AMT = "INVALID_AMT";
          string internal constant IMPL_NOT_FOUND = "IMPL_NOT_FOUND";
          string internal constant ROUTE_NOT_FOUND = "ROUTE_NOT_FOUND";
          string internal constant IMPL_NOT_ALLOWED = "IMPL_NOT_ALLOWED";
          string internal constant ROUTE_NOT_ALLOWED = "ROUTE_NOT_ALLOWED";
          string internal constant INVALID_CHAIN_DATA = "INVALID_CHAIN_DATA";
          string internal constant CHAIN_NOT_SUPPORTED = "CHAIN_NOT_SUPPORTED";
          string internal constant TOKEN_NOT_SUPPORTED = "TOKEN_NOT_SUPPORTED";
          string internal constant NOT_IMPLEMENTED = "NOT_IMPLEMENTED";
          string internal constant INVALID_SENDER = "INVALID_SENDER";
          string internal constant INVALID_BRIDGE_ID = "INVALID_BRIDGE_ID";
          string internal constant MIDDLEWARE_ACTION_FAILED =
              "MIDDLEWARE_ACTION_FAILED";
          string internal constant VALUE_SHOULD_BE_ZERO = "VALUE_SHOULD_BE_ZERO";
          string internal constant VALUE_SHOULD_NOT_BE_ZERO = "VALUE_SHOULD_NOT_BE_ZERO";
          string internal constant VALUE_NOT_ENOUGH = "VALUE_NOT_ENOUGH";
          string internal constant VALUE_NOT_EQUAL_TO_AMOUNT = "VALUE_NOT_EQUAL_TO_AMOUNT";
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.4;
      import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
      import "@openzeppelin/contracts/access/Ownable.sol";
      import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
      import "./helpers/errors.sol";
      /**
      @title Abstract Implementation Contract.
      @notice All Bridge Implementation will follow this interface. 
      */
      abstract contract ImplBase is Ownable {
          using SafeERC20 for IERC20;
          address public registry;
          address public constant NATIVE_TOKEN_ADDRESS =
              address(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE);
          event UpdateRegistryAddress(address indexed registryAddress);
          constructor(address _registry) Ownable() {
              registry = _registry;
          }
          modifier onlyRegistry() {
              require(msg.sender == registry, MovrErrors.INVALID_SENDER);
              _;
          }
          function updateRegistryAddress(address newRegistry) external onlyOwner {
              registry = newRegistry;
              emit UpdateRegistryAddress(newRegistry);
          }
          function rescueFunds(
              address token,
              address userAddress,
              uint256 amount
          ) external onlyOwner {
              IERC20(token).safeTransfer(userAddress, amount);
          }
          function outboundTransferTo(
              uint256 _amount,
              address _from,
              address _receiverAddress,
              address _token,
              uint256 _toChainId,
              bytes memory _extraData
          ) external payable virtual;
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.4;
      import "@openzeppelin/contracts/access/Ownable.sol";
      import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
      import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
      import "./helpers/errors.sol";
      /**
      // @title Abstract Contract for middleware services.
      // @notice All middleware services will follow this interface. 
      */
      abstract contract MiddlewareImplBase is Ownable {
          using SafeERC20 for IERC20;
          address public immutable registry;
          /// @notice only registry address is required.
          constructor(address _registry) Ownable() {
              registry = _registry;
          }
          modifier onlyRegistry {
              require(msg.sender == registry, MovrErrors.INVALID_SENDER);
              _;
          }
          function performAction(
              address from,
              address fromToken,
              uint256 amount,
              address receiverAddress,
              bytes memory data
          ) external payable virtual returns (uint256);
          function rescueFunds(
              address token,
              address userAddress,
              uint256 amount
          ) external onlyOwner {
              IERC20(token).safeTransfer(userAddress, amount);
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.0;
      /*
       * @dev Provides information about the current execution context, including the
       * sender of the transaction and its data. While these are generally available
       * via msg.sender and msg.data, they should not be accessed in such a direct
       * manner, since when dealing with meta-transactions the account sending and
       * paying for execution may not be the actual sender (as far as an application
       * is concerned).
       *
       * This contract is only required for intermediate, library-like contracts.
       */
      abstract contract Context {
          function _msgSender() internal view virtual returns (address) {
              return msg.sender;
          }
          function _msgData() internal view virtual returns (bytes calldata) {
              this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
              return msg.data;
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.0;
      /**
       * @dev Collection of functions related to the address type
       */
      library Address {
          /**
           * @dev Returns true if `account` is a contract.
           *
           * [IMPORTANT]
           * ====
           * It is unsafe to assume that an address for which this function returns
           * false is an externally-owned account (EOA) and not a contract.
           *
           * Among others, `isContract` will return false for the following
           * types of addresses:
           *
           *  - an externally-owned account
           *  - a contract in construction
           *  - an address where a contract will be created
           *  - an address where a contract lived, but was destroyed
           * ====
           */
          function isContract(address account) internal view returns (bool) {
              // This method relies on extcodesize, which returns 0 for contracts in
              // construction, since the code is only stored at the end of the
              // constructor execution.
              uint256 size;
              // solhint-disable-next-line no-inline-assembly
              assembly { size := extcodesize(account) }
              return size > 0;
          }
          /**
           * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
           * `recipient`, forwarding all available gas and reverting on errors.
           *
           * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
           * of certain opcodes, possibly making contracts go over the 2300 gas limit
           * imposed by `transfer`, making them unable to receive funds via
           * `transfer`. {sendValue} removes this limitation.
           *
           * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
           *
           * IMPORTANT: because control is transferred to `recipient`, care must be
           * taken to not create reentrancy vulnerabilities. Consider using
           * {ReentrancyGuard} or the
           * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
           */
          function sendValue(address payable recipient, uint256 amount) internal {
              require(address(this).balance >= amount, "Address: insufficient balance");
              // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
              (bool success, ) = recipient.call{ value: amount }("");
              require(success, "Address: unable to send value, recipient may have reverted");
          }
          /**
           * @dev Performs a Solidity function call using a low level `call`. A
           * plain`call` is an unsafe replacement for a function call: use this
           * function instead.
           *
           * If `target` reverts with a revert reason, it is bubbled up by this
           * function (like regular Solidity function calls).
           *
           * Returns the raw returned data. To convert to the expected return value,
           * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
           *
           * Requirements:
           *
           * - `target` must be a contract.
           * - calling `target` with `data` must not revert.
           *
           * _Available since v3.1._
           */
          function functionCall(address target, bytes memory data) internal returns (bytes memory) {
            return functionCall(target, data, "Address: low-level call failed");
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
           * `errorMessage` as a fallback revert reason when `target` reverts.
           *
           * _Available since v3.1._
           */
          function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
              return functionCallWithValue(target, data, 0, errorMessage);
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but also transferring `value` wei to `target`.
           *
           * Requirements:
           *
           * - the calling contract must have an ETH balance of at least `value`.
           * - the called Solidity function must be `payable`.
           *
           * _Available since v3.1._
           */
          function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
              return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
          }
          /**
           * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
           * with `errorMessage` as a fallback revert reason when `target` reverts.
           *
           * _Available since v3.1._
           */
          function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
              require(address(this).balance >= value, "Address: insufficient balance for call");
              require(isContract(target), "Address: call to non-contract");
              // solhint-disable-next-line avoid-low-level-calls
              (bool success, bytes memory returndata) = target.call{ value: value }(data);
              return _verifyCallResult(success, returndata, errorMessage);
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but performing a static call.
           *
           * _Available since v3.3._
           */
          function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
              return functionStaticCall(target, data, "Address: low-level static call failed");
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
           * but performing a static call.
           *
           * _Available since v3.3._
           */
          function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) {
              require(isContract(target), "Address: static call to non-contract");
              // solhint-disable-next-line avoid-low-level-calls
              (bool success, bytes memory returndata) = target.staticcall(data);
              return _verifyCallResult(success, returndata, errorMessage);
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but performing a delegate call.
           *
           * _Available since v3.4._
           */
          function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
              return functionDelegateCall(target, data, "Address: low-level delegate call failed");
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
           * but performing a delegate call.
           *
           * _Available since v3.4._
           */
          function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
              require(isContract(target), "Address: delegate call to non-contract");
              // solhint-disable-next-line avoid-low-level-calls
              (bool success, bytes memory returndata) = target.delegatecall(data);
              return _verifyCallResult(success, returndata, errorMessage);
          }
          function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {
              if (success) {
                  return returndata;
              } else {
                  // Look for revert reason and bubble it up if present
                  if (returndata.length > 0) {
                      // The easiest way to bubble the revert reason is using memory via assembly
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                          let returndata_size := mload(returndata)
                          revert(add(32, returndata), returndata_size)
                      }
                  } else {
                      revert(errorMessage);
                  }
              }
          }
      }
      

      File 2 of 3: GasMovr
      // SPDX-License-Identifier: MIT
      pragma solidity >0.8.0;
      import "@openzeppelin/contracts/access/Ownable.sol";
      import "@openzeppelin/contracts/security/Pausable.sol";
      contract GasMovr is Ownable, Pausable {
          /* 
              Variables
          */
          mapping(uint256 => ChainData) public chainConfig;
          mapping(bytes32 => bool) public processedHashes;
          mapping(address => bool) public senders;
          struct ChainData {
              uint256 chainId;
              bool isEnabled;
          }
          /* 
              Events
          */
          event Deposit(
              address indexed destinationReceiver,
              uint256 amount,
              uint256 indexed destinationChainId
          );
          event Withdrawal(address indexed receiver, uint256 amount);
          event Donation(address sender, uint256 amount);
          event Send(address receiver, uint256 amount, bytes32 srcChainTxHash);
          event GrantSender(address sender);
          event RevokeSender(address sender);
          modifier onlySender() {
              require(senders[msg.sender], "Sender role required");
              _;
          }
          constructor() {
              _grantSenderRole(msg.sender);
          }
          receive() external payable {
              emit Donation(msg.sender, msg.value);
          }
          function depositNativeToken(uint256 destinationChainId, address _to)
              public
              payable
              whenNotPaused
          {
              require(
                  chainConfig[destinationChainId].isEnabled,
                  "Chain is currently disabled"
              );
              emit Deposit(_to, msg.value, destinationChainId);
          }
          function withdrawBalance(address _to, uint256 _amount) public onlyOwner {
              _withdrawBalance(_to, _amount);
          }
          function withdrawFullBalance(address _to) public onlyOwner {
              _withdrawBalance(_to, address(this).balance);
          }
          function _withdrawBalance(address _to, uint256 _amount) private {
              (bool success, ) = _to.call{value: _amount}("");
              require(success, "Failed to send Ether");
              emit Withdrawal(_to, _amount);
          }
          function setIsEnabled(uint256 chainId, bool _isEnabled)
              public
              onlyOwner
              returns (bool)
          {
              chainConfig[chainId].isEnabled = _isEnabled;
              return chainConfig[chainId].isEnabled;
          }
          function setPause() public onlyOwner returns (bool) {
              _pause();
              return paused();
          }
          function setUnPause() public onlyOwner returns (bool) {
              _unpause();
              return paused();
          }
          function addRoutes(ChainData[] calldata _routes) external onlyOwner {
              for (uint256 i = 0; i < _routes.length; i++) {
                  chainConfig[_routes[i].chainId] = _routes[i];
              }
          }
          function getChainData(uint256 chainId)
              public
              view
              returns (ChainData memory)
          {
              return (chainConfig[chainId]);
          }
          function batchSendNativeToken(
              address payable[] memory receivers,
              uint256[] memory amounts,
              bytes32[] memory srcChainTxHashes,
              uint256 perUserGasAmount,
              uint256 maxLimit
          ) public onlySender {
              require(
                  receivers.length == amounts.length &&
                      receivers.length == srcChainTxHashes.length,
                  "Input length mismatch"
              );
              uint256 gasPrice;
              assembly {
                  gasPrice := gasprice()
              }
              for (uint256 i = 0; i < receivers.length; i++) {
                  uint256 _gasFees = amounts[i] > maxLimit
                      ? (amounts[i] - maxLimit + (gasPrice * perUserGasAmount))
                      : gasPrice * perUserGasAmount;
                  _sendNativeToken(
                      receivers[i],
                      amounts[i],
                      srcChainTxHashes[i],
                      _gasFees
                  );
              }
          }
          function sendNativeToken(
              address payable receiver,
              uint256 amount,
              bytes32 srcChainTxHash,
              uint256 perUserGasAmount,
              uint256 maxLimit
          ) public onlySender {
              uint256 gasPrice;
              assembly {
                  gasPrice := gasprice()
              }
              uint256 _gasFees = amount > maxLimit
                  ? (amount - maxLimit + (gasPrice * perUserGasAmount))
                  : gasPrice * perUserGasAmount;
              _sendNativeToken(receiver, amount, srcChainTxHash, _gasFees);
          }
          function _sendNativeToken(
              address payable receiver,
              uint256 amount,
              bytes32 srcChainTxHash,
              uint256 gasFees
          ) private {
              if (processedHashes[srcChainTxHash]) return;
              processedHashes[srcChainTxHash] = true;
              uint256 sendAmount = amount - gasFees;
              emit Send(receiver, sendAmount, srcChainTxHash);
              (bool success, ) = receiver.call{value: sendAmount, gas: 5000}("");
              require(success, "Failed to send Ether");
          }
          function grantSenderRole(address sender) public onlyOwner {
              _grantSenderRole(sender);
          }
          function revokeSenderRole(address sender) public onlyOwner {
              _revokeSenderRole(sender);
          }
          function _grantSenderRole(address sender) private {
              senders[sender] = true;
              emit GrantSender(sender);
          }
          function _revokeSenderRole(address sender) private {
              senders[sender] = false;
              emit RevokeSender(sender);
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.0 (access/Ownable.sol)
      pragma solidity ^0.8.0;
      import "../utils/Context.sol";
      /**
       * @dev Contract module which provides a basic access control mechanism, where
       * there is an account (an owner) that can be granted exclusive access to
       * specific functions.
       *
       * By default, the owner account will be the one that deploys the contract. This
       * can later be changed with {transferOwnership}.
       *
       * This module is used through inheritance. It will make available the modifier
       * `onlyOwner`, which can be applied to your functions to restrict their use to
       * the owner.
       */
      abstract contract Ownable is Context {
          address private _owner;
          event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
          /**
           * @dev Initializes the contract setting the deployer as the initial owner.
           */
          constructor() {
              _transferOwnership(_msgSender());
          }
          /**
           * @dev Returns the address of the current owner.
           */
          function owner() public view virtual returns (address) {
              return _owner;
          }
          /**
           * @dev Throws if called by any account other than the owner.
           */
          modifier onlyOwner() {
              require(owner() == _msgSender(), "Ownable: caller is not the owner");
              _;
          }
          /**
           * @dev Leaves the contract without owner. It will not be possible to call
           * `onlyOwner` functions anymore. Can only be called by the current owner.
           *
           * NOTE: Renouncing ownership will leave the contract without an owner,
           * thereby removing any functionality that is only available to the owner.
           */
          function renounceOwnership() public virtual onlyOwner {
              _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);
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.0 (security/Pausable.sol)
      pragma solidity ^0.8.0;
      import "../utils/Context.sol";
      /**
       * @dev Contract module which allows children to implement an emergency stop
       * mechanism that can be triggered by an authorized account.
       *
       * This module is used through inheritance. It will make available the
       * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
       * the functions of your contract. Note that they will not be pausable by
       * simply including this module, only once the modifiers are put in place.
       */
      abstract contract Pausable is Context {
          /**
           * @dev Emitted when the pause is triggered by `account`.
           */
          event Paused(address account);
          /**
           * @dev Emitted when the pause is lifted by `account`.
           */
          event Unpaused(address account);
          bool private _paused;
          /**
           * @dev Initializes the contract in unpaused state.
           */
          constructor() {
              _paused = false;
          }
          /**
           * @dev Returns true if the contract is paused, and false otherwise.
           */
          function paused() public view virtual returns (bool) {
              return _paused;
          }
          /**
           * @dev Modifier to make a function callable only when the contract is not paused.
           *
           * Requirements:
           *
           * - The contract must not be paused.
           */
          modifier whenNotPaused() {
              require(!paused(), "Pausable: paused");
              _;
          }
          /**
           * @dev Modifier to make a function callable only when the contract is paused.
           *
           * Requirements:
           *
           * - The contract must be paused.
           */
          modifier whenPaused() {
              require(paused(), "Pausable: not paused");
              _;
          }
          /**
           * @dev Triggers stopped state.
           *
           * Requirements:
           *
           * - The contract must not be paused.
           */
          function _pause() internal virtual whenNotPaused {
              _paused = true;
              emit Paused(_msgSender());
          }
          /**
           * @dev Returns to normal state.
           *
           * Requirements:
           *
           * - The contract must be paused.
           */
          function _unpause() internal virtual whenPaused {
              _paused = false;
              emit Unpaused(_msgSender());
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.0 (utils/Context.sol)
      pragma solidity ^0.8.0;
      /**
       * @dev Provides information about the current execution context, including the
       * sender of the transaction and its data. While these are generally available
       * via msg.sender and msg.data, they should not be accessed in such a direct
       * manner, since when dealing with meta-transactions the account sending and
       * paying for execution may not be the actual sender (as far as an application
       * is concerned).
       *
       * This contract is only required for intermediate, library-like contracts.
       */
      abstract contract Context {
          function _msgSender() internal view virtual returns (address) {
              return msg.sender;
          }
          function _msgData() internal view virtual returns (bytes calldata) {
              return msg.data;
          }
      }
      

      File 3 of 3: RefuelBridgeImpl
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.4;
      import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
      import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
      import "../../interfaces/refuel.sol";
      import "../../helpers/errors.sol";
      import "../../ImplBase.sol";
      contract RefuelBridgeImpl is ImplBase, ReentrancyGuard {
          IRefuel public router;
          /**
          @notice Constructor sets the router address and registry address.
          */
          constructor(IRefuel _router, address _registry)
              ImplBase(_registry)
          {
              router = _router;
          }
          /**
          @notice function responsible for calling cross chain transfer using refuel bridge.
          @param _receiverAddress receivers address.
          @param _toChainId destination chain Id
          */
          function outboundTransferTo(
              uint256,
              address,
              address _receiverAddress,
              address,
              uint256 _toChainId,
              bytes calldata
          ) external payable override onlyRegistry nonReentrant {
              require(msg.value != 0, MovrErrors.VALUE_SHOULD_NOT_BE_ZERO);
              router.depositNativeToken{value: msg.value}(
                      _toChainId,
                      _receiverAddress
              );
          }
      }
      // 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);
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.1 (security/ReentrancyGuard.sol)
      pragma solidity ^0.8.0;
      /**
       * @dev Contract module that helps prevent reentrant calls to a function.
       *
       * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
       * available, which can be applied to functions to make sure there are no nested
       * (reentrant) calls to them.
       *
       * Note that because there is a single `nonReentrant` guard, functions marked as
       * `nonReentrant` may not call one another. This can be worked around by making
       * those functions `private`, and then adding `external` `nonReentrant` entry
       * points to them.
       *
       * TIP: If you would like to learn more about reentrancy and alternative ways
       * to protect against it, check out our blog post
       * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
       */
      abstract contract ReentrancyGuard {
          // Booleans are more expensive than uint256 or any type that takes up a full
          // word because each write operation emits an extra SLOAD to first read the
          // slot's contents, replace the bits taken up by the boolean, and then write
          // back. This is the compiler's defense against contract upgrades and
          // pointer aliasing, and it cannot be disabled.
          // The values being non-zero value makes deployment a bit more expensive,
          // but in exchange the refund on every call to nonReentrant will be lower in
          // amount. Since refunds are capped to a percentage of the total
          // transaction's gas, it is best to keep them low in cases like this one, to
          // increase the likelihood of the full refund coming into effect.
          uint256 private constant _NOT_ENTERED = 1;
          uint256 private constant _ENTERED = 2;
          uint256 private _status;
          constructor() {
              _status = _NOT_ENTERED;
          }
          /**
           * @dev Prevents a contract from calling itself, directly or indirectly.
           * Calling a `nonReentrant` function from another `nonReentrant`
           * function is not supported. It is possible to prevent this from happening
           * by making the `nonReentrant` function external, and making it call a
           * `private` function that does the actual work.
           */
          modifier nonReentrant() {
              // On the first call to nonReentrant, _notEntered will be true
              require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
              // Any calls to nonReentrant after this point will fail
              _status = _ENTERED;
              _;
              // By storing the original value once again, a refund is triggered (see
              // https://eips.ethereum.org/EIPS/eip-2200)
              _status = _NOT_ENTERED;
          }
      }
      // SPDX-License-Identifier: Apache-2.0
      pragma solidity >=0.8.0;
      interface IRefuel {
          function depositNativeToken(uint256 destinationChainId, address _to) external payable;
      }
      // SPDX-License-Identifier: MIT
      pragma solidity >=0.8.0;
      library MovrErrors {
          string internal constant ADDRESS_0_PROVIDED = "ADDRESS_0_PROVIDED";
          string internal constant EMPTY_INPUT = "EMPTY_INPUT";
          string internal constant LENGTH_MISMATCH = "LENGTH_MISMATCH";
          string internal constant INVALID_VALUE = "INVALID_VALUE";
          string internal constant INVALID_AMT = "INVALID_AMT";
          string internal constant IMPL_NOT_FOUND = "IMPL_NOT_FOUND";
          string internal constant ROUTE_NOT_FOUND = "ROUTE_NOT_FOUND";
          string internal constant IMPL_NOT_ALLOWED = "IMPL_NOT_ALLOWED";
          string internal constant ROUTE_NOT_ALLOWED = "ROUTE_NOT_ALLOWED";
          string internal constant INVALID_CHAIN_DATA = "INVALID_CHAIN_DATA";
          string internal constant CHAIN_NOT_SUPPORTED = "CHAIN_NOT_SUPPORTED";
          string internal constant TOKEN_NOT_SUPPORTED = "TOKEN_NOT_SUPPORTED";
          string internal constant NOT_IMPLEMENTED = "NOT_IMPLEMENTED";
          string internal constant INVALID_SENDER = "INVALID_SENDER";
          string internal constant INVALID_BRIDGE_ID = "INVALID_BRIDGE_ID";
          string internal constant MIDDLEWARE_ACTION_FAILED =
              "MIDDLEWARE_ACTION_FAILED";
          string internal constant VALUE_SHOULD_BE_ZERO = "VALUE_SHOULD_BE_ZERO";
          string internal constant VALUE_SHOULD_NOT_BE_ZERO = "VALUE_SHOULD_NOT_BE_ZERO";
          string internal constant VALUE_NOT_ENOUGH = "VALUE_NOT_ENOUGH";
          string internal constant VALUE_NOT_EQUAL_TO_AMOUNT = "VALUE_NOT_EQUAL_TO_AMOUNT";
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.4;
      import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
      import "@openzeppelin/contracts/access/Ownable.sol";
      import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
      import "./helpers/errors.sol";
      /**
      @title Abstract Implementation Contract.
      @notice All Bridge Implementation will follow this interface. 
      */
      abstract contract ImplBase is Ownable {
          using SafeERC20 for IERC20;
          address public registry;
          address public constant NATIVE_TOKEN_ADDRESS =
              address(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE);
          event UpdateRegistryAddress(address indexed registryAddress);
          constructor(address _registry) Ownable() {
              registry = _registry;
          }
          modifier onlyRegistry() {
              require(msg.sender == registry, MovrErrors.INVALID_SENDER);
              _;
          }
          function updateRegistryAddress(address newRegistry) external onlyOwner {
              registry = newRegistry;
              emit UpdateRegistryAddress(newRegistry);
          }
          function rescueFunds(
              address token,
              address userAddress,
              uint256 amount
          ) external onlyOwner {
              IERC20(token).safeTransfer(userAddress, amount);
          }
          function rescueEther(
              address payable userAddress,
              uint256 amount
          ) external onlyOwner {
              userAddress.transfer(amount);
          }
          function outboundTransferTo(
              uint256 _amount,
              address _from,
              address _receiverAddress,
              address _token,
              uint256 _toChainId,
              bytes memory _extraData
          ) external payable virtual;
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)
      pragma solidity ^0.8.0;
      import "../utils/Context.sol";
      /**
       * @dev Contract module which provides a basic access control mechanism, where
       * there is an account (an owner) that can be granted exclusive access to
       * specific functions.
       *
       * By default, the owner account will be the one that deploys the contract. This
       * can later be changed with {transferOwnership}.
       *
       * This module is used through inheritance. It will make available the modifier
       * `onlyOwner`, which can be applied to your functions to restrict their use to
       * the owner.
       */
      abstract contract Ownable is Context {
          address private _owner;
          event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
          /**
           * @dev Initializes the contract setting the deployer as the initial owner.
           */
          constructor() {
              _transferOwnership(_msgSender());
          }
          /**
           * @dev Returns the address of the current owner.
           */
          function owner() public view virtual returns (address) {
              return _owner;
          }
          /**
           * @dev Throws if called by any account other than the owner.
           */
          modifier onlyOwner() {
              require(owner() == _msgSender(), "Ownable: caller is not the owner");
              _;
          }
          /**
           * @dev Leaves the contract without owner. It will not be possible to call
           * `onlyOwner` functions anymore. Can only be called by the current owner.
           *
           * NOTE: Renouncing ownership will leave the contract without an owner,
           * thereby removing any functionality that is only available to the owner.
           */
          function renounceOwnership() public virtual onlyOwner {
              _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);
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)
      pragma solidity ^0.8.0;
      import "../IERC20.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));
              }
          }
          /**
           * @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");
              }
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
      pragma solidity ^0.8.0;
      /**
       * @dev Provides information about the current execution context, including the
       * sender of the transaction and its data. While these are generally available
       * via msg.sender and msg.data, they should not be accessed in such a direct
       * manner, since when dealing with meta-transactions the account sending and
       * paying for execution may not be the actual sender (as far as an application
       * is concerned).
       *
       * This contract is only required for intermediate, library-like contracts.
       */
      abstract contract Context {
          function _msgSender() internal view virtual returns (address) {
              return msg.sender;
          }
          function _msgData() internal view virtual returns (bytes calldata) {
              return msg.data;
          }
      }
      // SPDX-License-Identifier: MIT
      // OpenZeppelin Contracts (last updated v4.5.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 functionCall(target, data, "Address: low-level call failed");
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
           * `errorMessage` as a fallback revert reason when `target` reverts.
           *
           * _Available since v3.1._
           */
          function functionCall(
              address target,
              bytes memory data,
              string memory errorMessage
          ) internal returns (bytes memory) {
              return functionCallWithValue(target, data, 0, errorMessage);
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but also transferring `value` wei to `target`.
           *
           * Requirements:
           *
           * - the calling contract must have an ETH balance of at least `value`.
           * - the called Solidity function must be `payable`.
           *
           * _Available since v3.1._
           */
          function functionCallWithValue(
              address target,
              bytes memory data,
              uint256 value
          ) internal returns (bytes memory) {
              return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
          }
          /**
           * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
           * with `errorMessage` as a fallback revert reason when `target` reverts.
           *
           * _Available since v3.1._
           */
          function functionCallWithValue(
              address target,
              bytes memory data,
              uint256 value,
              string memory errorMessage
          ) internal returns (bytes memory) {
              require(address(this).balance >= value, "Address: insufficient balance for call");
              require(isContract(target), "Address: call to non-contract");
              (bool success, bytes memory returndata) = target.call{value: value}(data);
              return verifyCallResult(success, returndata, errorMessage);
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but performing a static call.
           *
           * _Available since v3.3._
           */
          function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
              return functionStaticCall(target, data, "Address: low-level static call failed");
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
           * but performing a static call.
           *
           * _Available since v3.3._
           */
          function functionStaticCall(
              address target,
              bytes memory data,
              string memory errorMessage
          ) internal view returns (bytes memory) {
              require(isContract(target), "Address: static call to non-contract");
              (bool success, bytes memory returndata) = target.staticcall(data);
              return verifyCallResult(success, returndata, errorMessage);
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but performing a delegate call.
           *
           * _Available since v3.4._
           */
          function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
              return functionDelegateCall(target, data, "Address: low-level delegate call failed");
          }
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
           * but performing a delegate call.
           *
           * _Available since v3.4._
           */
          function functionDelegateCall(
              address target,
              bytes memory data,
              string memory errorMessage
          ) internal returns (bytes memory) {
              require(isContract(target), "Address: delegate call to non-contract");
              (bool success, bytes memory returndata) = target.delegatecall(data);
              return verifyCallResult(success, returndata, errorMessage);
          }
          /**
           * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
           * revert reason using the provided one.
           *
           * _Available since v4.3._
           */
          function verifyCallResult(
              bool success,
              bytes memory returndata,
              string memory errorMessage
          ) internal pure returns (bytes memory) {
              if (success) {
                  return returndata;
              } else {
                  // Look for revert reason and bubble it up if present
                  if (returndata.length > 0) {
                      // The easiest way to bubble the revert reason is using memory via assembly
                      assembly {
                          let returndata_size := mload(returndata)
                          revert(add(32, returndata), returndata_size)
                      }
                  } else {
                      revert(errorMessage);
                  }
              }
          }
      }