// SPDX-License-Identifier: UNLICENSED pragma solidity =0.8.18; import "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol"; import "../../SomaAccessControl/utils/AccessibleUpgradeable.sol"; import "./INonTransferable.sol"; contract NonTransferable is INonTransferable, ERC20Upgradeable, AccessibleUpgradeable { /** * @notice Initializer for extended contracts. */ function __NonTransferable_init() internal onlyInitializing { __Accessible_init_unchained(); __SomaContract_init_unchained(); __ERC165_init_unchained(); __Context_init_unchained(); __Multicall_init_unchained(); __Pausable_init_unchained(); __NonTransferable_init_unchained(); } /** * @notice Unchained initializer. */ function __NonTransferable_init_unchained() internal onlyInitializing { __NonTransferable_init_unchained(new address[](0)); } /** * @notice Unchained initializer. */ function __NonTransferable_init_unchained(address[] memory whitelistAccounts) internal onlyInitializing { LOCAL_WHITELIST_ROLE = keccak256(abi.encodePacked(address(this), GLOBAL_WHITELIST_ROLE)); for (uint256 i = 0; i < whitelistAccounts.length; ++i) { _whitelist(whitelistAccounts[i]); } } /** * @notice Returns the GLOBAL_WHITELIST_ROLE. */ bytes32 public constant GLOBAL_WHITELIST_ROLE = keccak256("NonTransferable.GLOBAL_WHITELIST_ROLE"); /** * @notice Returns the LOCAL_WHITELIST_ROLE. */ bytes32 public LOCAL_WHITELIST_ROLE; /** * @notice Modifier to restrict a function caller to accounts that have the GLOBAL_WHITELIST_ROLE or LOCAL_WHITELIST_ROLE. */ modifier onlyWhitelistRole() { address sender = _msgSender(); require( hasRole(LOCAL_WHITELIST_ROLE, sender) || hasRole(GLOBAL_WHITELIST_ROLE, sender), "NonTransferable: UNAUTHORIZED" ); _; } mapping(address => bool) $whitelisted; /** * @notice Checks if NonTransferable inherits a given contract interface. * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { return interfaceId == type(INonTransferable).interfaceId || super.supportsInterface(interfaceId); } /** * @inheritdoc INonTransferable */ function whitelisted(address account) public view virtual override returns (bool) { return $whitelisted[account]; } /** * @inheritdoc INonTransferable */ function whitelist(address account) external override onlyWhitelistRole { _whitelist(account); } /** * @inheritdoc INonTransferable */ function revokeWhitelist(address account) external override onlyWhitelistRole { _revokeWhitelist(account); } function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual override(ERC20Upgradeable) { require( // allowing minting and burning from == address(0) || to == address(0) || whitelisted(from) || whitelisted(to), "NonTransferable: NON_TRANSFERABLE" ); return super._beforeTokenTransfer(from, to, amount); } function _whitelist(address account) internal virtual { require(!$whitelisted[account], "NonTransferable: ALREADY_WHITELISTED"); $whitelisted[account] = true; emit Whitelisted(account, _msgSender()); } function _revokeWhitelist(address account) internal virtual { require($whitelisted[account], "NonTransferable: NOT_WHITELISTED"); delete $whitelisted[account]; emit WhitelistedRevoked(account, _msgSender()); } }