Created
January 28, 2025 08:28
-
-
Save CepIbp1950/738415c212774d54bc24b0c6c20b1c28 to your computer and use it in GitHub Desktop.
Created using remix-ide: Realtime Ethereum Contract Compiler and Runtime. Load this file by pasting this gists URL or ID at https://remix.ethereum.org/#version=soljson-v0.8.26+commit.8a97fa7a.js&optimize=false&runs=200&gist=
This file contains hidden or 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
// SPDX-License-Identifier: UNLICENSED | |
pragma solidity 0.8.18; | |
import "@balancer-labs/v2-interfaces/contracts/vault/IVault.sol"; | |
import "@balancer-labs/v2-interfaces/contracts/vault/IFlashLoanRecipient.sol"; | |
import {ISwapRouter} from "@uniswap/v3-periphery/contracts/interfaces/ISwapRouter.sol"; | |
contract Arbitrage is IFlashLoanRecipient { | |
IVault private constant vault = | |
IVault(0xBA12222222228d8Ba445958a75a0704d566BF2C8); | |
address public owner; | |
struct Trade { | |
address[] routerPath; | |
address[] tokenPath; | |
uint24 fee; | |
} | |
constructor() { | |
owner = msg.sender; | |
} | |
function executeTrade( | |
address[] memory _routerPath, | |
address[] memory _tokenPath, | |
uint24 _fee, | |
uint256 _flashAmount | |
) external { | |
bytes memory data = abi.encode( | |
Trade({routerPath: _routerPath, tokenPath: _tokenPath, fee: _fee}) | |
); | |
// Token to flash loan, by default we are flash loaning 1 token. | |
IERC20[] memory tokens = new IERC20[](1); | |
tokens[0] = IERC20(_tokenPath[0]); | |
// Flash loan amount. | |
uint256[] memory amounts = new uint256[](1); | |
amounts[0] = _flashAmount; | |
vault.flashLoan(this, tokens, amounts, data); | |
} | |
function receiveFlashLoan( | |
IERC20[] memory tokens, | |
uint256[] memory amounts, | |
uint256[] memory feeAmounts, | |
bytes memory userData | |
) external override { | |
require(msg.sender == address(vault)); | |
// Decode our swap data so we can use it | |
Trade memory trade = abi.decode(userData, (Trade)); | |
uint256 flashAmount = amounts[0]; | |
// Since balancer called this function, we should have funds to begin swapping... | |
// We perform the 1st swap. | |
// We swap the flashAmount of token0 and expect to get X amount of token1 | |
_swapOnV3( | |
trade.routerPath[0], | |
trade.tokenPath[0], | |
flashAmount, | |
trade.tokenPath[1], | |
0, | |
trade.fee | |
); | |
// We perform the 2nd swap. | |
// We swap the contract balance of token1 and | |
// expect to at least get the flashAmount of token0 | |
_swapOnV3( | |
trade.routerPath[1], | |
trade.tokenPath[1], | |
IERC20(trade.tokenPath[1]).balanceOf(address(this)), | |
trade.tokenPath[0], | |
flashAmount, | |
trade.fee | |
); | |
// Transfer back what we flash loaned | |
IERC20(trade.tokenPath[0]).transfer(address(vault), flashAmount); | |
// Transfer any excess tokens [i.e. profits] to owner | |
IERC20(trade.tokenPath[0]).transfer( | |
owner, | |
IERC20(trade.tokenPath[0]).balanceOf(address(this)) | |
); | |
} | |
// -- INTERNAL FUNCTIONS -- // | |
function _swapOnV3( | |
address _router, | |
address _tokenIn, | |
uint256 _amountIn, | |
address _tokenOut, | |
uint256 _amountOut, | |
uint24 _fee | |
) internal { | |
// Approve token to swap | |
IERC20(_tokenIn).approve(_router, _amountIn); | |
// Setup swap parameters | |
ISwapRouter.ExactInputSingleParams memory params = ISwapRouter | |
.ExactInputSingleParams({ | |
tokenIn: _tokenIn, | |
tokenOut: _tokenOut, | |
fee: _fee, | |
recipient: address(this), | |
deadline: block.timestamp, | |
amountIn: _amountIn, | |
amountOutMinimum: _amountOut, | |
sqrtPriceLimitX96: 0 | |
}); | |
// Perform swap | |
ISwapRouter(_router).exactInputSingle(params); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment