Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
[submodule "lib/forge-std"]
path = lib/forge-std
url = https://github.com/foundry-rs/forge-std
[submodule "lib/balancer-v2-monorepo"]
path = lib/balancer-v2-monorepo
url = https://github.com/balancer/balancer-v2-monorepo
[submodule "lib/openzeppelin-contracts"]
path = lib/openzeppelin-contracts
url = https://github.com/OpenZeppelin/openzeppelin-contracts
[submodule "lib/v3-periphery"]
path = lib/v3-periphery
url = https://github.com/uniswap/v3-periphery
[submodule "lib/v3-core"]
path = lib/v3-core
url = https://github.com/uniswap/v3-core
8 changes: 7 additions & 1 deletion foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,11 @@
src = "src"
out = "out"
libs = ["lib"]

remappings = [
"forge-std/=lib/forge-std/src/",
"@balancer/balancer-v2-monorepo/pkg/=lib/balancer-v2-monorepo/pkg/",
"@openzeppelin/openzeppelin-contracts/contracts/=lib/openzeppelin-contracts/contracts/",
"@uniswap/v3-periphery/contracts/interfaces=lib/v3-periphery/contracts/interfaces",
"@uniswap/v3-core/contracts/interfaces/=lib/v3-core/contracts/interfaces/",
]
# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options
1 change: 1 addition & 0 deletions lib/balancer-v2-monorepo
Submodule balancer-v2-monorepo added at 36d282
1 change: 1 addition & 0 deletions lib/openzeppelin-contracts
Submodule openzeppelin-contracts added at 281550
1 change: 1 addition & 0 deletions lib/v3-core
Submodule v3-core added at e3589b
1 change: 1 addition & 0 deletions lib/v3-periphery
Submodule v3-periphery added at 80f26c
104 changes: 104 additions & 0 deletions src/Arbitrage.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.18;

import "@balancer/balancer-v2-monorepo/pkg/interfaces/contracts/vault/IVault.sol";
import "@balancer/balancer-v2-monorepo/pkg/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);
}
}
1 change: 0 additions & 1 deletion src/ArbitrageMaker.sol

This file was deleted.

1 change: 0 additions & 1 deletion src/FlashLoanTx.sol

This file was deleted.

Loading