Skip to content

Commit

Permalink
fix: shaito suggested fixes implemented, cleaned up repo for future p…
Browse files Browse the repository at this point in the history
…rojects
  • Loading branch information
deo committed Nov 30, 2023
1 parent 9cba32f commit 65faa75
Show file tree
Hide file tree
Showing 12 changed files with 83 additions and 42 deletions.
37 changes: 37 additions & 0 deletions solidity/contracts/Swapper/Keeper.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
pragma solidity ^0.8.19;

import {IKeep3rV2} from '../../interfaces/Swapper/IKeep3rV2.sol';
import {IWETH} from '../../interfaces/Swapper/IWETH.sol';

contract Keeper {
error KeeperNotValid();
error JobNotReady();

uint256 lastWorked;
address keep3r = 0xdc02981c9C062d48a9bD54adBf51b816623dcc6E;
uint256 constant TEN_MINUTES = 600;

IWETH constant WETH = IWETH(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);

modifier validateAndPayKeeper(address _keeper) {
if (!IKeep3rV2(keep3r).isKeeper(_keeper)) revert KeeperNotValid();
_;
IKeep3rV2(keep3r).directTokenPayment(address(WETH), _keeper, 1e17);
}

function work() external validateAndPayKeeper(msg.sender) {
if (!workable()) {
revert JobNotReady();
}

lastWorked = block.timestamp;

swap();
}

function workable() public view returns (bool _workable) {
return block.timestamp >= lastWorked + TEN_MINUTES;
}

function swap() public virtual {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ pragma solidity ^0.8.19;
import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import {SafeERC20} from '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';
import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol';
import {ISwapper} from '../interfaces/ISwapper.sol';
import {ISwapper} from '../../interfaces/Swapper/ISwapper.sol';

/// @title Swapper contract
/// @author 0xdeo
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {SafeERC20} from '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol
import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol';
import {IUniswapV2Router01} from '@uniswap/periphery/interfaces/IUniswapV2Router01.sol';
import {IUniswapV2Pair} from '@uniswap/core/interfaces/IUniswapV2Pair.sol';
import {ISwapperV2} from '../interfaces/ISwapperV2.sol';
import {ISwapperV2} from '../../interfaces/Swapper/ISwapperV2.sol';

/// @title Swapper contract V2
/// @author 0xdeo
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,66 +5,73 @@ import {SafeERC20} from '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol
import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol';
import {IUniswapV2Router01} from '@uniswap/periphery/interfaces/IUniswapV2Router01.sol';
import {IUniswapV2Pair} from '@uniswap/core/interfaces/IUniswapV2Pair.sol';
import {ISwapperV3} from '../interfaces/ISwapperV3.sol';
import {IKeep3rV2} from '../interfaces/IKeep3rV2.sol';
import {ISwapperV3} from '../../interfaces/Swapper/ISwapperV3.sol';
import {Keeper} from './Keeper.sol';

/// @title Swapper contract V2
/// @author 0xdeo
/// @notice Allows friends to pool their tokens and make a swap
/// @notice Routes swaps thru Uniswap
contract SwapperV3 is ISwapperV3 {
contract SwapperV3 is ISwapperV3, Keeper {
using SafeERC20 for IERC20;

IERC20 constant WETH = IERC20(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);
IERC20 constant DAI = IERC20(0x6B175474E89094C44Da98b954EedeAC495271d0F);

IUniswapV2Router01 constant router = IUniswapV2Router01(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D);
IUniswapV2Pair constant pair = IUniswapV2Pair(0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11);

mapping(uint256 => Swap) public swaps;
mapping(uint256 _swapId => Swap _swapData) public swaps;
uint256 swapId;

uint256 lastWorked;
address keep3r = 0xdc02981c9C062d48a9bD54adBf51b816623dcc6E;

constructor() {}

modifier validateAndPayKeeper(address _keeper) {
if (!IKeep3rV2(keep3r).isKeeper(_keeper)) revert KeeperNotValid();
_;
IKeep3rV2(keep3r).directTokenPayment(address(WETH), _keeper, 1e17);
/// @notice Can provide ETH when swap hasn't been done yet
/// @notice Cannot provide tokens if fromToken balance will exceed contract's toToken balance
/// @notice User's deposit gets recorded in latest epoch, and the swapId recording his deposit is then returned
function provide() public payable returns (uint256 _swapId) {
swaps[swapId].balances[msg.sender] += msg.value;

emit Provide(msg.sender, msg.value, swapId);
return swapId;
}

/// @notice Can only provide tokens when swap hasn't been done yet
/// @notice Can provide WETH when swap hasn't been done yet
/// @notice Cannot provide tokens if fromToken balance will exceed contract's toToken balance
/// @notice User's deposit gets recorded in latest epoch, and the swapId recording his deposit is then returned
function provide() public payable returns (uint256) {
swaps[swapId].balances[msg.sender] += msg.value;
/// @param _deposit amount of WETH the user deposited
function provide(uint256 _deposit) public payable returns (uint256 _swapId) {
swaps[swapId].balances[msg.sender] += _deposit;

WETH.transferFrom(msg.sender, address(this), _deposit);

emit Provide(msg.sender, msg.value, swapId);
return swapId;
}

/// @notice Initiates swap by sealing off further deposits and allowing withdrawals of toTokens, for that swapId's epoch
/// @notice Sends any deposited fromTokens to the owner of the contract who provided initial liquidity
function swap() public {
function swap() public virtual override(ISwapperV3, Keeper) {
address[] memory _path = new address[](2);
_path[0] = address(WETH);
_path[1] = address(DAI);
(uint256 _reserve0, uint256 _reserve1,) = pair.getReserves();
uint256 _amountOutMin;
uint256 _amountOut = DAI.balanceOf(address(this));
uint256 _amountOut;
uint256 _amountIn = address(this).balance;

swaps[swapId].swapped = true;
swapId++;
++swapId;

if (address(WETH) == pair.token0()) {
_amountOutMin = router.getAmountOut(address(this).balance, _reserve0, _reserve1);
} else {
_amountOutMin = router.getAmountOut(address(this).balance, _reserve1, _reserve0);
}

if (WETH.balanceOf(address(this)) != 0) {
WETH.withdraw(WETH.balanceOf(address(this)));
}

// 1% slippage to prevent frontrunning
_amountOut = router.swapExactETHForTokens{value: address(this).balance}(
_amountOutMin * 99 / 100, _path, address(this), block.timestamp
Expand Down Expand Up @@ -95,27 +102,18 @@ contract SwapperV3 is ISwapperV3 {
DAI.safeTransfer(msg.sender, balance);
} else {
_refunded = true;
payable(msg.sender).transfer(balance);
}

emit Withdraw(msg.sender, balance, _swapId, _refunded);
}
(bool _success,) = payable(msg.sender).call{value: balance}('');

function work() external validateAndPayKeeper(msg.sender) {
if (!workable()) {
revert JobNotReady();
if (!_success) {
revert RefundFailed();
}
}

lastWorked = block.timestamp;

swap();
emit Withdraw(msg.sender, balance, _swapId, _refunded);
}

function workable() public view returns (bool) {
if (block.timestamp >= lastWorked + 600) {
return true;
}

return false;
receive() external payable {
provide();
}
}
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@ interface ISwapperV3 {
function withdraw(uint256 _swapId) external;

error ZeroBalance();
error KeeperNotValid();
error JobNotReady();
error RefundFailed();

event Provide(address _depositor, uint256 _amount, uint256 _swapId);
event Swapped(uint256 _swapId);
Expand Down
7 changes: 7 additions & 0 deletions solidity/interfaces/Swapper/IWETH.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
pragma solidity ^0.8.19;

import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';

interface IWETH is IERC20 {
function withdraw(uint256 wad) external;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
pragma solidity ^0.8.19;

import {Test, console2} from 'forge-std/Test.sol';

Check warning on line 4 in solidity/test/unit/Swapper/Swapper.t.sol

View workflow job for this annotation

GitHub Actions / Run Linters (16.x)

imported name console2 is not used
import {Swapper} from '../../contracts/Swapper.sol';
import {Swapper} from '../../../contracts/Swapper/Swapper.sol';
import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';

error InsufficientLiquidity();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
pragma solidity ^0.8.19;

import {Test, console2} from 'forge-std/Test.sol';
import {SwapperV2} from '../../contracts/SwapperV2.sol';
import {SwapperV2} from '../../../contracts/Swapper/SwapperV2.sol';
import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';

error InsufficientLiquidity();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
pragma solidity ^0.8.19;

import {Test, console2} from 'forge-std/Test.sol';
import {SwapperV3} from '../../contracts/SwapperV3.sol';
import {SwapperV3} from '../../../contracts/Swapper/SwapperV3.sol';
import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import {IKeep3rV2} from '../../interfaces/IKeep3rV2.sol';
import {IKeep3rV2} from '../../../interfaces/Swapper/IKeep3rV2.sol';

error InsufficientLiquidity();
error ZeroBalance();
Expand Down

0 comments on commit 65faa75

Please sign in to comment.