creation_sourcecode: {{
    "language": "Solidity",
    "sources": {
      "src/KycManager.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.19;\n\nimport \"openzeppelin-contracts/access/Ownable.sol\";\n\nimport \"./interfaces/Errors.sol\";\nimport \"./interfaces/IKycManager.sol\";\n\n/**\n * Handles address permissions. An address can be KYCed for US or non-US purposes. Additionally, an address may be banned\n */\ncontract KycManager is IKycManager, Ownable {\n    mapping(address => User) userData;\n    address[] public userList;\n    uint16 userCount = 0;\n    bool strictOn;\n\n    modifier onlyNonZeroAddress(address _investor) {\n        if (_investor == address(0)) {\n            revert InvalidAddress(_investor);\n        }\n        _;\n    }\n\n    constructor(bool _strictOn) {\n        strictOn = _strictOn;\n    }\n\n    ////////////////////////////////////////////////////////////\n    // Grant\n    ////////////////////////////////////////////////////////////\n\n    function bulkGrantKyc(address[] calldata _investors, KycType[] calldata _kycTypes) external onlyOwner {\n        require(_investors.length == _kycTypes.length, \"invalid input\");\n        for (uint256 i = 0; i < _investors.length; i++) {\n            _grantKyc(_investors[i], _kycTypes[i]);\n        }\n    }\n\n    function _grantKyc(address _investor, KycType _kycType) internal onlyNonZeroAddress(_investor) {\n        require(KycType.US_KYC == _kycType || KycType.GENERAL_KYC == _kycType, \"invalid kyc type\");\n\n        _addUserIfNotExist(_investor);\n        userData[_investor].kycType = _kycType;\n        emit GrantKyc(_investor, _kycType);\n    }\n\n    ////////////////////////////////////////////////////////////\n    // Revoke\n    ////////////////////////////////////////////////////////////\n\n    function bulkRevokeKyc(address[] calldata _investors) external onlyOwner {\n        for (uint256 i = 0; i < _investors.length; i++) {\n            _revokeKyc(_investors[i]);\n        }\n    }\n\n    function _revokeKyc(address _investor) internal onlyNonZeroAddress(_investor) {\n        _addUserIfNotExist(_investor);\n        User storage user = userData[_investor];\n        emit RevokeKyc(_investor, user.kycType);\n        user.kycType = KycType.NON_KYC;\n    }\n\n    ////////////////////////////////////////////////////////////\n    // Ban\n    ////////////////////////////////////////////////////////////\n\n    function bulkBan(address[] calldata _investors) external onlyOwner {\n        for (uint256 i = 0; i < _investors.length; i++) {\n            _setBanned(_investors[i], true);\n        }\n    }\n\n    ////////////////////////////////////////////////////////////\n    // Unban\n    ////////////////////////////////////////////////////////////\n\n    function bulkUnBan(address[] calldata _investors) external onlyOwner {\n        for (uint256 i = 0; i < _investors.length; i++) {\n            _setBanned(_investors[i], false);\n        }\n    }\n\n    function _setBanned(address _investor, bool _status) internal onlyNonZeroAddress(_investor) {\n        userData[_investor].isBanned = _status;\n        emit Banned(_investor, _status);\n    }\n\n    function setStrict(bool _status) external onlyOwner {\n        strictOn = _status;\n        emit SetStrict(_status);\n    }\n\n    ////////////////////////////////////////////////////////////\n    // Public getters\n    ////////////////////////////////////////////////////////////\n\n    function getUserInfo(address _investor) external view returns (User memory user) {\n        user = userData[_investor];\n    }\n\n    function getAllUsers() external view returns (address[] memory) {\n        return userList;\n    }\n\n    function getAllUserInfo() external view returns (UserAddress[] memory info) {\n        info = new UserAddress[](userCount);\n        for (uint16 i = 0; i < userCount; i++) {\n            address user = userList[i];\n            User storage data = userData[user];\n            info[i].user = user;\n            info[i].kycType = data.kycType;\n            info[i].isBanned = data.isBanned;\n        }\n        return info;\n    }\n\n    function onlyNotBanned(address _investor) external view {\n        if (userData[_investor].isBanned) {\n            revert UserBanned(_investor);\n        }\n    }\n\n    function onlyKyc(address _investor) external view {\n        if (KycType.NON_KYC == userData[_investor].kycType) {\n            revert UserMissingKyc(_investor);\n        }\n    }\n\n    function isBanned(address _investor) external view returns (bool) {\n        return userData[_investor].isBanned;\n    }\n\n    function isKyc(address _investor) external view returns (bool) {\n        return KycType.NON_KYC != userData[_investor].kycType;\n    }\n\n    function isUSKyc(address _investor) external view returns (bool) {\n        return KycType.US_KYC == userData[_investor].kycType;\n    }\n\n    function isNonUSKyc(address _investor) external view returns (bool) {\n        return KycType.GENERAL_KYC == userData[_investor].kycType;\n    }\n\n    function isStrict() external view returns (bool) {\n        return strictOn;\n    }\n\n    function _addUserIfNotExist(address user) internal {\n        if (!userData[user].exists) {\n            userList.push(user);\n            userData[user].exists = true;\n            ++userCount;\n        }\n    }\n}\n"
      },
      "lib/openzeppelin-contracts/contracts/access/Ownable.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../utils/Context.sol\";\n\n/**\n * @dev Contract module which provides a basic access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * By default, the owner account will be the one that deploys the contract. This\n * can later be changed with {transferOwnership}.\n *\n * This module is used through inheritance. It will make available the modifier\n * `onlyOwner`, which can be applied to your functions to restrict their use to\n * the owner.\n */\nabstract contract Ownable is Context {\n    address private _owner;\n\n    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n    /**\n     * @dev Initializes the contract setting the deployer as the initial owner.\n     */\n    constructor() {\n        _transferOwnership(_msgSender());\n    }\n\n    /**\n     * @dev Throws if called by any account other than the owner.\n     */\n    modifier onlyOwner() {\n        _checkOwner();\n        _;\n    }\n\n    /**\n     * @dev Returns the address of the current owner.\n     */\n    function owner() public view virtual returns (address) {\n        return _owner;\n    }\n\n    /**\n     * @dev Throws if the sender is not the owner.\n     */\n    function _checkOwner() internal view virtual {\n        require(owner() == _msgSender(), \"Ownable: caller is not the owner\");\n    }\n\n    /**\n     * @dev Leaves the contract without owner. It will not be possible to call\n     * `onlyOwner` functions. Can only be called by the current owner.\n     *\n     * NOTE: Renouncing ownership will leave the contract without an owner,\n     * thereby disabling any functionality that is only available to the owner.\n     */\n    function renounceOwnership() public virtual onlyOwner {\n        _transferOwnership(address(0));\n    }\n\n    /**\n     * @dev Transfers ownership of the contract to a new account (`newOwner`).\n     * Can only be called by the current owner.\n     */\n    function transferOwnership(address newOwner) public virtual onlyOwner {\n        require(newOwner != address(0), \"Ownable: new owner is the zero address\");\n        _transferOwnership(newOwner);\n    }\n\n    /**\n     * @dev Transfers ownership of the contract to a new account (`newOwner`).\n     * Internal function without access restriction.\n     */\n    function _transferOwnership(address newOwner) internal virtual {\n        address oldOwner = _owner;\n        _owner = newOwner;\n        emit OwnershipTransferred(oldOwner, newOwner);\n    }\n}\n"
      },
      "src/interfaces/Errors.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.19;\n\n/**\n * @dev ERC-1066 codes\n * https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1066.md\n */\n\nuint8 constant SUCCESS_CODE = 0x01;\nuint8 constant DISALLOWED_OR_STOP_CODE = 0x10;\nuint8 constant REVOKED_OR_BANNED_CODE = 0x16;\nstring constant SUCCESS_MESSAGE = \"Success\";\nstring constant DISALLOWED_OR_STOP_MESSAGE = \"User is not KYCed\";\nstring constant REVOKED_OR_BANNED_MESSAGE = \"User is banned\";\n\nerror PermissionDenied();\nerror InvalidAddress(address addr);\nerror InvalidAmount(uint256 amount);\nerror UserMissingKyc(address user);\nerror UserBanned(address user);\nerror RedemptionQueueEmpty();\nerror NoExcessReserves();\nerror InsufficientBalance(uint256 current, uint256 needed);\nerror InsufficientAllowance(uint256 current, uint256 needed);\nerror MinimumDepositRequired(uint256 minimum);\nerror MinimumInitialDepositRequired(uint256 minimum);\nerror MaximumDepositExceeded(uint256 maximum);\nerror MinimumWithdrawRequired(uint256 minimum);\nerror MaximumWithdrawExceeded(uint256 maximum);\n"
      },
      "src/interfaces/IKycManager.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.19;\n\ninterface IKycManager {\n    enum KycType {\n        NON_KYC,\n        US_KYC,\n        GENERAL_KYC\n    }\n\n    struct UserAddress {\n        address user;\n        KycType kycType;\n        bool isBanned;\n    }\n\n    struct User {\n        bool exists;\n        KycType kycType;\n        bool isBanned;\n    }\n\n    event GrantKyc(address _investor, KycType _kycType);\n    event RevokeKyc(address _investor, KycType _kycType);\n    event Banned(address _investor, bool _status);\n    event SetStrict(bool _status);\n\n    function onlyNotBanned(address investor) external view;\n\n    function onlyKyc(address investor) external view;\n\n    function isBanned(address investor) external view returns (bool);\n\n    function isKyc(address investor) external view returns (bool);\n\n    function isUSKyc(address investor) external view returns (bool);\n\n    function isNonUSKyc(address investor) external view returns (bool);\n\n    function isStrict() external view returns (bool);\n}\n"
      },
      "lib/openzeppelin-contracts/contracts/utils/Context.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n    function _msgSender() internal view virtual returns (address) {\n        return msg.sender;\n    }\n\n    function _msgData() internal view virtual returns (bytes calldata) {\n        return msg.data;\n    }\n}\n"
      }
    },
    "settings": {
      "remappings": [
        "chainlink/=lib/chainlink/contracts/src/v0.8/",
        "ds-test/=lib/forge-std/lib/ds-test/src/",
        "erc4626-tests/=lib/chainlink/contracts/foundry-lib/openzeppelin-contracts/lib/erc4626-tests/",
        "forge-std/=lib/forge-std/src/",
        "openzeppelin-contracts/=lib/openzeppelin-contracts/contracts/",
        "openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
        "openzeppelin/=lib/openzeppelin-contracts-upgradeable/contracts/"
      ],
      "optimizer": {
        "enabled": true,
        "runs": 200
      },
      "metadata": {
        "useLiteralContent": false,
        "bytecodeHash": "ipfs",
        "appendCBOR": true
      },
      "outputSelection": {
        "*": {
          "*": [
            "evm.bytecode",
            "evm.deployedBytecode",
            "devdoc",
            "userdoc",
            "metadata",
            "abi"
          ]
        }
      },
      "evmVersion": "paris",
      "libraries": {}
    }
  }}