creation_sourcecode: {{
    "language": "Solidity",
    "sources": {
      "src/token/RestrictionManager.sol": {
        "content": "// SPDX-License-Identifier: AGPL-3.0-only\npragma solidity 0.8.21;\n\nimport {Auth} from \"./../util/Auth.sol\";\nimport {IERC20} from \"../interfaces/IERC20.sol\";\n\ninterface RestrictionManagerLike {\n    function updateMember(address user, uint256 validUntil) external;\n    function members(address user) external view returns (uint256);\n    function hasMember(address user) external view returns (bool);\n    function freeze(address user) external;\n    function unfreeze(address user) external;\n}\n\n/// @title  Restriction Manager\n/// @notice ERC1404 based contract that checks transfer restrictions.\ncontract RestrictionManager is Auth {\n    string internal constant SUCCESS_MESSAGE = \"RestrictionManager/transfer-allowed\";\n    string internal constant SOURCE_IS_FROZEN_MESSAGE = \"RestrictionManager/source-is-frozen\";\n    string internal constant DESTINATION_IS_FROZEN_MESSAGE = \"RestrictionManager/destination-is-frozen\";\n    string internal constant DESTINATION_NOT_A_MEMBER_RESTRICTION_MESSAGE =\n        \"RestrictionManager/destination-not-a-member\";\n\n    uint8 public constant SUCCESS_CODE = 0;\n    uint8 public constant SOURCE_IS_FROZEN_CODE = 1;\n    uint8 public constant DESTINATION_IS_FROZEN_CODE = 2;\n    uint8 public constant DESTINATION_NOT_A_MEMBER_RESTRICTION_CODE = 3;\n\n    IERC20 public immutable token;\n\n    /// @dev Frozen accounts that tokens cannot be transferred from or to\n    mapping(address => uint256 isFrozen) public frozen;\n\n    /// @dev Member accounts that tokens can be transferred to, with an end date\n    mapping(address => uint256 validUntil) public members;\n\n    // --- Events ---\n    event UpdateMember(address indexed user, uint256 validUntil);\n    event Freeze(address indexed user);\n    event Unfreeze(address indexed user);\n\n    constructor(address token_) {\n        token = IERC20(token_);\n\n        wards[msg.sender] = 1;\n        emit Rely(msg.sender);\n    }\n\n    // --- ERC1404 implementation ---\n    function detectTransferRestriction(address from, address to, uint256 /* value */ ) public view returns (uint8) {\n        if (frozen[from] == 1) {\n            return SOURCE_IS_FROZEN_CODE;\n        }\n\n        if (frozen[to] == 1) {\n            return DESTINATION_IS_FROZEN_CODE;\n        }\n\n        if (!hasMember(to)) {\n            return DESTINATION_NOT_A_MEMBER_RESTRICTION_CODE;\n        }\n\n        return SUCCESS_CODE;\n    }\n\n    function messageForTransferRestriction(uint8 restrictionCode) public pure returns (string memory) {\n        if (restrictionCode == SOURCE_IS_FROZEN_CODE) {\n            return SOURCE_IS_FROZEN_MESSAGE;\n        }\n\n        if (restrictionCode == DESTINATION_IS_FROZEN_CODE) {\n            return DESTINATION_IS_FROZEN_MESSAGE;\n        }\n\n        if (restrictionCode == DESTINATION_NOT_A_MEMBER_RESTRICTION_CODE) {\n            return DESTINATION_NOT_A_MEMBER_RESTRICTION_MESSAGE;\n        }\n\n        return SUCCESS_MESSAGE;\n    }\n\n    // --- Handling freezes ---\n    function freeze(address user) public auth {\n        require(user != address(0), \"RestrictionManager/cannot-freeze-zero-address\");\n        frozen[user] = 1;\n        emit Freeze(user);\n    }\n\n    function unfreeze(address user) public auth {\n        frozen[user] = 0;\n        emit Unfreeze(user);\n    }\n\n    // --- Managing members ---\n    function updateMember(address user, uint256 validUntil) public auth {\n        require(block.timestamp <= validUntil, \"RestrictionManager/invalid-valid-until\");\n        members[user] = validUntil;\n\n        emit UpdateMember(user, validUntil);\n    }\n\n    function hasMember(address user) public view returns (bool) {\n        return members[user] >= block.timestamp;\n    }\n\n    // --- Misc ---\n    function afterTransfer(address, /* from */ address, /* to */ uint256 /* value */ ) public virtual auth {}\n    function afterMint(address, /* to */ uint256 /* value */ ) public virtual auth {}\n}\n"
      },
      "src/util/Auth.sol": {
        "content": "// SPDX-License-Identifier: AGPL-3.0-only\npragma solidity 0.8.21;\n\n/// @title  Auth\n/// @notice Simple authentication pattern\n/// @author Based on code from https://github.com/makerdao/dss\ncontract Auth {\n    mapping(address => uint256) public wards;\n\n    event Rely(address indexed user);\n    event Deny(address indexed user);\n\n    /// @dev Give permissions to the user\n    function rely(address user) external auth {\n        wards[user] = 1;\n        emit Rely(user);\n    }\n\n    /// @dev Remove permissions from the user\n    function deny(address user) external auth {\n        wards[user] = 0;\n        emit Deny(user);\n    }\n\n    /// @dev Check if the msg.sender has permissions\n    modifier auth() {\n        require(wards[msg.sender] == 1, \"Auth/not-authorized\");\n        _;\n    }\n}\n"
      },
      "src/interfaces/IERC20.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.21;\n\n/// @title  IERC20\n/// @dev    Interface of the ERC20 standard as defined in the EIP.\n/// @author Modified from OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)\ninterface IERC20 {\n    /**\n     * @dev Emitted when `value` tokens are moved from one account (`from`) to\n     * another (`to`).\n     *\n     * Note that `value` may be zero.\n     */\n    event Transfer(address indexed from, address indexed to, uint256 value);\n\n    /**\n     * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n     * a call to {approve}. `value` is the new allowance.\n     */\n    event Approval(address indexed owner, address indexed spender, uint256 value);\n\n    /**\n     * @dev Returns the value of tokens in existence.\n     */\n    function totalSupply() external view returns (uint256);\n\n    /**\n     * @dev Returns the value of tokens owned by `account`.\n     */\n    function balanceOf(address account) external view returns (uint256);\n\n    /**\n     * @dev Moves a `value` amount of tokens from the caller's account to `to`.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * Emits a {Transfer} event.\n     */\n    function transfer(address to, uint256 value) external returns (bool);\n\n    /**\n     * @dev Returns the remaining number of tokens that `spender` will be\n     * allowed to spend on behalf of `owner` through {transferFrom}. This is\n     * zero by default.\n     *\n     * This value changes when {approve} or {transferFrom} are called.\n     */\n    function allowance(address owner, address spender) external view returns (uint256);\n\n    /**\n     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the\n     * caller's tokens.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * IMPORTANT: Beware that changing an allowance with this method brings the risk\n     * that someone may use both the old and the new allowance by unfortunate\n     * transaction ordering. One possible solution to mitigate this race\n     * condition is to first reduce the spender's allowance to 0 and set the\n     * desired value afterwards:\n     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n     *\n     * Emits an {Approval} event.\n     */\n    function approve(address spender, uint256 value) external returns (bool);\n\n    /**\n     * @dev Moves a `value` amount of tokens from `from` to `to` using the\n     * allowance mechanism. `value` is then deducted from the caller's\n     * allowance.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * Emits a {Transfer} event.\n     */\n    function transferFrom(address from, address to, uint256 value) external returns (bool);\n}\n\n/**\n * @dev Interface for the optional metadata functions from the ERC20 standard.\n */\ninterface IERC20Metadata is IERC20 {\n    /**\n     * @dev Returns the name of the token.\n     */\n    function name() external view returns (string memory);\n\n    /**\n     * @dev Returns the symbol of the token.\n     */\n    function symbol() external view returns (string memory);\n\n    /**\n     * @dev Returns the decimals places of the token.\n     */\n    function decimals() external view returns (uint8);\n}\n\n/**\n * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in\n * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].\n *\n * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by\n * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't\n * need to send a transaction, and thus is not required to hold Ether at all.\n *\n * ==== Security Considerations\n *\n * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature\n * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be\n * considered as an intention to spend the allowance in any specific way. The second is that because permits have\n * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should\n * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be\n * generally recommended is:\n *\n * ```solidity\n * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {\n *     try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}\n *     doThing(..., value);\n * }\n *\n * function doThing(..., uint256 value) public {\n *     token.safeTransferFrom(msg.sender, address(this), value);\n *     ...\n * }\n * ```\n *\n * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of\n * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also\n * {SafeERC20-safeTransferFrom}).\n *\n * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so\n * contracts should have entry points that don't rely on permit.\n */\ninterface IERC20Permit {\n    /**\n     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,\n     * given ``owner``'s signed approval.\n     *\n     * IMPORTANT: The same issues {IERC20-approve} has related to transaction\n     * ordering also apply here.\n     *\n     * Emits an {Approval} event.\n     *\n     * Requirements:\n     *\n     * - `spender` cannot be the zero address.\n     * - `deadline` must be a timestamp in the future.\n     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`\n     * over the EIP712-formatted function arguments.\n     * - the signature must use ``owner``'s current nonce (see {nonces}).\n     *\n     * For more information on the signature format, see the\n     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP\n     * section].\n     *\n     * CAUTION: See Security Considerations above.\n     */\n    function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s)\n        external;\n\n    /**\n     * @dev Returns the current nonce for `owner`. This value must be\n     * included whenever a signature is generated for {permit}.\n     *\n     * Every successful call to {permit} increases ``owner``'s nonce by one. This\n     * prevents a signature from being used multiple times.\n     */\n    function nonces(address owner) external view returns (uint256);\n\n    /**\n     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.\n     */\n    // solhint-disable-next-line func-name-mixedcase\n    function DOMAIN_SEPARATOR() external view returns (bytes32);\n}\n"
      }
    },
    "settings": {
      "remappings": [
        "forge-std/=lib/forge-std/src/",
        "ds-test/=lib/forge-std/lib/ds-test/src/"
      ],
      "optimizer": {
        "enabled": true,
        "runs": 1000
      },
      "metadata": {
        "useLiteralContent": false,
        "bytecodeHash": "ipfs",
        "appendCBOR": true
      },
      "outputSelection": {
        "*": {
          "*": [
            "evm.bytecode",
            "evm.deployedBytecode",
            "devdoc",
            "userdoc",
            "metadata",
            "abi"
          ]
        }
      },
      "evmVersion": "paris",
      "libraries": {}
    }
  }}