Skip to content

Instantly share code, notes, and snippets.

@CepIbp1950
Created January 28, 2025 08:28
Show Gist options
  • Save CepIbp1950/738415c212774d54bc24b0c6c20b1c28 to your computer and use it in GitHub Desktop.
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=
// 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