creation_sourcecode: {{
    "language": "Solidity",
    "sources": {
      "contracts/SimpleFarm.sol": {
        "content": "// SPDX-License-Identifier: -- BCOM --\n\npragma solidity =0.8.25;\n\nimport \"./TokenWrapper.sol\";\n\ncontract SimpleFarm is TokenWrapper {\n\n    IERC20 public immutable stakeToken;\n    IERC20 public immutable rewardToken;\n\n    uint256 public rewardRate;\n    uint256 public periodFinished;\n    uint256 public rewardDuration;\n    uint256 public lastUpdateTime;\n    uint256 public perTokenStored;\n\n    uint256 constant PRECISION = 1E18;\n\n    mapping(address => uint256) public userRewards;\n    mapping(address => uint256) public perTokenPaid;\n\n    address public ownerAddress;\n    address public proposedOwner;\n    address public managerAddress;\n\n    modifier onlyOwner() {\n        require(\n            msg.sender == ownerAddress,\n            \"SimpleFarm: INVALID_OWNER\"\n        );\n        _;\n    }\n\n    modifier onlyManager() {\n        require(\n            msg.sender == managerAddress,\n            \"SimpleFarm: INVALID_MANAGER\"\n        );\n        _;\n    }\n\n    modifier updateFarm() {\n        perTokenStored = rewardPerToken();\n        lastUpdateTime = lastTimeRewardApplicable();\n        _;\n    }\n\n    modifier updateUser() {\n        userRewards[msg.sender] = earned(msg.sender);\n        perTokenPaid[msg.sender] = perTokenStored;\n        _;\n    }\n\n    modifier updateSender(address _sender) {\n        userRewards[_sender] = earned(_sender);\n        perTokenPaid[_sender] = perTokenStored;\n        _;\n    }\n\n    event Staked(\n        address indexed user,\n        uint256 tokenAmount\n    );\n\n    event Withdrawn(\n        address indexed user,\n        uint256 tokenAmount\n    );\n\n    event RewardAdded(\n        uint256 rewardRate,\n        uint256 tokenAmount\n    );\n\n    event RewardPaid(\n        address indexed user,\n        uint256 tokenAmount\n    );\n\n    event Recovered(\n        IERC20 indexed token,\n        uint256 tokenAmount\n    );\n\n    event RewardsDurationUpdated(\n        uint256 newRewardDuration\n    );\n\n    event OwnerProposed(\n        address proposedOwner\n    );\n\n    event OwnerChanged(\n        address newOwner\n    );\n\n    event ManagerChanged(\n        address newManager\n    );\n\n    constructor(\n        IERC20 _stakeToken,\n        IERC20 _rewardToken,\n        uint256 _defaultDuration\n    ) {\n        require(\n            _defaultDuration > 0,\n            \"SimpleFarm: INVALID_DURATION\"\n        );\n\n        stakeToken = _stakeToken;\n        rewardToken = _rewardToken;\n\n        ownerAddress = msg.sender;\n        managerAddress = msg.sender;\n\n        rewardDuration = _defaultDuration;\n    }\n\n    /**\n     * @dev Tracks timestamp for when reward was applied last time\n     */\n    function lastTimeRewardApplicable()\n        public\n        view\n        returns (uint256 res)\n    {\n        res = block.timestamp < periodFinished\n            ? block.timestamp\n            : periodFinished;\n    }\n\n    /**\n     * @dev Relative value on reward for single staked token\n     */\n    function rewardPerToken()\n        public\n        view\n        returns (uint256)\n    {\n        if (_totalStaked == 0) {\n            return perTokenStored;\n        }\n\n        uint256 timeFrame = lastTimeRewardApplicable()\n            - lastUpdateTime;\n\n        uint256 extraFund = timeFrame\n            * rewardRate\n            * PRECISION\n            / _totalStaked;\n\n        return perTokenStored\n            + extraFund;\n    }\n\n    /**\n     * @dev Reports earned amount by wallet address not yet collected\n     */\n    function earned(\n        address _walletAddress\n    )\n        public\n        view\n        returns (uint256)\n    {\n        uint256 difference = rewardPerToken()\n            - perTokenPaid[_walletAddress];\n\n        return _balances[_walletAddress]\n            * difference\n            / PRECISION\n            + userRewards[_walletAddress];\n    }\n\n    /**\n     * @dev Performs deposit of staked token into the farm\n     */\n    function farmDeposit(\n        uint256 _stakeAmount\n    )\n        external\n        updateFarm()\n        updateUser()\n    {\n        address senderAddress = msg.sender;\n\n        _stake(\n            _stakeAmount,\n            senderAddress\n        );\n\n        safeTransferFrom(\n            stakeToken,\n            senderAddress,\n            address(this),\n            _stakeAmount\n        );\n\n        emit Staked(\n            senderAddress,\n            _stakeAmount\n        );\n    }\n\n    /**\n     * @dev Performs withdrawal of staked token from the farm\n     */\n    function farmWithdraw(\n        uint256 _withdrawAmount\n    )\n        public\n        updateFarm()\n        updateUser()\n    {\n        if (block.timestamp < periodFinished) {\n            require(\n                _totalStaked > _withdrawAmount,\n                \"SimpleFarm: STILL_EARNING\"\n            );\n        }\n\n        address senderAddress = msg.sender;\n\n        _withdraw(\n            _withdrawAmount,\n            senderAddress\n        );\n\n        safeTransfer(\n            stakeToken,\n            senderAddress,\n            _withdrawAmount\n        );\n\n        emit Withdrawn(\n            senderAddress,\n            _withdrawAmount\n        );\n    }\n\n    /**\n     * @dev Allows to withdraw staked tokens and claim rewards\n     */\n    function exitFarm()\n        external\n    {\n        uint256 withdrawAmount = _balances[\n            msg.sender\n        ];\n\n        farmWithdraw(\n            withdrawAmount\n        );\n\n        claimReward();\n    }\n\n    /**\n     * @dev Allows to claim accumulated rewards up to date\n     */\n    function claimReward()\n        public\n        updateFarm()\n        updateUser()\n        returns (uint256 rewardAmount)\n    {\n        address senderAddress = msg.sender;\n\n        rewardAmount = earned(\n            senderAddress\n        );\n\n        require(\n            rewardAmount > 0,\n            \"SimpleFarm: NOTHING_TO_CLAIM\"\n        );\n\n        userRewards[senderAddress] = 0;\n\n        safeTransfer(\n            rewardToken,\n            senderAddress,\n            rewardAmount\n        );\n\n        emit RewardPaid(\n            senderAddress,\n            rewardAmount\n        );\n    }\n\n    /**\n     * @dev Allows to invoke owner-change procedure\n     */\n    function proposeNewOwner(\n        address _newOwner\n    )\n        external\n        onlyOwner\n    {\n        if (_newOwner == ZERO_ADDRESS) {\n            revert(\"SimpleFarm: WRONG_ADDRESS\");\n        }\n\n        proposedOwner = _newOwner;\n\n        emit OwnerProposed(\n            _newOwner\n        );\n    }\n\n    /**\n     * @dev Finalizes owner-change 2-step procedure\n     */\n    function claimOwnership()\n        external\n    {\n        require(\n            msg.sender == proposedOwner,\n            \"SimpleFarm: INVALID_CANDIDATE\"\n        );\n\n        ownerAddress = proposedOwner;\n\n        emit OwnerChanged(\n            ownerAddress\n        );\n    }\n\n    /**\n     * @dev Allows to change manager of the farm\n     */\n    function changeManager(\n        address _newManager\n    )\n        external\n        onlyOwner\n    {\n        if (_newManager == ZERO_ADDRESS) {\n            revert(\"SimpleFarm: WRONG_ADDRESS\");\n        }\n\n        managerAddress = _newManager;\n\n        emit ManagerChanged(\n            _newManager\n        );\n    }\n\n    /**\n     * @dev Allows to recover accidentally sent tokens\n     * into the farm except stake and reward tokens\n     */\n    function recoverToken(\n        IERC20 tokenAddress,\n        uint256 tokenAmount\n    )\n        external\n    {\n        if (tokenAddress == stakeToken) {\n            revert(\"SimpleFarm: INVALID_TOKEN\");\n        }\n\n        if (tokenAddress == rewardToken) {\n            revert(\"SimpleFarm: INVALID_TOKEN\");\n        }\n\n        safeTransfer(\n            tokenAddress,\n            ownerAddress,\n            tokenAmount\n        );\n\n        emit Recovered(\n            tokenAddress,\n            tokenAmount\n        );\n    }\n\n    /**\n     * @dev Manager sets the cycle duration for distribution\n     * in seconds and can't be changed during active cycle\n     */\n    function setRewardDuration(\n        uint256 _rewardDuration\n    )\n        external\n        onlyManager\n    {\n        require(\n            _rewardDuration > 0,\n            \"SimpleFarm: INVALID_DURATION\"\n        );\n\n        require(\n            block.timestamp > periodFinished,\n            \"SimpleFarm: ONGOING_DISTRIBUTION\"\n        );\n\n        rewardDuration = _rewardDuration;\n\n        emit RewardsDurationUpdated(\n            _rewardDuration\n        );\n    }\n\n    /**\n     * @dev Manager sets reward per second to be distributed\n     * and invokes initial distribution to be started right away,\n     * must have some tokens already staked before executing.\n     */\n    function setRewardRate(\n        uint256 _newRewardRate\n    )\n        external\n        onlyManager\n        updateFarm()\n    {\n        require(\n            _totalStaked > 0,\n            \"SimpleFarm: NO_STAKERS\"\n        );\n\n        require(\n            _newRewardRate > 0,\n            \"SimpleFarm: INVALID_RATE\"\n        );\n\n        uint256 currentPeriodFinish = periodFinished;\n\n        lastUpdateTime = block.timestamp;\n        periodFinished = block.timestamp\n            + rewardDuration;\n\n        if (block.timestamp < currentPeriodFinish) {\n\n            require(\n                _newRewardRate >= rewardRate,\n                \"SimpleFarm: RATE_CANT_DECREASE\"\n            );\n\n            uint256 remainingTime = currentPeriodFinish\n                - block.timestamp;\n\n            uint256 rewardRemains = remainingTime\n                * rewardRate;\n\n            safeTransfer(\n                rewardToken,\n                managerAddress,\n                rewardRemains\n            );\n        }\n\n        rewardRate = _newRewardRate;\n\n        uint256 newRewardAmount = rewardDuration\n            * _newRewardRate;\n\n        safeTransferFrom(\n            rewardToken,\n            managerAddress,\n            address(this),\n            newRewardAmount\n        );\n\n        emit RewardAdded(\n            _newRewardRate,\n            newRewardAmount\n        );\n    }\n\n    /**\n     * @dev Allows to transfer receipt tokens\n     */\n    function transfer(\n        address _recipient,\n        uint256 _amount\n    )\n        external\n        updateFarm()\n        updateUser()\n        updateSender(_recipient)\n        returns (bool)\n    {\n        _transfer(\n            msg.sender,\n            _recipient,\n            _amount\n        );\n\n        return true;\n    }\n\n    /**\n     * @dev Allows to transfer receipt tokens on owner's behalf\n     */\n    function transferFrom(\n        address _sender,\n        address _recipient,\n        uint256 _amount\n    )\n        external\n        updateFarm()\n        updateSender(_sender)\n        updateSender(_recipient)\n        returns (bool)\n    {\n        if (_allowances[_sender][msg.sender] != type(uint256).max) {\n            _allowances[_sender][msg.sender] -= _amount;\n        }\n\n        _transfer(\n            _sender,\n            _recipient,\n            _amount\n        );\n\n        return true;\n    }\n}\n"
      },
      "contracts/TokenWrapper.sol": {
        "content": "// SPDX-License-Identifier: -- BCOM --\n\npragma solidity =0.8.25;\n\nimport \"./SafeERC20.sol\";\n\ncontract TokenWrapper is SafeERC20 {\n\n\n    string public constant name = \"VerseFarm\";\n    string public constant symbol = \"VFARM\";\n\n    uint8 public constant decimals = 18;\n\n    uint256 _totalStaked;\n    uint256 _totalStakedSQRT;\n\n    mapping(address => uint256) _balances;\n    mapping(address => mapping(address => uint256)) _allowances;\n\n    address constant ZERO_ADDRESS = address(0x0);\n\n    event Transfer(\n        address indexed from,\n        address indexed to,\n        uint256 value\n    );\n\n    event Approval(\n        address indexed owner,\n        address indexed spender,\n        uint256 value\n    );\n\n    /**\n     * @dev Returns total amount of staked tokens\n     */\n    function totalSupply()\n        external\n        view\n        returns (uint256)\n    {\n        return _totalStaked;\n    }\n\n    /**\n     * @dev Returns staked amount by wallet address\n     */\n    function balanceOf(\n        address _walletAddress\n    )\n        external\n        view\n        returns (uint256)\n    {\n        return _balances[_walletAddress];\n    }\n\n    /**\n     * @dev Increases staked amount by wallet address\n     */\n    function _stake(\n        uint256 _amount,\n        address _address\n    )\n        internal\n    {\n        _totalStaked =\n        _totalStaked + _amount;\n\n        unchecked {\n            _balances[_address] =\n            _balances[_address] + _amount;\n        }\n\n        emit Transfer(\n            ZERO_ADDRESS,\n            _address,\n            _amount\n        );\n    }\n\n    /**\n     * @dev Decreases total staked amount\n    */\n    function _withdraw(\n        uint256 _amount,\n        address _address\n    )\n        internal\n    {\n        _burn(\n            _amount,\n            _address\n        );\n    }\n\n    /**\n     * @dev Decreases total staked amount\n     */\n    function _burn(\n        uint256 _amount,\n        address _address\n    )\n        internal\n    {\n        unchecked {\n            _totalStaked =\n            _totalStaked - _amount;\n        }\n\n        _balances[_address] =\n        _balances[_address] - _amount;\n\n        emit Transfer(\n            _address,\n            ZERO_ADDRESS,\n            _amount\n        );\n    }\n\n    /**\n     * @dev Updates balances during transfer\n     */\n    function _transfer(\n        address _sender,\n        address _recipient,\n        uint256 _amount\n    )\n        internal\n    {\n        _balances[_sender] =\n        _balances[_sender] - _amount;\n\n        unchecked {\n            _balances[_recipient] =\n            _balances[_recipient] + _amount;\n        }\n\n        emit Transfer(\n            _sender,\n            _recipient,\n            _amount\n        );\n    }\n\n    /**\n     * @dev Grants permission for receipt tokens transfer on owner's behalf\n     */\n    function approve(\n        address _spender,\n        uint256 _amount\n    )\n        external\n        returns (bool)\n    {\n        _approve(\n            msg.sender,\n            _spender,\n            _amount\n        );\n\n        return true;\n    }\n\n    /**\n     * @dev Checks value for receipt tokens transfer on owner's behalf\n     */\n    function allowance(\n        address _owner,\n        address _spender\n    )\n        external\n        view\n        returns (uint256)\n    {\n        return _allowances[_owner][_spender];\n    }\n\n    /**\n     * @dev Allowance update for receipt tokens transfer on owner's behalf\n     */\n    function _approve(\n        address _owner,\n        address _spender,\n        uint256 _amount\n    )\n        internal\n    {\n        _allowances[_owner][_spender] = _amount;\n\n        emit Approval(\n            _owner,\n            _spender,\n            _amount\n        );\n    }\n\n    /**\n     * @dev Increases value for receipt tokens transfer on owner's behalf\n     */\n    function increaseAllowance(\n        address _spender,\n        uint256 _addedValue\n    )\n        external\n        returns (bool)\n    {\n        _approve(\n            msg.sender,\n            _spender,\n            _allowances[msg.sender][_spender] + _addedValue\n        );\n\n        return true;\n    }\n\n    /**\n     * @dev Decreases value for receipt tokens transfer on owner's behalf\n     */\n    function decreaseAllowance(\n        address _spender,\n        uint256 _subtractedValue\n    )\n        external\n        returns (bool)\n    {\n        _approve(\n            msg.sender,\n            _spender,\n            _allowances[msg.sender][_spender] - _subtractedValue\n        );\n\n        return true;\n    }\n}\n"
      },
      "contracts/SafeERC20.sol": {
        "content": "// SPDX-License-Identifier: -- BCOM --\n\npragma solidity =0.8.25;\n\nimport \"./IERC20.sol\";\n\ncontract SafeERC20 {\n\n    /**\n     * @dev Allows to execute transfer for a token\n     */\n    function safeTransfer(\n        IERC20 _token,\n        address _to,\n        uint256 _value\n    )\n        internal\n    {\n        callOptionalReturn(\n            _token,\n            abi.encodeWithSelector(\n                _token.transfer.selector,\n                _to,\n                _value\n            )\n        );\n    }\n\n    /**\n     * @dev Allows to execute transferFrom for a token\n     */\n    function safeTransferFrom(\n        IERC20 _token,\n        address _from,\n        address _to,\n        uint256 _value\n    )\n        internal\n    {\n        callOptionalReturn(\n            _token,\n            abi.encodeWithSelector(\n                _token.transferFrom.selector,\n                _from,\n                _to,\n                _value\n            )\n        );\n    }\n\n    function callOptionalReturn(\n        IERC20 _token,\n        bytes memory _data\n    )\n        private\n    {\n        (\n            bool success,\n            bytes memory returndata\n        ) = address(_token).call(_data);\n\n        require(\n            success,\n            \"SafeERC20: CALL_FAILED\"\n        );\n\n        if (returndata.length > 0) {\n            require(\n                abi.decode(\n                    returndata,\n                    (bool)\n                ),\n                \"SafeERC20: OPERATION_FAILED\"\n            );\n        }\n    }\n}\n"
      },
      "contracts/IERC20.sol": {
        "content": "// SPDX-License-Identifier: -- BCOM --\n\npragma solidity =0.8.25;\n\ninterface IERC20 {\n\n    /**\n     * @dev Interface fo transfer function\n     */\n    function transfer(\n        address recipient,\n        uint256 amount\n    )\n        external\n        returns (bool);\n\n    /**\n     * @dev Interface for transferFrom function\n     */\n    function transferFrom(\n        address sender,\n        address recipient,\n        uint256 amount\n    )\n        external\n        returns (bool);\n\n    /**\n     * @dev Interface for approve function\n     */\n    function approve(\n        address spender,\n        uint256 amount\n    )\n        external\n        returns (bool);\n\n    function balanceOf(\n        address account\n    )\n        external\n        view\n        returns (uint256);\n\n        function mint(\n            address _user,\n            uint256 _amount\n        )\n            external;\n}\n"
      }
    },
    "settings": {
      "remappings": [
        "@chainlink/=node_modules/@chainlink/",
        "@ensdomains/=node_modules/@ensdomains/",
        "@eth-optimism/=node_modules/@eth-optimism/",
        "@openzeppelin/=node_modules/@openzeppelin/",
        "ds-test/=lib/forge-std/lib/ds-test/src/",
        "eth-gas-reporter/=node_modules/eth-gas-reporter/",
        "forge-std/=lib/forge-std/src/",
        "hardhat/=node_modules/hardhat/",
        "truffle/=node_modules/truffle/"
      ],
      "optimizer": {
        "enabled": true,
        "runs": 9999999
      },
      "metadata": {
        "useLiteralContent": false,
        "bytecodeHash": "ipfs",
        "appendCBOR": true
      },
      "outputSelection": {
        "*": {
          "*": [
            "evm.bytecode",
            "evm.deployedBytecode",
            "devdoc",
            "userdoc",
            "metadata",
            "abi"
          ]
        }
      },
      "evmVersion": "shanghai",
      "viaIR": false,
      "libraries": {}
    }
  }}