creation_sourcecode: {{
    "language": "Solidity",
    "sources": {
      "src/staking/Staking.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.17;\n\nimport {IStaking} from \"../interfaces/IStaking.sol\";\n\nimport {Owned} from \"@solmate/src/auth/Owned.sol\";\nimport {ERC20} from \"@solmate/src/tokens/ERC20.sol\";\n\n// from https://solidity-by-example.org/defi/staking-rewards/\ncontract Staking is IStaking, Owned(msg.sender) {\n    ERC20 public immutable stakingToken;\n    ERC20 public immutable rewardsToken;\n\n    // Duration of rewards to be paid out (in seconds)\n    uint public duration; \n    // Timestamp of when the rewards finish\n    uint public finishAt;\n    // Minimum of last updated time and reward finish time\n    uint public updatedAt;\n    // Reward to be paid out per second\n    uint public rewardRate;\n    // Sum of (reward rate * dt * 1e18 / total supply)\n    uint public rewardPerTokenStored;\n    // User address => rewardPerTokenStored\n    mapping(address => uint) public userRewardPerTokenPaid;\n    // User address => rewards to be claimed\n    mapping(address => uint) public rewards;\n\n    // Total staked\n    uint public totalSupply;\n    // User address => staked amount\n    mapping(address => uint) public balanceOf;\n\n    constructor(ERC20 _stakingToken, ERC20 _rewardToken) {\n      stakingToken = _stakingToken;\n      rewardsToken = _rewardToken;\n    }\n\n    modifier updateReward(address _account) {\n      rewardPerTokenStored = rewardPerToken();\n      updatedAt = lastTimeRewardApplicable();\n\n      if (_account != address(0)) {\n          rewards[_account] = earned(_account);\n          userRewardPerTokenPaid[_account] = rewardPerTokenStored;\n      }\n\n      _;\n    }\n\n    function lastTimeRewardApplicable() public view returns (uint) {\n      return _min(finishAt, block.timestamp);\n    }\n\n    function rewardPerToken() public view returns (uint) {\n      if (totalSupply == 0) {\n          return rewardPerTokenStored;\n      }\n\n      return\n          rewardPerTokenStored +\n          (rewardRate * (lastTimeRewardApplicable() - updatedAt) * 1e18) /\n          totalSupply;\n    }\n\n    function stake(uint _amount) external updateReward(msg.sender) {\n      require(_amount > 0, \"amount = 0\");\n      stakingToken.transferFrom(msg.sender, address(this), _amount);\n      balanceOf[msg.sender] += _amount;\n      totalSupply += _amount;\n      emit Staked(msg.sender, _amount);\n    }\n\n    function withdraw(uint _amount) external updateReward(msg.sender) {\n      require(_amount > 0, \"amount = 0\");\n      balanceOf[msg.sender] -= _amount;\n      totalSupply -= _amount;\n      stakingToken.transfer(msg.sender, _amount);\n      emit Withdrawn(msg.sender, _amount);\n    }\n\n    function earned(address _account) public view returns (uint) {\n      return\n          ((balanceOf[_account] *\n              (rewardPerToken() - userRewardPerTokenPaid[_account])) / 1e18) +\n          rewards[_account];\n    }\n\n    function getReward() external updateReward(msg.sender) {\n      uint reward = rewards[msg.sender];\n      if (reward > 0) {\n          rewards[msg.sender] = 0;\n          rewardsToken.transfer(msg.sender, reward);\n          emit RewardPaid(msg.sender, reward);\n      }\n    }\n\n    function setRewardsDuration(uint _duration) external onlyOwner {\n      require(finishAt < block.timestamp, \"reward duration not finished\");\n      duration = _duration;\n      emit RewardsDurationUpdated(_duration);\n    }\n\n    function notifyRewardAmount(\n      uint _amount\n    ) external onlyOwner updateReward(address(0)) {\n      if (block.timestamp >= finishAt) {\n          rewardRate = _amount / duration;\n      } else {\n          uint remainingRewards = (finishAt - block.timestamp) * rewardRate;\n          rewardRate = (_amount + remainingRewards) / duration;\n      }\n\n      require(rewardRate > 0, \"reward rate = 0\");\n      require(\n          rewardRate * duration <= rewardsToken.balanceOf(address(this)),\n          \"reward amount > balance\"\n      );\n\n      finishAt = block.timestamp + duration;\n      updatedAt = block.timestamp;\n      emit RewardAdded(_amount);\n    }\n\n    function _min(uint x, uint y) private pure returns (uint) {\n      return x <= y ? x : y;\n    }\n}\n"
      },
      "src/interfaces/IStaking.sol": {
        "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.17;\n\ninterface IStaking {\n  event Staked                (address indexed user, uint amount);\n  event Withdrawn             (address indexed user, uint amount);\n  event RewardPaid            (address indexed user, uint reward);\n  event RewardAdded           (uint reward);\n  event RewardsDurationUpdated(uint newDuration);\n}\n"
      },
      "lib/solmate/src/auth/Owned.sol": {
        "content": "// SPDX-License-Identifier: AGPL-3.0-only\npragma solidity >=0.8.0;\n\n/// @notice Simple single owner authorization mixin.\n/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/auth/Owned.sol)\nabstract contract Owned {\n    /*//////////////////////////////////////////////////////////////\n                                 EVENTS\n    //////////////////////////////////////////////////////////////*/\n\n    event OwnershipTransferred(address indexed user, address indexed newOwner);\n\n    /*//////////////////////////////////////////////////////////////\n                            OWNERSHIP STORAGE\n    //////////////////////////////////////////////////////////////*/\n\n    address public owner;\n\n    modifier onlyOwner() virtual {\n        require(msg.sender == owner, \"UNAUTHORIZED\");\n\n        _;\n    }\n\n    /*//////////////////////////////////////////////////////////////\n                               CONSTRUCTOR\n    //////////////////////////////////////////////////////////////*/\n\n    constructor(address _owner) {\n        owner = _owner;\n\n        emit OwnershipTransferred(address(0), _owner);\n    }\n\n    /*//////////////////////////////////////////////////////////////\n                             OWNERSHIP LOGIC\n    //////////////////////////////////////////////////////////////*/\n\n    function transferOwnership(address newOwner) public virtual onlyOwner {\n        owner = newOwner;\n\n        emit OwnershipTransferred(msg.sender, newOwner);\n    }\n}\n"
      },
      "lib/solmate/src/tokens/ERC20.sol": {
        "content": "// SPDX-License-Identifier: AGPL-3.0-only\npragma solidity >=0.8.0;\n\n/// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.\n/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)\n/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)\n/// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.\nabstract contract ERC20 {\n    /*//////////////////////////////////////////////////////////////\n                                 EVENTS\n    //////////////////////////////////////////////////////////////*/\n\n    event Transfer(address indexed from, address indexed to, uint256 amount);\n\n    event Approval(address indexed owner, address indexed spender, uint256 amount);\n\n    /*//////////////////////////////////////////////////////////////\n                            METADATA STORAGE\n    //////////////////////////////////////////////////////////////*/\n\n    string public name;\n\n    string public symbol;\n\n    uint8 public immutable decimals;\n\n    /*//////////////////////////////////////////////////////////////\n                              ERC20 STORAGE\n    //////////////////////////////////////////////////////////////*/\n\n    uint256 public totalSupply;\n\n    mapping(address => uint256) public balanceOf;\n\n    mapping(address => mapping(address => uint256)) public allowance;\n\n    /*//////////////////////////////////////////////////////////////\n                            EIP-2612 STORAGE\n    //////////////////////////////////////////////////////////////*/\n\n    uint256 internal immutable INITIAL_CHAIN_ID;\n\n    bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;\n\n    mapping(address => uint256) public nonces;\n\n    /*//////////////////////////////////////////////////////////////\n                               CONSTRUCTOR\n    //////////////////////////////////////////////////////////////*/\n\n    constructor(\n        string memory _name,\n        string memory _symbol,\n        uint8 _decimals\n    ) {\n        name = _name;\n        symbol = _symbol;\n        decimals = _decimals;\n\n        INITIAL_CHAIN_ID = block.chainid;\n        INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();\n    }\n\n    /*//////////////////////////////////////////////////////////////\n                               ERC20 LOGIC\n    //////////////////////////////////////////////////////////////*/\n\n    function approve(address spender, uint256 amount) public virtual returns (bool) {\n        allowance[msg.sender][spender] = amount;\n\n        emit Approval(msg.sender, spender, amount);\n\n        return true;\n    }\n\n    function transfer(address to, uint256 amount) public virtual returns (bool) {\n        balanceOf[msg.sender] -= amount;\n\n        // Cannot overflow because the sum of all user\n        // balances can't exceed the max uint256 value.\n        unchecked {\n            balanceOf[to] += amount;\n        }\n\n        emit Transfer(msg.sender, to, amount);\n\n        return true;\n    }\n\n    function transferFrom(\n        address from,\n        address to,\n        uint256 amount\n    ) public virtual returns (bool) {\n        uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.\n\n        if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;\n\n        balanceOf[from] -= amount;\n\n        // Cannot overflow because the sum of all user\n        // balances can't exceed the max uint256 value.\n        unchecked {\n            balanceOf[to] += amount;\n        }\n\n        emit Transfer(from, to, amount);\n\n        return true;\n    }\n\n    /*//////////////////////////////////////////////////////////////\n                             EIP-2612 LOGIC\n    //////////////////////////////////////////////////////////////*/\n\n    function permit(\n        address owner,\n        address spender,\n        uint256 value,\n        uint256 deadline,\n        uint8 v,\n        bytes32 r,\n        bytes32 s\n    ) public virtual {\n        require(deadline >= block.timestamp, \"PERMIT_DEADLINE_EXPIRED\");\n\n        // Unchecked because the only math done is incrementing\n        // the owner's nonce which cannot realistically overflow.\n        unchecked {\n            address recoveredAddress = ecrecover(\n                keccak256(\n                    abi.encodePacked(\n                        \"\\x19\\x01\",\n                        DOMAIN_SEPARATOR(),\n                        keccak256(\n                            abi.encode(\n                                keccak256(\n                                    \"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)\"\n                                ),\n                                owner,\n                                spender,\n                                value,\n                                nonces[owner]++,\n                                deadline\n                            )\n                        )\n                    )\n                ),\n                v,\n                r,\n                s\n            );\n\n            require(recoveredAddress != address(0) && recoveredAddress == owner, \"INVALID_SIGNER\");\n\n            allowance[recoveredAddress][spender] = value;\n        }\n\n        emit Approval(owner, spender, value);\n    }\n\n    function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {\n        return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();\n    }\n\n    function computeDomainSeparator() internal view virtual returns (bytes32) {\n        return\n            keccak256(\n                abi.encode(\n                    keccak256(\"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)\"),\n                    keccak256(bytes(name)),\n                    keccak256(\"1\"),\n                    block.chainid,\n                    address(this)\n                )\n            );\n    }\n\n    /*//////////////////////////////////////////////////////////////\n                        INTERNAL MINT/BURN LOGIC\n    //////////////////////////////////////////////////////////////*/\n\n    function _mint(address to, uint256 amount) internal virtual {\n        totalSupply += amount;\n\n        // Cannot overflow because the sum of all user\n        // balances can't exceed the max uint256 value.\n        unchecked {\n            balanceOf[to] += amount;\n        }\n\n        emit Transfer(address(0), to, amount);\n    }\n\n    function _burn(address from, uint256 amount) internal virtual {\n        balanceOf[from] -= amount;\n\n        // Cannot underflow because a user's balance\n        // will never be larger than the total supply.\n        unchecked {\n            totalSupply -= amount;\n        }\n\n        emit Transfer(from, address(0), amount);\n    }\n}\n"
      }
    },
    "settings": {
      "remappings": [
        "@openzeppelin/=lib/openzeppelin-contracts/",
        "@solmate/=lib/solmate/",
        "aave-v3-core/=lib/aave-v3-core/",
        "ds-test/=lib/forge-std/lib/ds-test/src/",
        "forge-std/=lib/forge-std/src/",
        "openzeppelin-contracts/=lib/openzeppelin-contracts/",
        "solmate/=lib/solmate/src/",
        "v2-periphery/=lib/v2-periphery/contracts/"
      ],
      "optimizer": {
        "enabled": true,
        "runs": 1000000
      },
      "metadata": {
        "useLiteralContent": false,
        "bytecodeHash": "ipfs"
      },
      "outputSelection": {
        "*": {
          "*": [
            "evm.bytecode",
            "evm.deployedBytecode",
            "devdoc",
            "userdoc",
            "metadata",
            "abi"
          ]
        }
      },
      "evmVersion": "london",
      "viaIR": true,
      "libraries": {}
    }
  }}