// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.19; import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; /// @title Bannable /// @dev Contract module which allows an owner to ban or unban addresses. /// Utilizes OpenZeppelin's OwnableUpgradeable for ownership management. abstract contract Bannable is OwnableUpgradeable { using EnumerableSet for EnumerableSet.AddressSet; // ------------------------ // STATE VARIABLES // ------------------------ // @dev DEPRECATED: This storage slot is no longer used as of ERC7201 migration // @dev Kept for upgrade compatibility - do not use EnumerableSet.AddressSet private banList; // ------------------------ // ERC7201 STORAGE // ------------------------ /// @custom:storage-location erc7201:bannable.v1.banlist struct BanListStorage { EnumerableSet.AddressSet banList; } // keccak256(abi.encode(uint256(keccak256("bannable.v1.banlist")) - 1)) & ~bytes32(uint256(0xff)) bytes32 private constant BAN_LIST_STORAGE_LOCATION = 0xc570069450541cbafa33b546085c421153a2f00786cad22bec4f4eb838578e00; function _getBanListStorage() private pure returns (BanListStorage storage $) { assembly { $.slot := BAN_LIST_STORAGE_LOCATION } } // ------------------------ // EVENTS // ------------------------ event Banned(address indexed account); event UnBanned(address indexed account); // ------------------------ // ERRORS // ------------------------ error AccountBanned(); error AlreadyBanned(); error NotBanned(); // ------------------------ // MODIFIERS // ------------------------ /// @notice Modifier that reverts if the account is banned. /// @param account The address to check for a ban. modifier notBanned(address account) { if (isBanned(account)) { revert AccountBanned(); } _; } // ------------------------ // FUNCTIONS // ------------------------ /// @notice Bans a specified account. /// @dev Can only be called by the contract owner. /// @param account The address to be banned. function ban(address account) external onlyOwner { BanListStorage storage $ = _getBanListStorage(); bool success = $.banList.add(account); if (!success) { revert AlreadyBanned(); } emit Banned(account); } /// @notice Unbans a specified account. /// @dev Can only be called by the contract owner. /// @param account The address to be unbanned. function unban(address account) external onlyOwner { BanListStorage storage $ = _getBanListStorage(); bool success = $.banList.remove(account); if (!success) { revert NotBanned(); } emit UnBanned(account); } /// @notice Retrieves the banned address at a specific index. /// @param index The index of the banned address in the list. /// @return The address of the banned account. function getBan(uint256 index) external view returns (address) { BanListStorage storage $ = _getBanListStorage(); return $.banList.at(index); } /// @notice Lists all banned addresses. /// @return An array of banned addresses. function listBanned() external view returns (address[] memory) { BanListStorage storage $ = _getBanListStorage(); return $.banList.values(); } /// @notice Checks if an account is banned. /// @param account The address to check. /// @return True if the account is banned, false otherwise. function isBanned(address account) public view returns (bool) { BanListStorage storage $ = _getBanListStorage(); return $.banList.contains(account); } /// @notice Bans a list of accounts. /// @param accounts The addresses to ban. /// @dev Can only be called by the contract owner. function batchBan(address[] calldata accounts) external onlyOwner { BanListStorage storage $ = _getBanListStorage(); uint256 length = accounts.length; for (uint256 i; i < length;) { if (!$.banList.contains(accounts[i])) { $.banList.add(accounts[i]); emit Banned(accounts[i]); } unchecked { ++i; } } } }