diff --git a/.gitignore b/.gitignore index 22f4761..7f15958 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,6 @@ report/ # Node.js deps node_modules/ + +# remappings +remappings.txt diff --git a/.gitmodules b/.gitmodules index fbe83c7..3689dcf 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,6 @@ [submodule "lib/forge-std"] path = lib/forge-std url = https://github.com/foundry-rs/forge-std +[submodule "lib/solmate"] + path = lib/solmate + url = https://github.com/transmissions11/solmate diff --git a/lib/solmate b/lib/solmate new file mode 160000 index 0000000..2001af4 --- /dev/null +++ b/lib/solmate @@ -0,0 +1 @@ +Subproject commit 2001af43aedb46fdc2335d2a7714fb2dae7cfcd1 diff --git a/src/Farm.sol b/src/Farm.sol index 94e3f03..56e9587 100644 --- a/src/Farm.sol +++ b/src/Farm.sol @@ -1,12 +1,8 @@ pragma solidity ^0.8.14; import {GemAbstract} from "dss-interfaces/ERC/GemAbstract.sol"; -import {SafeMath} from "./utils/SafeMath.sol"; -import {ReentrancyGuard} from "./utils/ReentrancyGuard.sol"; - -contract Farm is ReentrancyGuard { - using SafeMath for uint256; +contract Farm { GemAbstract public immutable rewardGem; GemAbstract public immutable gem; @@ -15,17 +11,16 @@ contract Farm is ReentrancyGuard { mapping(address => uint256) public userRewardPerTokenPaid; mapping(address => uint256) public rewards; + uint256 public live; uint256 public periodFinish = 0; uint256 public rewardRate = 0; uint256 public rewardsDuration = 7 days; uint256 public lastUpdateTime; uint256 public rewardPerTokenStored; - uint public lastPauseTime; - bool public paused; address public rewardsDistribution; - uint256 private _totalSupply; mapping(address => uint256) private _balances; + uint256 private _totalSupply; /** * @notice `usr` was granted admin access. @@ -38,12 +33,30 @@ contract Farm is ReentrancyGuard { * @param usr The user address. */ event Deny(address indexed usr); - event PauseChanged(bool isPaused); + /** + * @notice A contract parameter was updated. + * @param what The changed parameter name. Currently the supported values are: "rewardDuration". + * @param data The new value of the parameter. + */ + event File(bytes32 indexed what, uint256 data); + /** + * @notice A contract parameter was updated. + * @param what The changed parameter name. Currently the supported values are: "rewardsDistribution". + * @param data The new value of the parameter. + */ + event File(bytes32 indexed what, address data); + /** + * @notice Recover ERC20 token `amt` to `usr`. + * @param token The token address. + * @param usr The destination address. + * @param amt The amount of `token` flushed out. + */ + event Yank(address indexed token, address indexed usr, uint256 amt); + event RewardAdded(uint256 reward); event Staked(address indexed user, uint256 amount); event Withdrawn(address indexed user, uint256 amount); event RewardPaid(address indexed user, uint256 reward); - event RewardsDurationUpdated(uint256 newDuration); event Recovered(address token, uint256 amt, address to); /** @@ -54,13 +67,8 @@ contract Farm is ReentrancyGuard { _; } - modifier notPaused() { - require(!paused, "Farm/is-paused"); - _; - } - - modifier onlyRewardsDistribution() { - require(msg.sender == rewardsDistribution, "Farm/not-rewards-distribution"); + modifier isLive() { + require(live == 1, "Farm/not-live"); _; } @@ -74,15 +82,20 @@ contract Farm is ReentrancyGuard { _; } - constructor(address _rewardsDistribution, address _rewardGem, address _gem) public { + constructor(address _rewardsDistribution, address _rewardGem, address _gem) { rewardGem = GemAbstract(_rewardGem); gem = GemAbstract(_gem); rewardsDistribution = _rewardsDistribution; + live = 1; wards[msg.sender] = 1; emit Rely(msg.sender); } + /*////////////////////////////////// + Authorization + //////////////////////////////////*/ + /** * @notice Grants `usr` admin access to this contract. * @param usr The user address. @@ -101,39 +114,59 @@ contract Farm is ReentrancyGuard { emit Deny(usr); } - function setRewardsDuration(uint256 _rewardsDuration) external auth { - require(block.timestamp > periodFinish, "Farm/period-no-finished"); - rewardsDuration = _rewardsDuration; - emit RewardsDurationUpdated(rewardsDuration); - } + /*////////////////////////////////// + Administration + //////////////////////////////////*/ - function setRewardsDistribution(address _rewardsDistribution) external auth { - rewardsDistribution = _rewardsDistribution; + /** + * @notice Updates a contract parameter. + * @dev Reward duration can be updated only when previouse distribution is done + * @param what The changed parameter name. `rewardDuration` + * @param data The new value of the parameter. + */ + function file(bytes32 what, uint256 data) external auth { + if (what == "rewardDuration") { + require(block.timestamp > periodFinish, "Farm/period-no-finished"); + rewardsDuration = data; + } else { + revert("Farm/unrecognised-param"); + } + + emit File(what, data); } /** - * @notice Change the paused state of the contract - * @dev Only the contract owner may call this. + * @notice Updates a contract parameter. + * @param what The changed parameter name. `rewardDistribution` + * @param data The new value of the parameter. */ - function setPaused(bool _paused) external auth { - // Ensure we're actually changing the state before we do anything - if (_paused == paused) { - return; + function file(bytes32 what, address data) external auth { + if (what == "rewardDistribution") { + rewardsDistribution = data; + } else { + revert("Farm/unrecognised-param"); } - // Set our paused state. - paused = _paused; + emit File(what, data); + } - // If applicable, set the last pause time. - if (paused) { - lastPauseTime = block.timestamp; - } + /** + * @notice Cage farm + */ + function cage() external auth { + live = 0; + } - // Let everyone know that our pause state has changed. - emit PauseChanged(paused); + /** + * @notice Escape from cage + */ + function escape() external auth { + live = 1; } - /* ========== VIEWS ========== */ + /*////////////////////////////////// + View + //////////////////////////////////*/ function totalSupply() external view returns (uint256) { return _totalSupply; @@ -152,41 +185,49 @@ contract Farm is ReentrancyGuard { return rewardPerTokenStored; } return - rewardPerTokenStored.add( - lastTimeRewardApplicable().sub(lastUpdateTime).mul(rewardRate).mul(1e18).div(_totalSupply) + _add( + rewardPerTokenStored, + _div(_mul(_sub(lastTimeRewardApplicable(), lastUpdateTime), rewardRate * 1e18), _totalSupply) ); } function earned(address account) public view returns (uint256) { return - _balances[account].mul(rewardPerToken().sub(userRewardPerTokenPaid[account])).div(1e18).add( + _add( + _div(_mul(_balances[account], _sub(rewardPerToken(), userRewardPerTokenPaid[account])), 1e18), rewards[account] ); } function getRewardForDuration() external view returns (uint256) { - return rewardRate.mul(rewardsDuration); + return _mul(rewardRate, rewardsDuration); } - /* ========== MUTATIVE FUNCTIONS ========== */ + /*////////////////////////////////// + Operations + //////////////////////////////////*/ - function stake(uint256 amount) external nonReentrant notPaused updateReward(msg.sender) { + function stake(uint256 amount) external isLive updateReward(msg.sender) { require(amount > 0, "Farm/invalid-amount"); - _totalSupply = _totalSupply.add(amount); - _balances[msg.sender] = _balances[msg.sender].add(amount); + + _totalSupply = _add(_totalSupply, amount); + _balances[msg.sender] = _add(_balances[msg.sender], amount); gem.transferFrom(msg.sender, address(this), amount); + emit Staked(msg.sender, amount); } - function withdraw(uint256 amount) public nonReentrant updateReward(msg.sender) { + function withdraw(uint256 amount) public updateReward(msg.sender) { require(amount > 0, "Farm/invalid-amount"); - _totalSupply = _totalSupply.sub(amount); - _balances[msg.sender] = _balances[msg.sender].sub(amount); + + _totalSupply = _sub(_totalSupply, amount); + _balances[msg.sender] = _sub(_balances[msg.sender], amount); gem.transfer(msg.sender, amount); + emit Withdrawn(msg.sender, amount); } - function getReward() public nonReentrant updateReward(msg.sender) { + function getReward() public updateReward(msg.sender) { uint256 reward = rewards[msg.sender]; if (reward > 0) { rewards[msg.sender] = 0; @@ -200,19 +241,30 @@ contract Farm is ReentrancyGuard { getReward(); } - function recoverERC20(address token, uint256 amt, address to) external auth { + /** + * @notice Flushes out `amt` of `token` sitting in this contract to `usr` address. + * @dev Can only be called by the admin. + * @param token Token address. + * @param amt Token amount. + * @param usr Destination address. + */ + function yank(address token, uint256 amt, address usr) external auth { require(token != address(gem), "Farm/gem-not-allowed"); - GemAbstract(token).transfer(to, amt); - emit Recovered(token, amt, to); + + GemAbstract(token).transfer(usr, amt); + + emit Yank(token, usr, amt); } - function notifyRewardAmount(uint256 reward) external onlyRewardsDistribution updateReward(address(0)) { + function notifyRewardAmount(uint256 reward) external updateReward(address(0)) { + require(wards[msg.sender] == 1 || msg.sender == rewardsDistribution, "Farm/not-authorized"); + if (block.timestamp >= periodFinish) { - rewardRate = reward.div(rewardsDuration); + rewardRate = _div(reward, rewardsDuration); } else { - uint256 remaining = periodFinish.sub(block.timestamp); - uint256 leftover = remaining.mul(rewardRate); - rewardRate = reward.add(leftover).div(rewardsDuration); + uint256 remaining = _sub(periodFinish, block.timestamp); + uint256 leftover = _mul(remaining, rewardRate); + rewardRate = _div(_add(reward, leftover), rewardsDuration); } // Ensure the provided reward amount is not more than the balance in the contract. @@ -220,10 +272,40 @@ contract Farm is ReentrancyGuard { // very high values of rewardRate in the earned and rewardsPerToken functions; // Reward + leftover must be less than 2^256 / 10^18 to avoid overflow. uint balance = rewardGem.balanceOf(address(this)); - require(rewardRate <= balance.div(rewardsDuration), "Farm/invalid-reward"); + require(rewardRate <= _div(balance, rewardsDuration), "Farm/invalid-reward"); lastUpdateTime = block.timestamp; - periodFinish = block.timestamp.add(rewardsDuration); + periodFinish = _add(block.timestamp, rewardsDuration); + emit RewardAdded(reward); } + + /*////////////////////////////////// + Math + //////////////////////////////////*/ + + function _add(uint256 x, uint256 y) internal pure returns (uint256 z) { + unchecked { + require((z = x + y) >= x, "Math/add-overflow"); + } + } + + function _sub(uint256 x, uint256 y) internal pure returns (uint256 z) { + unchecked { + require((z = x - y) <= x, "Math/sub-overflow"); + } + } + + function _mul(uint256 x, uint256 y) internal pure returns (uint256 z) { + unchecked { + require(y == 0 || (z = x * y) / y == x, "Math/mul-overflow"); + } + } + + function _div(uint x, uint y) internal pure returns (uint z) { + unchecked { + require(y > 0, "Math/divide-by-zero"); + return x / y; + } + } } diff --git a/src/Farm.t.sol b/src/Farm.t.sol new file mode 100644 index 0000000..d6c96e0 --- /dev/null +++ b/src/Farm.t.sol @@ -0,0 +1,433 @@ +pragma experimental ABIEncoderV2; +pragma solidity ^0.8.14; + +import "forge-std/Test.sol"; +import {ERC20} from "solmate/tokens/ERC20.sol"; +import {Farm} from "./Farm.sol"; + +contract FarmTest is Test { + uint256 internal constant WAD = 10 ** 18; + + TestToken internal rewardGem; + TestToken internal gem; + Farm internal farm; + + event Rely(address indexed usr); + event Deny(address indexed usr); + event RewardAdded(uint256 reward); + event Staked(address indexed user, uint256 amount); + event Withdrawn(address indexed user, uint256 amount); + event RewardPaid(address indexed user, uint256 reward); + event Yank(address indexed token, address indexed usr, uint256 amt); + event File(bytes32 indexed what, uint256 data); + event File(bytes32 indexed what, address data); + + function div(uint256 a, uint256 b) internal pure returns (uint256) { + require(b > 0, "ERR"); + return a / b; + } + + function setupReward(uint256 amt) internal { + rewardGem.mint(amt); + rewardGem.transfer(address(farm), amt); + farm.notifyRewardAmount(amt); + } + + function setupStakingToken(uint256 amt) internal { + gem.mint(amt); + gem.approve(address(farm), amt); + } + + function setUp() public { + rewardGem = new TestToken("SubDaoT", 18); + gem = new TestToken("MKR", 18); + + farm = new Farm(address(this), address(rewardGem), address(gem)); + } + + function testConstructor() public { + vm.expectEmit(true, true, true, true); + emit Rely(address(this)); + Farm f = new Farm(address(this), address(rewardGem), address(gem)); + + assertEq(address(f.rewardGem()), address(rewardGem)); + assertEq(address(f.gem()), address(gem)); + assertEq(f.rewardsDistribution(), address(this)); + assertEq(f.wards(address(this)), 1); + } + + function testRelyDeny() public { + assertEq(farm.wards(address(0)), 0); + + // -------------------- + vm.expectEmit(true, false, false, false); + emit Rely(address(0)); + + farm.rely(address(0)); + + assertEq(farm.wards(address(0)), 1); + + // -------------------- + vm.expectEmit(true, false, false, false); + emit Deny(address(0)); + + farm.deny(address(0)); + + assertEq(farm.wards(address(0)), 0); + } + + function testFileRewardDistribution() public { + farm.file(bytes32("rewardDistribution"), address(0)); + assertEq(farm.rewardsDistribution(), address(0)); + } + + function testRevertOnUnauthorizedMethods() public { + vm.startPrank(address(0)); + + vm.expectRevert("Farm/not-authorized"); + farm.rely(address(0)); + + vm.expectRevert("Farm/not-authorized"); + farm.deny(address(0)); + + vm.expectRevert("Farm/not-authorized"); + farm.file(bytes32("rewardsDistribution"), address(0)); + + vm.expectRevert("Farm/not-authorized"); + farm.file(bytes32("rewardsDuration"), 1 days); + + vm.expectRevert("Farm/not-authorized"); + farm.cage(); + + vm.expectRevert("Farm/not-authorized"); + farm.escape(); + + vm.expectRevert("Farm/not-authorized"); + farm.yank(address(0), 1, address(0)); + } + + function testRevertStakeWhenCaged() public { + farm.cage(); + + vm.expectRevert("Farm/not-live"); + farm.stake(1); + } + + function testEscape() public { + farm.cage(); + + vm.expectRevert("Farm/not-live"); + farm.stake(1); + + farm.escape(); + + setupStakingToken(1); + farm.stake(1); + } + + function testRevertOnYankingStakingToken() public { + vm.expectRevert("Farm/gem-not-allowed"); + farm.yank(address(gem), 1, address(this)); + } + + function testYank() public { + TestToken t = new TestToken("TT", 18); + t.mint(10); + t.transfer(address(farm), 10); + + assertEq(t.balanceOf(address(farm)), 10); + + vm.expectEmit(true, true, true, true); + emit Yank(address(t), address(this), 10); + + farm.yank(address(t), 10, address(this)); + + assertEq(t.balanceOf(address(farm)), 0); + assertEq(t.balanceOf(address(this)), 10); + } + + function testLastTimeRewardApplicable() public { + assertEq(farm.lastTimeRewardApplicable(), 0); + + setupReward(10 * WAD); + + assertEq(farm.lastTimeRewardApplicable(), block.timestamp); + } + + function testRewardPerToken() public { + assertEq(farm.rewardPerToken(), 0); + + setupStakingToken(100 * WAD); + farm.stake(100 * WAD); + + assertEq(farm.totalSupply(), 100 * WAD); + + setupReward(5000 * WAD); + + skip(1 days); + + assert(farm.rewardPerToken() > 0); + } + + function testStakeEmitEvent() public { + setupStakingToken(100 * WAD); + + vm.expectEmit(true, true, true, true); + emit Staked(address(this), 100 * WAD); + farm.stake(100 * WAD); + } + + function testStaking() public { + setupStakingToken(100 * WAD); + + uint256 gemBalance = gem.balanceOf(address(this)); + + farm.stake(100 * WAD); + + assertEq(farm.balanceOf(address(this)), 100 * WAD); + assertEq(gem.balanceOf(address(this)), gemBalance - 100 * WAD); + assertEq(gem.balanceOf(address(farm)), 100 * WAD); + } + + function testRevertOnZeroStake() public { + vm.expectRevert("Farm/invalid-amount"); + farm.stake(0); + } + + function testEarned() public { + assertEq(farm.earned(address(this)), 0); + + setupStakingToken(100 * WAD); + farm.stake(100 * WAD); + + setupReward(5000 * WAD); + + skip(1 days); + + assert(farm.earned(address(this)) > 0); + } + + function testRewardRateIncreaseOnNewRewardBeforeDurationEnd() public { + setupReward(5000 * WAD); + + uint256 rewardRate = farm.rewardRate(); + + setupReward(5000 * WAD); + + assert(rewardRate > 0); + assert(farm.rewardRate() > rewardRate); + } + + function earnedShouldIncreaseAfterDuration() public { + setupStakingToken(100 * WAD); + farm.stake(100 * WAD); + + setupStakingToken(5000 * WAD); + + skip(7 days); + + uint256 earned = farm.earned(address(this)); + + setupStakingToken(5000 * WAD); + + skip(7 days); + + assertEq(farm.earned(address(this)), earned + earned); + } + + function testGetRewardEvent() public { + setupStakingToken(100 * WAD); + farm.stake(100 * WAD); + + setupReward(5000 * WAD); + + skip(1 days); + + vm.expectEmit(true, true, true, true); + emit RewardPaid(address(this), farm.rewardRate() * 1 days); + farm.getReward(); + } + + function testGetReward() public { + setupStakingToken(100 * WAD); + farm.stake(100 * WAD); + + setupReward(5000 * WAD); + + skip(1 days); + + uint256 rewardBalance = rewardGem.balanceOf(address(this)); + uint256 earned = farm.earned(address(this)); + + farm.getReward(); + + assert(farm.earned(address(this)) < earned); + assert(rewardGem.balanceOf(address(this)) > rewardBalance); + } + + function testFileRewardDurationEvent() public { + vm.expectEmit(true, true, true, true); + emit File(bytes32("rewardDuration"), 70 days); + farm.file(bytes32("rewardDuration"), 70 days); + } + + function testFileRewardDurationBeforeDistribution() public { + assertEq(farm.rewardsDuration(), 7 days); + + farm.file(bytes32("rewardDuration"), 70 days); + + assertEq(farm.rewardsDuration(), 70 days); + } + + function testRevertFileRewardDurationOnActiveDistribution() public { + setupStakingToken(100 * WAD); + farm.stake(100 * WAD); + + setupReward(100 * WAD); + + skip(1 days); + + vm.expectRevert("Farm/period-no-finished"); + farm.file(bytes32("rewardDuration"), 70 days); + } + + function testFileRewardDurationAfterDistributionPeriod() public { + setupStakingToken(100 * WAD); + farm.stake(100 * WAD); + + setupReward(100 * WAD); + + skip(8 days); + + farm.file(bytes32("rewardDuration"), 70 days); + assertEq(farm.rewardsDuration(), 70 days); + } + + function testGetRewardForDuration() public { + setupReward(5000 * WAD); + + uint256 rewardForDuration = farm.getRewardForDuration(); + uint256 rewardDuration = farm.rewardsDuration(); + uint256 rewardRate = farm.rewardRate(); + + assert(rewardForDuration > 0); + assertEq(rewardForDuration, rewardRate * rewardDuration); + } + + function testWithdrawalEvent() public { + setupStakingToken(100 * WAD); + farm.stake(100 * WAD); + + vm.expectEmit(true, true, true, true); + emit Withdrawn(address(this), 1 * WAD); + farm.withdraw(1 * WAD); + } + + function testFailtIfNothingToWithdraw() public { + farm.withdraw(1); + } + + function testRevertOnZeroWithdraw() public { + vm.expectRevert("Farm/invalid-amount"); + farm.withdraw(0); + } + + function testWithdrwal() public { + setupStakingToken(100 * WAD); + farm.stake(100 * WAD); + + uint256 initialStakeBalance = farm.balanceOf(address(this)); + + farm.withdraw(100 * WAD); + + assertEq(initialStakeBalance, farm.balanceOf(address(this)) + 100 * WAD); + assertEq(gem.balanceOf(address(this)), 100 * WAD); + } + + function testExit() public { + setupStakingToken(100 * WAD); + farm.stake(100 * WAD); + + setupReward(500 * WAD); + + skip(1 days); + + farm.exit(); + + assertEq(farm.earned(address(this)), 0); + assertEq(gem.balanceOf(address(this)), 100 * WAD); + assertEq(rewardGem.balanceOf(address(this)), farm.rewardRate() * 1 days); + } + + function testNotifyRewardEvent() public { + vm.expectEmit(true, true, true, true); + emit RewardAdded(100 * WAD); + + setupReward(100 * WAD); + } + + function testRevertOnNotBeingRewardDistributor() public { + vm.prank(address(0)); + vm.expectRevert("Farm/not-authorized"); + farm.notifyRewardAmount(1); + } + + function testRevertOnRewardGreaterThenBalance() public { + rewardGem.mint(100 * WAD); + rewardGem.transfer(address(farm), 100 * WAD); + + vm.expectRevert("Farm/invalid-reward"); + farm.notifyRewardAmount(101 * WAD); + } + + function testRevertOnRewardGreaterThenBalancePlusRollOverBalance() public { + setupReward(100 * WAD); + + rewardGem.mint(100 * WAD); + rewardGem.transfer(address(farm), 100 * WAD); + + vm.expectRevert("Farm/invalid-reward"); + farm.notifyRewardAmount(101 * WAD); + } + + function testFarm() public { + uint256 staked = 100 * WAD; + + setupStakingToken(staked); + farm.stake(staked); + + setupReward(5000 * WAD); + + // Period finish should be 7 days from now + assertEq(farm.periodFinish(), block.timestamp + 7 days); + + // Reward duration is 7 days, so we'll + // skip by 6 days to prevent expiration + skip(6 days); + + // Make sure we earned in proportion to reward per token + assertEq(farm.earned(address(this)), (farm.rewardPerToken() * staked) / WAD); + + // Make sure we get staking token after withdrawal and we still have the same amount earned + farm.withdraw(20 * WAD); + assertEq(gem.balanceOf(address(this)), 20 * WAD); + assertEq(farm.earned(address(this)), (farm.rewardPerToken() * staked) / WAD); + + // Get rewards + farm.getReward(); + assertEq(rewardGem.balanceOf(address(this)), (farm.rewardPerToken() * staked) / WAD); + assertEq(farm.earned(address(this)), 0); + + // exit + farm.exit(); + assertEq(gem.balanceOf(address(this)), staked); + } +} + +contract TestToken is ERC20 { + constructor(string memory symbol_, uint8 decimals_) ERC20("TestToken", symbol_, decimals) {} + + function mint(uint256 wad) external { + _mint(msg.sender, wad); + } +} diff --git a/src/utils/ReentrancyGuard.sol b/src/utils/ReentrancyGuard.sol deleted file mode 100644 index bb8112b..0000000 --- a/src/utils/ReentrancyGuard.sol +++ /dev/null @@ -1,77 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.8.0) (security/ReentrancyGuard.sol) - -pragma solidity ^0.8.0; - -/** - * @dev Contract module that helps prevent reentrant calls to a function. - * - * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier - * available, which can be applied to functions to make sure there are no nested - * (reentrant) calls to them. - * - * Note that because there is a single `nonReentrant` guard, functions marked as - * `nonReentrant` may not call one another. This can be worked around by making - * those functions `private`, and then adding `external` `nonReentrant` entry - * points to them. - * - * TIP: If you would like to learn more about reentrancy and alternative ways - * to protect against it, check out our blog post - * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. - */ -abstract contract ReentrancyGuard { - // Booleans are more expensive than uint256 or any type that takes up a full - // word because each write operation emits an extra SLOAD to first read the - // slot's contents, replace the bits taken up by the boolean, and then write - // back. This is the compiler's defense against contract upgrades and - // pointer aliasing, and it cannot be disabled. - - // The values being non-zero value makes deployment a bit more expensive, - // but in exchange the refund on every call to nonReentrant will be lower in - // amount. Since refunds are capped to a percentage of the total - // transaction's gas, it is best to keep them low in cases like this one, to - // increase the likelihood of the full refund coming into effect. - uint256 private constant _NOT_ENTERED = 1; - uint256 private constant _ENTERED = 2; - - uint256 private _status; - - constructor() { - _status = _NOT_ENTERED; - } - - /** - * @dev Prevents a contract from calling itself, directly or indirectly. - * Calling a `nonReentrant` function from another `nonReentrant` - * function is not supported. It is possible to prevent this from happening - * by making the `nonReentrant` function external, and making it call a - * `private` function that does the actual work. - */ - modifier nonReentrant() { - _nonReentrantBefore(); - _; - _nonReentrantAfter(); - } - - function _nonReentrantBefore() private { - // On the first call to nonReentrant, _status will be _NOT_ENTERED - require(_status != _ENTERED, "ReentrancyGuard: reentrant call"); - - // Any calls to nonReentrant after this point will fail - _status = _ENTERED; - } - - function _nonReentrantAfter() private { - // By storing the original value once again, a refund is triggered (see - // https://eips.ethereum.org/EIPS/eip-2200) - _status = _NOT_ENTERED; - } - - /** - * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a - * `nonReentrant` function in the call stack. - */ - function _reentrancyGuardEntered() internal view returns (bool) { - return _status == _ENTERED; - } -} \ No newline at end of file diff --git a/src/utils/SafeMath.sol b/src/utils/SafeMath.sol deleted file mode 100644 index 7f6c7d8..0000000 --- a/src/utils/SafeMath.sol +++ /dev/null @@ -1,215 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.6.0) (utils/math/SafeMath.sol) - -pragma solidity ^0.8.0; - -// CAUTION -// This version of SafeMath should only be used with Solidity 0.8 or later, -// because it relies on the compiler's built in overflow checks. - -/** - * @dev Wrappers over Solidity's arithmetic operations. - * - * NOTE: `SafeMath` is generally not needed starting with Solidity 0.8, since the compiler - * now has built in overflow checking. - */ -library SafeMath { - /** - * @dev Returns the addition of two unsigned integers, with an overflow flag. - * - * _Available since v3.4._ - */ - function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) { - unchecked { - uint256 c = a + b; - if (c < a) return (false, 0); - return (true, c); - } - } - - /** - * @dev Returns the subtraction of two unsigned integers, with an overflow flag. - * - * _Available since v3.4._ - */ - function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) { - unchecked { - if (b > a) return (false, 0); - return (true, a - b); - } - } - - /** - * @dev Returns the multiplication of two unsigned integers, with an overflow flag. - * - * _Available since v3.4._ - */ - function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) { - unchecked { - // Gas optimization: this is cheaper than requiring 'a' not being zero, but the - // benefit is lost if 'b' is also tested. - // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 - if (a == 0) return (true, 0); - uint256 c = a * b; - if (c / a != b) return (false, 0); - return (true, c); - } - } - - /** - * @dev Returns the division of two unsigned integers, with a division by zero flag. - * - * _Available since v3.4._ - */ - function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) { - unchecked { - if (b == 0) return (false, 0); - return (true, a / b); - } - } - - /** - * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag. - * - * _Available since v3.4._ - */ - function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) { - unchecked { - if (b == 0) return (false, 0); - return (true, a % b); - } - } - - /** - * @dev Returns the addition of two unsigned integers, reverting on - * overflow. - * - * Counterpart to Solidity's `+` operator. - * - * Requirements: - * - * - Addition cannot overflow. - */ - function add(uint256 a, uint256 b) internal pure returns (uint256) { - return a + b; - } - - /** - * @dev Returns the subtraction of two unsigned integers, reverting on - * overflow (when the result is negative). - * - * Counterpart to Solidity's `-` operator. - * - * Requirements: - * - * - Subtraction cannot overflow. - */ - function sub(uint256 a, uint256 b) internal pure returns (uint256) { - return a - b; - } - - /** - * @dev Returns the multiplication of two unsigned integers, reverting on - * overflow. - * - * Counterpart to Solidity's `*` operator. - * - * Requirements: - * - * - Multiplication cannot overflow. - */ - function mul(uint256 a, uint256 b) internal pure returns (uint256) { - return a * b; - } - - /** - * @dev Returns the integer division of two unsigned integers, reverting on - * division by zero. The result is rounded towards zero. - * - * Counterpart to Solidity's `/` operator. - * - * Requirements: - * - * - The divisor cannot be zero. - */ - function div(uint256 a, uint256 b) internal pure returns (uint256) { - return a / b; - } - - /** - * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), - * reverting when dividing by zero. - * - * Counterpart to Solidity's `%` operator. This function uses a `revert` - * opcode (which leaves remaining gas untouched) while Solidity uses an - * invalid opcode to revert (consuming all remaining gas). - * - * Requirements: - * - * - The divisor cannot be zero. - */ - function mod(uint256 a, uint256 b) internal pure returns (uint256) { - return a % b; - } - - /** - * @dev Returns the subtraction of two unsigned integers, reverting with custom message on - * overflow (when the result is negative). - * - * CAUTION: This function is deprecated because it requires allocating memory for the error - * message unnecessarily. For custom revert reasons use {trySub}. - * - * Counterpart to Solidity's `-` operator. - * - * Requirements: - * - * - Subtraction cannot overflow. - */ - function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { - unchecked { - require(b <= a, errorMessage); - return a - b; - } - } - - /** - * @dev Returns the integer division of two unsigned integers, reverting with custom message on - * division by zero. The result is rounded towards zero. - * - * Counterpart to Solidity's `/` operator. Note: this function uses a - * `revert` opcode (which leaves remaining gas untouched) while Solidity - * uses an invalid opcode to revert (consuming all remaining gas). - * - * Requirements: - * - * - The divisor cannot be zero. - */ - function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { - unchecked { - require(b > 0, errorMessage); - return a / b; - } - } - - /** - * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), - * reverting with custom message when dividing by zero. - * - * CAUTION: This function is deprecated because it requires allocating memory for the error - * message unnecessarily. For custom revert reasons use {tryMod}. - * - * Counterpart to Solidity's `%` operator. This function uses a `revert` - * opcode (which leaves remaining gas untouched) while Solidity uses an - * invalid opcode to revert (consuming all remaining gas). - * - * Requirements: - * - * - The divisor cannot be zero. - */ - function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { - unchecked { - require(b > 0, errorMessage); - return a % b; - } - } -} \ No newline at end of file