creation_sourcecode: {{
    "language": "Solidity",
    "sources": {
      "src/AceTower.sol": {
        "content": "// SPDX-License-Identifier: MIT\n\npragma solidity 0.8.20;\n\nimport {Ownable} from \"@openzeppelin/contracts/access/Ownable.sol\";\n\ncontract AceTower is Ownable {\n    uint256 private s_towerMasterFID;\n    address private s_towerMasterAddress;\n\n    uint256 private constant ENTRY_FEE = 0.001 ether;\n    uint256 private constant TICKET_FEE_PERCENTAGE = 100; // 10%\n    uint256 private s_protocolFees;\n\n    mapping(uint256 userFID => uint256 earnings) private s_earnings;\n    mapping(address playingAddress => bool isPlaying) private s_playing;\n    mapping(uint256 userFID => uint256 attackAttempts) private s_attackAttempts;\n\n    event EnteredGame(address player, uint256 playerFID);\n    event NewTowerMaster(address winner, uint256 playerFID);\n    event PlayerLost(address player, uint256 playerFID);\n\n    error AceTower__InvalidTicketPrice();\n    error AceTower__AlreadyTowerMaster();\n    error AceTower__TransferFailed();\n    error AceTower__UserNotPlaying();\n    error AceTower__UserAlreadyPlaying();\n\n    constructor(uint256 _initialTowerMasterFID) Ownable(msg.sender) {\n        s_towerMasterFID = _initialTowerMasterFID;\n        s_towerMasterAddress = msg.sender;\n    }\n\n    function buyTicket(uint256 _playerFID) external payable {\n        if (msg.value < ENTRY_FEE) {\n            revert AceTower__InvalidTicketPrice();\n        }\n        if (msg.sender == s_towerMasterAddress) {\n            revert AceTower__AlreadyTowerMaster();\n        }\n\n        s_playing[msg.sender] = true;\n\n        uint256 protocolFee = (msg.value * TICKET_FEE_PERCENTAGE) / 1000;\n        s_protocolFees += protocolFee;\n        uint256 towerEarnings = msg.value - protocolFee;\n        s_earnings[s_towerMasterFID] += towerEarnings;\n\n        (bool callSuccess, ) = s_towerMasterAddress.call{value: towerEarnings}(\n            \"\"\n        );\n        if (!callSuccess) {\n            revert AceTower__TransferFailed();\n        }\n        emit EnteredGame(msg.sender, _playerFID);\n    }\n\n    function gameResult(\n        uint256 _playerFID,\n        address _playerAddress,\n        bool _playerWon\n    ) external onlyOwner {\n        if (s_playing[_playerAddress] == false) {\n            revert AceTower__UserNotPlaying();\n        }\n        if (_playerWon) {\n            _setNewTowerMaster(_playerFID, _playerAddress);\n        } else {\n            s_attackAttempts[s_towerMasterFID] += 1;\n            emit PlayerLost(_playerAddress, _playerFID);\n        }\n        s_playing[_playerAddress] = false;\n    }\n\n    function _setNewTowerMaster(\n        uint256 _newTowerMasterFID,\n        address _newTowerMasterAddress\n    ) internal {\n        if (_newTowerMasterFID == s_towerMasterFID) {\n            revert AceTower__AlreadyTowerMaster();\n        }\n        s_towerMasterFID = _newTowerMasterFID;\n        s_towerMasterAddress = _newTowerMasterAddress;\n        emit NewTowerMaster(_newTowerMasterAddress, _newTowerMasterFID);\n    }\n\n    function withdrawFees() external onlyOwner {\n        (bool callSuccess, ) = msg.sender.call{value: s_protocolFees}(\"\");\n        if (!callSuccess) {\n            revert AceTower__TransferFailed();\n        }\n        s_protocolFees = 0;\n    }\n\n    function getPlayerEarnings(\n        uint256 _playerFID\n    ) external view returns (uint256) {\n        return s_earnings[_playerFID];\n    }\n\n    function getPlayerAttackAttempts(\n        uint256 _playerFID\n    ) external view returns (uint256) {\n        return s_attackAttempts[_playerFID];\n    }\n\n    function getTowerMasterInfo()\n        external\n        view\n        returns (uint256, address, uint256, uint256)\n    {\n        return (\n            s_towerMasterFID,\n            s_towerMasterAddress,\n            s_attackAttempts[s_towerMasterFID],\n            s_earnings[s_towerMasterFID]\n        );\n    }\n}\n"
      },
      "lib/openzeppelin-contracts/contracts/access/Ownable.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)\n\npragma solidity ^0.8.20;\n\nimport {Context} from \"../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 * The initial owner is set to the address provided by the deployer. This can\n * 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    /**\n     * @dev The caller account is not authorized to perform an operation.\n     */\n    error OwnableUnauthorizedAccount(address account);\n\n    /**\n     * @dev The owner is not a valid owner account. (eg. `address(0)`)\n     */\n    error OwnableInvalidOwner(address owner);\n\n    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n    /**\n     * @dev Initializes the contract setting the address provided by the deployer as the initial owner.\n     */\n    constructor(address initialOwner) {\n        if (initialOwner == address(0)) {\n            revert OwnableInvalidOwner(address(0));\n        }\n        _transferOwnership(initialOwner);\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        if (owner() != _msgSender()) {\n            revert OwnableUnauthorizedAccount(_msgSender());\n        }\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        if (newOwner == address(0)) {\n            revert OwnableInvalidOwner(address(0));\n        }\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"
      },
      "lib/openzeppelin-contracts/contracts/utils/Context.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)\n\npragma solidity ^0.8.20;\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    function _contextSuffixLength() internal view virtual returns (uint256) {\n        return 0;\n    }\n}\n"
      }
    },
    "settings": {
      "remappings": [
        "@openzeppelin/=lib/openzeppelin-contracts/",
        "@solmate/=lib/solmate/src/",
        "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
        "ds-test/=lib/openzeppelin-contracts/lib/forge-std/lib/ds-test/src/",
        "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
        "forge-std/=lib/forge-std/src/",
        "openzeppelin-contracts/=lib/openzeppelin-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": {}
    }
  }}