// SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.18; import "@openzeppelin/contracts/utils/Context.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; /** * @title MutableERC2771Context * @notice Context variant with ERC2771 support (with mutable `_trustedForwarder`). * @dev Based on OpenZeppelin ERC2771Context contract. */ abstract contract MutableERC2771Context is Context, Ownable { address private _trustedForwarder; event TrustedForwarderSet(address indexed previousForwarder, address indexed newForwarder); constructor(address trustedForwarder_) { _setTrustedForwarder(trustedForwarder_); } function setTrustedForwarder(address trustedForwarder) external virtual onlyOwner { _setTrustedForwarder(trustedForwarder); } function _setTrustedForwarder(address trustedForwarder) internal virtual { address previousForwarder = _trustedForwarder; _trustedForwarder = trustedForwarder; emit TrustedForwarderSet(previousForwarder, trustedForwarder); } function isTrustedForwarder(address forwarder) public view virtual returns (bool) { return forwarder == _trustedForwarder; } function _msgSender() internal view virtual override returns (address sender) { if (isTrustedForwarder(msg.sender)) { // The assembly code is more direct than the Solidity version using `abi.decode`. /// @solidity memory-safe-assembly assembly { sender := shr(96, calldataload(sub(calldatasize(), 20))) } } else { return super._msgSender(); } } function _msgData() internal view virtual override returns (bytes calldata) { if (isTrustedForwarder(msg.sender)) { return msg.data[:msg.data.length - 20]; } else { return super._msgData(); } } }