{{
  "language": "Solidity",
  "sources": {
    "contracts/AccountImplementation.sol": {
      "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity 0.8.17;\n\n// Uncomment this line to use console.log\n// import \"hardhat/console.sol\";\nimport \"./AccountGuard.sol\";\n\ncontract AccountImplementation {\n    AccountGuard public immutable guard;\n\n    modifier authAndWhitelisted(address target, bool asDelegateCall) {\n        (bool canCall, bool isWhitelisted) = guard.canCallAndWhitelisted(\n            address(this),\n            msg.sender,\n            target,\n            asDelegateCall\n        );\n        require(\n            canCall,\n            \"account-guard/no-permit\"\n        );\n        require(\n            isWhitelisted,\n            \"account-guard/illegal-target\"\n        );\n        _;\n    }\n\n    constructor(AccountGuard _guard) {\n        require(\n            address(_guard) != address(0x0),\n            \"account-guard/wrong-guard-address\"\n        );\n        guard = _guard;\n    }\n\n    function send(address _target, bytes calldata _data)\n        external\n        payable\n        authAndWhitelisted(_target, false)\n    {\n        (bool status, ) = (_target).call{value: msg.value}(_data);\n        require(status, \"account-guard/call-failed\");\n    }\n\n    function execute(address _target, bytes memory /* code do not compile with calldata */ _data)\n        external\n        payable\n        authAndWhitelisted(_target, true)\n\n        returns (bytes32)\n    {\n        // call contract in current context\n        assembly {\n            let succeeded := delegatecall(\n                sub(gas(), 5000),\n                _target,\n                add(_data, 0x20),\n                mload(_data),\n                0,\n                32\n            )\n            returndatacopy(0, 0, returndatasize())\n            switch succeeded\n            case 0 {\n                // throw if delegatecall failed\n                revert(0, returndatasize())\n            }\n            default {\n                return(0, 0x20)\n            }\n        }\n    }\n \n    receive() external payable {\n        emit FundsRecived(msg.sender, msg.value);\n    }\n\n    function owner() external view returns (address) {\n        return guard.owners(address(this));\n    }\n\n    event FundsRecived(address sender, uint256 amount);\n}\n"
    },
    "contracts/AccountGuard.sol": {
      "content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity 0.8.17;\n\n// Uncomment this line to use console.log\n// import \"hardhat/console.sol\";\nimport \"@openzeppelin/contracts/access/Ownable.sol\";\n\ncontract AccountGuard is Ownable {\n    address factory;\n    uint8 constant WHITELISTED_EXECUTE_MASK = 1;\n    uint8 constant WHITELISTED_SEND_MASK = 2;\n    mapping(address => mapping(address => bool)) private allowed;\n    mapping(address => uint8) private whitelisted;\n    mapping(address => address) public owners;\n\n    function isWhitelisted(address target) public view returns (bool) {\n        return (whitelisted[target] & WHITELISTED_EXECUTE_MASK) > 0;\n    }\n\n    function setWhitelist(address target, bool status) external onlyOwner {\n        whitelisted[target] = status\n            ? whitelisted[target] | WHITELISTED_EXECUTE_MASK\n            : whitelisted[target] & ~WHITELISTED_EXECUTE_MASK;\n    }\n\n    function isWhitelistedSend(address target) public view returns (bool) {\n        return (whitelisted[target] & WHITELISTED_SEND_MASK) > 0;\n    }\n\n    function setWhitelistSend(address target, bool status) external onlyOwner {\n        whitelisted[target] = status\n            ? whitelisted[target] | WHITELISTED_SEND_MASK\n            : whitelisted[target] & ~WHITELISTED_SEND_MASK;\n    }\n\n    function canCallAndWhitelisted(\n        address proxy,\n        address operator,\n        address callTarget,\n        bool asDelegateCall\n    ) external view returns (bool, bool) {\n        return (\n            allowed[operator][proxy],\n            asDelegateCall\n                ? isWhitelisted(callTarget)\n                : isWhitelistedSend(callTarget)\n        );\n    }\n\n    function canCall(address target, address operator)\n        external\n        view\n        returns (bool)\n    {\n        return owners[target] == operator || allowed[operator][target];\n    }\n\n    function initializeFactory() external {\n        require(factory == address(0), \"account-guard/factory-set\");\n        factory = msg.sender;\n    }\n\n    function permit(\n        address caller,\n        address target,\n        bool allowance\n    ) external {\n        require(\n            allowed[msg.sender][target] || msg.sender == factory,\n            \"account-guard/no-permit\"\n        );\n        if (msg.sender == factory) {\n            owners[target] = caller;\n            allowed[target][target] = true;\n        } else {\n            require(owners[target] != caller, \"account-guard/cant-deny-owner\");\n        }\n        allowed[caller][target] = allowance;\n\n        if (allowance) {\n            emit PermissionGranted(caller, target);\n        } else {\n            emit PermissionRevoked(caller, target);\n        }\n    }\n\n    function changeOwner(address newOwner, address target) external {\n        require(newOwner != address(0), \"account-guard/zero-address\");\n        require(owners[target] == msg.sender, \"account-guard/only-proxy-owner\");\n        owners[target] = newOwner;\n        allowed[msg.sender][target] = false;\n        allowed[newOwner][target] = true;\n        emit ProxyOwnershipTransferred(newOwner, msg.sender, target);\n    }\n\n    event ProxyOwnershipTransferred(\n        address indexed newOwner,\n        address indexed oldAddress,\n        address indexed proxy\n    );\n    event PermissionGranted(address indexed caller, address indexed proxy);\n    event PermissionRevoked(address indexed caller, address indexed proxy);\n}\n"
    },
    "@openzeppelin/contracts/access/Ownable.sol": {
      "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.7.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 anymore. Can only be called by the current owner.\n     *\n     * NOTE: Renouncing ownership will leave the contract without an owner,\n     * thereby removing 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"
    },
    "@openzeppelin/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": {
    "optimizer": {
      "enabled": false,
      "runs": 200
    },
    "outputSelection": {
      "*": {
        "*": [
          "evm.bytecode",
          "evm.deployedBytecode",
          "devdoc",
          "userdoc",
          "metadata",
          "abi"
        ]
      }
    },
    "libraries": {}
  }
}}