-
Notifications
You must be signed in to change notification settings - Fork 30
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
deo
committed
Dec 20, 2023
1 parent
6c26f88
commit f8432e7
Showing
3 changed files
with
140 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
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 {IReceiver} from '../../interfaces/Flashloan/IReceiver.sol'; | ||
import {IFlashloan} from '../../interfaces/Flashloan/IFlashloan.sol'; | ||
import {ReentrancyGuard} from '@openzeppelin/contracts/utils/ReentrancyGuard.sol'; | ||
|
||
// Without reentrancy guard, user can make a flashloan attack by using his flashloan to deposit into the pool | ||
contract Flashloan is IFlashloan, ReentrancyGuard { | ||
using SafeERC20 for IERC20; | ||
|
||
mapping(address _user => mapping(address _token => uint256 _amount)) tokenBalances; | ||
mapping(address _user => uint256 _amount) ethBalances; | ||
|
||
constructor() {} | ||
|
||
/// @dev allows users to flashloan any tokens or eth deposited in the pool | ||
/// @dev If address(0) is passed as _token, this is treated as ETH | ||
/// @param _amount flashloan total desired, must not exceed pool balance | ||
/// @param _token token (or eth) to be flashloaned | ||
function flashloan(uint256 _amount, address _token) external nonReentrant { | ||
if (address(_token) == address(0)) { | ||
uint256 poolBalance = address(this).balance; | ||
if (_amount > poolBalance) revert InsufficientPoolBalance(); | ||
|
||
IReceiver(msg.sender).getETH{value: _amount}(); | ||
|
||
uint256 balanceAfter = address(this).balance; | ||
|
||
if (balanceAfter < poolBalance + (poolBalance * 1e13) / 1e18) revert InsufficientRepayment(); | ||
} else { | ||
uint256 poolBalance = IERC20(_token).balanceOf(address(this)); | ||
if (_amount > poolBalance) revert InsufficientPoolBalance(); | ||
|
||
IERC20(_token).safeTransfer(msg.sender, _amount); | ||
IReceiver(msg.sender).getTokens(_token, _amount); | ||
|
||
uint256 balanceAfter = IERC20(_token).balanceOf(address(this)); | ||
|
||
if (balanceAfter < poolBalance + (poolBalance * 1e13) / 1e18) revert InsufficientRepayment(); | ||
} | ||
} | ||
|
||
/// @dev Deposit function for erc20 tokens or eth | ||
/// @dev if _token address is address(0), this is an eth deposit | ||
/// @param _amount Amount of token to deposit | ||
/// @param _token Address of token to deposit | ||
function deposit(uint256 _amount, address _token) external payable nonReentrant { | ||
if (_token == address(0)) { | ||
ethBalances[msg.sender] += msg.value; | ||
} else { | ||
IERC20(_token).safeTransferFrom(msg.sender, address(this), _amount); | ||
tokenBalances[msg.sender][_token] += _amount; | ||
} | ||
} | ||
|
||
/// @dev Withdraw function for erc20 tokens or eth | ||
/// @dev if _token address is address(0), this is an eth withdrawal | ||
/// @param _amount Amount of token to withdraw | ||
/// @param _token Address of token to withdraw | ||
function withdraw(uint256 _amount, address _token) external nonReentrant { | ||
if (_token == address(0)) { | ||
ethBalances[msg.sender] -= _amount; | ||
(bool success,) = payable(msg.sender).call{value: _amount}(''); | ||
require(success, 'Failed to send ETH'); | ||
} else { | ||
tokenBalances[msg.sender][_token] -= _amount; | ||
IERC20(_token).safeTransfer(msg.sender, _amount); | ||
} | ||
} | ||
|
||
/// @dev users can deposit eth by sending it directly to the contract | ||
receive() external payable {} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
pragma solidity ^0.8.19; | ||
|
||
import {IFlashloan} from '../../interfaces/Flashloan/IFlashloan.sol'; | ||
import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol'; | ||
import {SafeERC20} from '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol'; | ||
|
||
error OnlyPool(); | ||
|
||
contract GreedyReceiver { | ||
IFlashloan public pool; | ||
|
||
using SafeERC20 for IERC20; | ||
|
||
modifier onlyPool() { | ||
if (msg.sender != address(pool)) revert OnlyPool(); | ||
_; | ||
} | ||
|
||
constructor(address _poolAddress) { | ||
pool = IFlashloan(_poolAddress); | ||
} | ||
|
||
function flashLoan(uint256 _amount, address _token) external { | ||
pool.flashloan(_amount, _token); | ||
} | ||
|
||
function getETH() external payable onlyPool {} | ||
|
||
function getTokens(address _token, uint256 _amount) external onlyPool {} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
pragma solidity ^0.8.19; | ||
|
||
import {IFlashloan} from '../../interfaces/Flashloan/IFlashloan.sol'; | ||
import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol'; | ||
import {SafeERC20} from '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol'; | ||
|
||
error OnlyPool(); | ||
|
||
contract Receiver { | ||
IFlashloan public pool; | ||
|
||
using SafeERC20 for IERC20; | ||
|
||
modifier onlyPool() { | ||
if (msg.sender != address(pool)) revert OnlyPool(); | ||
_; | ||
} | ||
|
||
constructor(address _poolAddress) { | ||
pool = IFlashloan(_poolAddress); | ||
} | ||
|
||
function flashLoan(uint256 _amount, address _token) external { | ||
pool.flashloan(_amount, _token); | ||
} | ||
|
||
function getETH() external payable onlyPool { | ||
(bool success,) = payable(msg.sender).call{value: msg.value + (msg.value * 1e13) / 1e18}(''); | ||
require(success, 'failed to send ETH'); | ||
} | ||
|
||
function getTokens(address _token, uint256 _amount) external onlyPool { | ||
IERC20(_token).safeTransfer(msg.sender, _amount + (_amount * 1e13) / 1e18); | ||
} | ||
} |