// SPDX-License-Identifier: UNLICENSED pragma solidity =0.8.18; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; import "../../SomaAccessControl/utils/AccessibleUpgradeable.sol"; import "./ITokenRecoveryUpgradeable.sol"; /** * @notice Implementation of the {ITokenRecoveryUpgradeable} interface. */ abstract contract TokenRecoveryUpgradeable is ITokenRecoveryUpgradeable, AccessibleUpgradeable { using EnumerableSet for EnumerableSet.AddressSet; using SafeERC20 for IERC20; /** * @notice Initializer for the extended contracts. */ function __TokenRecovery__init() internal onlyInitializing { __TokenRecovery__init(new address[](0)); } /** * @notice Initializer for the extended contracts. */ function __TokenRecovery__init(address[] memory disabledTokens) internal onlyInitializing { __ERC165_init_unchained(); __Context_init_unchained(); __SomaContract_init_unchained(); __Multicall_init_unchained(); __Pausable_init_unchained(); __Accessible_init_unchained(); __TokenRecovery__init_unchained(disabledTokens); } /** * @notice Unchained initializer. */ function __TokenRecovery__init_unchained(address[] memory disabledTokens) internal onlyInitializing { for (uint256 i; i < disabledTokens.length; ++i) { _disabledTokens.add(disabledTokens[i]); } } /** * @inheritdoc ITokenRecoveryUpgradeable */ bytes32 public constant override TOKEN_RECOVERY_ROLE = keccak256("TokenRecovery.TOKEN_RECOVERY_ROLE"); EnumerableSet.AddressSet private _disabledTokens; /** * @notice Checks if TokenRecoveryUpgradeable inherits a given contract interface. * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { return interfaceId == type(ITokenRecoveryUpgradeable).interfaceId || super.supportsInterface(interfaceId); } /** * @inheritdoc ITokenRecoveryUpgradeable */ function recoverTokens(address token, address to, uint256 amount) external override onlyRole(TOKEN_RECOVERY_ROLE) { require(!_disabledTokens.contains(token), "TokenRecovery: INVALID_TOKEN"); IERC20(token).safeTransfer(to, amount); emit TokensRecovered(token, to, amount, _msgSender()); } function _disableTokenRecovery(address token) internal { _disabledTokens.add(token); } function _enableTokenRecovery(address token) internal { _disabledTokens.remove(token); } uint256[50] private __gap; }