V4UtilsRouter

0xcb3d2a42022741b06f9b38459e3dd1ee9a64d129

Verification
Verified
v0.8.26+commit.8a97fa7a
Type
Contract
14,558 bytes
ABI entries
56
12 read · 18 write
License
none

Contract information

Address
0xcb3d2a42022741b06f9b38459e3dd1ee9a64d129
Chain
Robinhood Chain (4663)
Compiler
v0.8.26+commit.8a97fa7a
Optimization
Enabled
Creation tx
0xf2305c5309…0fe4c9ba05

Token

Not a token

This contract does not expose ERC-20 metadata.

Read contract (12)

ADMIN_ROLE()bytes32
AUTOMATION_OPERATOR_ROLE()bytes32
DEFAULT_ADMIN_ROLE()bytes32
DOMAIN_SEPARATOR()bytes32
WITHDRAWER_ROLE()bytes32
getRoleAdmin(bytes32)bytes32
hasRole(bytes32, address)bool
implementationLocked()bool
isOrderCancelled(bytes)bool
paused()bool
supportsInterface(bytes4)bool
v4UtilsImplementations(address)address

Events (9)

CancelOrderDelegatedCallImplementationSetInitializedPausedRoleAdminChangedRoleGrantedRoleRevokedUnpaused

ABI

[
  {
    "inputs": [],
    "stateMutability": "nonpayable",
    "type": "constructor"
  },
  {
    "inputs": [],
    "name": "AccessControlBadConfirmation",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "account",
        "type": "address"
      },
      {
        "internalType": "bytes32",
        "name": "neededRole",
        "type": "bytes32"
      }
    ],
    "name": "AccessControlUnauthorizedAccount",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "bytes",
        "name": "data",
        "type": "bytes"
      }
    ],
    "name": "DelegateCallFailed",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "ECDSAInvalidSignature",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "length",
        "type": "uint256"
      }
    ],
    "name": "ECDSAInvalidSignatureLength",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "bytes32",
        "name": "s",
        "type": "bytes32"
      }
    ],
    "name": "ECDSAInvalidSignatureS",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "EnforcedPause",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "ExpectedPause",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "ImplementationLocked",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "InvalidInitialization",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "InvalidSelector",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "NotInitializing",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "token",
        "type": "address"
      }
    ],
    "name": "SafeERC20FailedOperation",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "posm",
        "type": "address"
      }
    ],
    "name": "UnsupportedPositionManager",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "ZeroAddress",
    "type": "error"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "address",
        "name": "user",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "bytes",
        "name": "order",
        "type": "bytes"
      },
      {
        "indexed": false,
        "internalType": "bytes",
        "name": "signature",
        "type": "bytes"
      }
    ],
    "name": "CancelOrder",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "target",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "bytes4",
        "name": "selector",
        "type": "bytes4"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "posm",
        "type": "address"
      }
    ],
    "name": "DelegatedCall",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "posm",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "implementation",
        "type": "address"
      }
    ],
    "name": "ImplementationSet",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "uint64",
        "name": "version",
        "type": "uint64"
      }
    ],
    "name": "Initialized",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "address",
        "name": "account",
        "type": "address"
      }
    ],
    "name": "Paused",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "bytes32",
        "name": "role",
        "type": "bytes32"
      },
      {
        "indexed": true,
        "internalType": "bytes32",
        "name": "previousAdminRole",
        "type": "bytes32"
      },
      {
        "indexed": true,
        "internalType": "bytes32",
        "name": "newAdminRole",
        "type": "bytes32"
      }
    ],
    "name": "RoleAdminChanged",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "bytes32",
        "name": "role",
        "type": "bytes32"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "account",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "sender",
        "type": "address"
      }
    ],
    "name": "RoleGranted",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "bytes32",
        "name": "role",
        "type": "bytes32"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "account",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "sender",
        "type": "address"
      }
    ],
    "name": "RoleRevoked",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "address",
        "name": "account",
        "type": "address"
      }
    ],
    "name": "Unpaused",
    "type": "event"
  },
  {
    "inputs": [],
    "name": "ADMIN_ROLE",
    "outputs": [
      {
        "internalType": "bytes32",
        "name": "",
        "type": "bytes32"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "AUTOMATION_OPERATOR_ROLE",
    "outputs": [
      {
        "internalType": "bytes32",
        "name": "",
        "type": "bytes32"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "DEFAULT_ADMIN_ROLE",
    "outputs": [
      {
        "internalType": "bytes32",
        "name": "",
        "type": "bytes32"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "DOMAIN_SEPARATOR",
    "outputs": [
      {
        "internalType": "bytes32",
        "name": "",
        "type": "bytes32"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "WITHDRAWER_ROLE",
    "outputs": [
      {
        "internalType": "bytes32",
        "name": "",
        "type": "bytes32"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "bytes",
        "name": "abiEncodedUserOrder",
        "type": "bytes"
      },
      {
        "internalType": "bytes",
        "name": "orderSignature",
        "type": "bytes"
      }
    ],
    "name": "cancelOrder",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "posm",
        "type": "address"
      },
      {
        "internalType": "bytes",
        "name": "data",
        "type": "bytes"
      }
    ],
    "name": "execute",
    "outputs": [],
    "stateMutability": "payable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "posm",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "tokenId",
        "type": "uint256"
      },
      {
        "internalType": "bytes",
        "name": "instructionData",
        "type": "bytes"
      },
      {
        "internalType": "bytes",
        "name": "abiEncodedUserOrder",
        "type": "bytes"
      },
      {
        "internalType": "bytes",
        "name": "orderSignature",
        "type": "bytes"
      }
    ],
    "name": "executeAutomation",
    "outputs": [],
    "stateMutability": "payable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "bytes32",
        "name": "role",
        "type": "bytes32"
      }
    ],
    "name": "getRoleAdmin",
    "outputs": [
      {
        "internalType": "bytes32",
        "name": "",
        "type": "bytes32"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "_address",
        "type": "address"
      }
    ],
    "name": "grantOperatorRole",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "bytes32",
        "name": "role",
        "type": "bytes32"
      },
      {
        "internalType": "address",
        "name": "account",
        "type": "address"
      }
    ],
    "name": "grantRole",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "bytes32",
        "name": "role",
        "type": "bytes32"
      },
      {
        "internalType": "address",
        "name": "account",
        "type": "address"
      }
    ],
    "name": "hasRole",
    "outputs": [
      {
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "implementationLocked",
    "outputs": [
      {
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "_swapRouter",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "_feeTaker",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "_weth",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "admin",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "withdrawer",
        "type": "address"
      }
    ],
    "name": "initialize",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "bytes",
        "name": "orderSignature",
        "type": "bytes"
      }
    ],
    "name": "isOrderCancelled",
    "outputs": [
      {
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "lockImplementation",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "operator",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "from",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "tokenId",
        "type": "uint256"
      },
      {
        "internalType": "bytes",
        "name": "data",
        "type": "bytes"
      }
    ],
    "name": "onERC721Received",
    "outputs": [
      {
        "internalType": "bytes4",
        "name": "",
        "type": "bytes4"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "pause",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "paused",
    "outputs": [
      {
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "bytes32",
        "name": "role",
        "type": "bytes32"
      },
      {
        "internalType": "address",
        "name": "callerConfirmation",
        "type": "address"
      }
    ],
    "name": "renounceRole",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "_address",
        "type": "address"
      }
    ],
    "name": "revokeOperatorRole",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "bytes32",
        "name": "role",
        "type": "bytes32"
      },
      {
        "internalType": "address",
        "name": "account",
        "type": "address"
      }
    ],
    "name": "revokeRole",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "_feeTaker",
        "type": "address"
      }
    ],
    "name": "setFeeTaker",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "posm",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "implementation",
        "type": "address"
      }
    ],
    "name": "setImplementation",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "bytes4",
        "name": "interfaceId",
        "type": "bytes4"
      }
    ],
    "name": "supportsInterface",
    "outputs": [
      {
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "unpause",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "posm",
        "type": "address"
      }
    ],
    "name": "v4UtilsImplementations",
    "outputs": [
      {
        "internalType": "address",
        "name": "implementation",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "contract IERC20[]",
        "name": "tokens",
        "type": "address[]"
      },
      {
        "internalType": "address",
        "name": "to",
        "type": "address"
      }
    ],
    "name": "withdrawERC20",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "contract IERC721",
        "name": "token",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "tokenId",
        "type": "uint256"
      },
      {
        "internalType": "address",
        "name": "to",
        "type": "address"
      }
    ],
    "name": "withdrawERC721",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "to",
        "type": "address"
      }
    ],
    "name": "withdrawNative",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "stateMutability": "payable",
    "type": "receive"
  }
]

Source code

// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.8.26;

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

import { IERC721Receiver } from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
import { IERC721 } from "@openzeppelin/contracts/token/ERC721/IERC721.sol";

import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { AccessControl } from "@openzeppelin/contracts/access/AccessControl.sol";
import { Initializable } from "@openzeppelin/contracts/proxy/utils/Initializable.sol";
import { Pausable } from "@openzeppelin/contracts/utils/Pausable.sol";

import { SharedStorage } from "./libraries/Storage.sol";
import { FeeType } from "./libraries/FeeConstants.sol";

import { EIP712 } from "./base/EIP712.sol";
import { Withdrawable } from "./base/Withdrawable.sol";

/**
 * @title V4UtilsRouter
 * @notice Router contract that delegates calls to the appropriate V4Utils implementation
 * based on the position manager (posm) address provided
 */
contract V4UtilsRouter is AccessControl, Pausable, Initializable, EIP712, IERC721Receiver, Withdrawable {
  bytes32 public constant WITHDRAWER_ROLE = keccak256("WITHDRAWER_ROLE");
  bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE");
  bytes32 public constant AUTOMATION_OPERATOR_ROLE = keccak256("AUTOMATION_OPERATOR_ROLE");

  /// @notice Mapping from position manager address to V4Utils implementation address
  mapping(address posm => address implementation) public v4UtilsImplementations;
  mapping(bytes32 => bool) _cancelledOrder;
  bool public implementationLocked;

  error ZeroAddress();
  error ImplementationLocked();
  error UnsupportedPositionManager(address posm);
  error DelegateCallFailed(bytes data);
  error InvalidSelector();

  event ImplementationSet(address indexed posm, address indexed implementation);
  event DelegatedCall(address indexed target, bytes4 indexed selector, address indexed posm);
  event CancelOrder(address user, bytes order, bytes signature);

  constructor() EIP712("V3AutomationOrder", "5.0") { }

  /**
   * @notice Initialize the contract with the swap router, fee taker, WETH address, admin, and withdrawer
   * @param _swapRouter The address of the swap router
   * @param _feeTaker The address that will take fees
   * @param _weth The address of the WETH token
   * @param admin The address of the admin
   * @param withdrawer The address that can withdraw tokens
   */
  function initialize(address _swapRouter, address _feeTaker, address _weth, address admin, address withdrawer)
    public
    initializer
  {
    require(_feeTaker != address(0), ZeroAddress());
    SharedStorage.Storage storage ss = SharedStorage.getStorage();
    ss.swapRouter = _swapRouter;
    ss.feeTaker = _feeTaker;
    ss.weth = _weth;
    ss.maxFeeX64[uint8(FeeType.GAS_FEE)] = 5_534_023_222_112_865_280; // 30%
    ss.maxFeeX64[uint8(FeeType.PROTOCOL_FEE)] = 5_534_023_222_112_865_280; // 30%
    ss.maxFeeX64[uint8(FeeType.PERFORMANCE_FEE)] = 3_689_348_814_741_910_528; // 20%
    _grantRole(ADMIN_ROLE, admin);
    _grantRole(DEFAULT_ADMIN_ROLE, admin);
    _grantRole(WITHDRAWER_ROLE, withdrawer);
    _grantRole(DEFAULT_ADMIN_ROLE, withdrawer);
  }

  /**
   * @notice Set the V4Utils implementation for a specific position manager
   * @param posm The position manager address
   * @param implementation The V4Utils implementation address
   */
  function setImplementation(address posm, address implementation) external onlyRole(ADMIN_ROLE) {
    require(!implementationLocked, ImplementationLocked());
    require(posm != address(0) && implementation != address(0), ZeroAddress());
    v4UtilsImplementations[posm] = implementation;
    emit ImplementationSet(posm, implementation);
  }

  /**
   * @notice Execute a function call on the appropriate V4Utils implementation
   * @param posm The position manager address to determine which V4Utils to use
   * @param data The encoded function call data including selector
   */
  function execute(address posm, bytes calldata data) external payable whenNotPaused {
    address implementation = v4UtilsImplementations[posm];
    require(implementation != address(0), UnsupportedPositionManager(posm));

    // Extract selector from data for event emission
    bytes4 selector;
    assembly {
      selector := calldataload(data.offset)
    }
    // Make sure we don't delegatecall to onERC721Received
    require(selector != IERC721Receiver.onERC721Received.selector, InvalidSelector());

    _delegateCall(implementation, data, posm, selector);
  }

  /**
   * @notice Execute an automation order by transferring the NFT to this contract
   * @param posm The position manager address
   * @param tokenId The NFT identifier
   * @param instructionData Additional data for the instruction
   * @param abiEncodedUserOrder The user order data encoded in ABI format
   * @param orderSignature The signature of the user order
   */
  function executeAutomation(
    address posm,
    uint256 tokenId,
    bytes calldata instructionData,
    bytes calldata abiEncodedUserOrder,
    bytes calldata orderSignature
  ) external payable onlyRole(AUTOMATION_OPERATOR_ROLE) whenNotPaused {
    address positionOwner = IERC721(posm).ownerOf(tokenId);
    _validateOrder(abiEncodedUserOrder, orderSignature, positionOwner);
    IERC721(posm).safeTransferFrom(positionOwner, address(this), tokenId, instructionData);
  }

  /**
   * @notice ERC721 callback function that routes to the appropriate V4Utils implementation
   * @param operator The address which called `safeTransferFrom`
   * @param from The address which previously owned the token
   * @param tokenId The NFT identifier which is being transferred
   * @param data Additional data with no specified format
   * @return bytes4 The selector to confirm token transfer
   */
  function onERC721Received(address operator, address from, uint256 tokenId, bytes calldata data)
    external
    override
    returns (bytes4)
  {
    address posm = msg.sender; // The position manager is the caller
    address implementation = v4UtilsImplementations[posm];
    require(implementation != address(0), UnsupportedPositionManager(posm));

    bytes memory callData =
      abi.encodeWithSelector(IERC721Receiver.onERC721Received.selector, operator, from, tokenId, data);

    (bool success, bytes memory result) = implementation.delegatecall(callData);
    require(success, DelegateCallFailed(result));

    emit DelegatedCall(implementation, IERC721Receiver.onERC721Received.selector, posm);

    return abi.decode(result, (bytes4));
  }

  /**
   * @notice Execute a delegate call and handle errors
   * @param target The target contract address
   * @param data The call data to execute (includes selector)
   * @param posm The position manager address for event emission
   * @param selector The function selector for event emission
   */
  function _delegateCall(address target, bytes calldata data, address posm, bytes4 selector) internal {
    (bool success, bytes memory result) = target.delegatecall(data);
    if (!success) {
      assembly {
        revert(add(result, 32), mload(result)) // Revert with the original reason
      }
    }

    emit DelegatedCall(target, selector, posm);
  }

  function _validateOrder(bytes memory abiEncodedUserOrder, bytes memory orderSignature, address actor) internal view {
    address userAddress = _recover(abiEncodedUserOrder, orderSignature);
    require(userAddress == actor);
    require(!_cancelledOrder[keccak256(orderSignature)]);
  }

  /**
   * @notice Cancel an order by marking it as cancelled
   * @param abiEncodedUserOrder The user order data encoded in ABI format
   * @param orderSignature The signature of the user order
   */
  function cancelOrder(bytes calldata abiEncodedUserOrder, bytes calldata orderSignature) external {
    _validateOrder(abiEncodedUserOrder, orderSignature, _msgSender());
    _cancelledOrder[keccak256(orderSignature)] = true;
    emit CancelOrder(_msgSender(), abiEncodedUserOrder, orderSignature);
  }

  /// @notice Check if an order has been cancelled
  /// @param orderSignature The signature of the order to check
  /// @return bool True if the order is cancelled, false otherwise
  function isOrderCancelled(bytes calldata orderSignature) external view returns (bool) {
    return _cancelledOrder[keccak256(orderSignature)];
  }

  /// @notice grant operator role to the address
  /// @param _address The address to which the operator role is granted
  function grantOperatorRole(address _address) external onlyRole(DEFAULT_ADMIN_ROLE) {
    grantRole(AUTOMATION_OPERATOR_ROLE, _address);
  }

  /// @notice revoke operator role from the address
  /// @param _address The address from which the operator role is revoked
  function revokeOperatorRole(address _address) external onlyRole(DEFAULT_ADMIN_ROLE) {
    revokeRole(AUTOMATION_OPERATOR_ROLE, _address);
  }

  /// @notice Set the fee taker address
  /// @param _feeTaker The fee taker address
  function setFeeTaker(address _feeTaker) external onlyRole(ADMIN_ROLE) {
    SharedStorage.getStorage().feeTaker = _feeTaker;
  }

  function pause() external onlyRole(ADMIN_ROLE) {
    _pause();
  }

  function unpause() external onlyRole(ADMIN_ROLE) {
    _unpause();
  }

  function withdrawERC20(IERC20[] calldata tokens, address to) public virtual override onlyRole(WITHDRAWER_ROLE) {
    super.withdrawERC20(tokens, to);
  }

  function withdrawNative(address to) public virtual override onlyRole(WITHDRAWER_ROLE) {
    super.withdrawNative(to);
  }

  function withdrawERC721(IERC721 token, uint256 tokenId, address to)
    public
    virtual
    override
    onlyRole(WITHDRAWER_ROLE)
  {
    super.withdrawERC721(token, tokenId, to);
  }

  /**
   * @notice Lock the implementation to prevent further changes
   * This function can only be called by an account with the ADMIN_ROLE
   */
  function lockImplementation() external onlyRole(ADMIN_ROLE) {
    implementationLocked = true;
  }

  /**
   * @notice Allow contract to receive ETH
   */
  receive() external payable { }
}
Chain explorer2819msChain node84ms