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
2 changes: 1 addition & 1 deletion script/DeployArbitrage.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ contract DeployArbitrage is Script {

function run() public returns (Arbitrage) {
vm.startBroadcast();
arbitrage = new Arbitrage(currentConfig.uniswapQuoter);
arbitrage = new Arbitrage();
vm.stopBroadcast();
return arbitrage;
}
Expand Down
24 changes: 18 additions & 6 deletions script/HelperConfig.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,61 +13,71 @@ contract HelperConfig is Script {
TYPES
//////////////////////////////////////////////////////////////*/
struct NetworkConfig {
address weth;
address usdc;
address uniswapFactory; //Uniswap V3
address uniswapRouter; //Uniswap V3
address uniswapQuoter;
address sushiswapFactory;
address sushiswapRouter;
address uniswapQuoter;
address sushiswapQuoter;
}

/*//////////////////////////////////////////////////////////////
CONFIGS
//////////////////////////////////////////////////////////////*/
function getBaseSepoliaConfig() public pure returns (NetworkConfig memory) {
NetworkConfig memory SepoliaConfig = NetworkConfig({
weth: 0x4200000000000000000000000000000000000006,
usdc: 0x036CbD53842c5426634e7929541eC2318f3dCF7e,
uniswapFactory: 0x4752ba5DBc23f44D87826276BF6Fd6b1C372aD24,
uniswapRouter: 0x94cC0AaC535CCDB3C01d6787D6413C739ae12bc4,
uniswapQuoter: 0xC5290058841028F1614F3A6F0F5816cAd0df5E27,
sushiswapFactory: address(0),
sushiswapRouter: address(0),
uniswapQuoter: 0xC5290058841028F1614F3A6F0F5816cAd0df5E27
sushiswapQuoter: address(0)
});
return SepoliaConfig;
}

function getETHSepoliaConfig() public pure returns (NetworkConfig memory) {
NetworkConfig memory SepoliaConfig = NetworkConfig({
weth: 0xfFf9976782d46CC05630D1f6eBAb18b2324d6B14,
usdc: 0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238,
uniswapFactory: 0x0227628f3F023bb0B980b67D528571c95c6DaC1c,
uniswapRouter: 0x3bFA4769FB09eefC5a80d6E87c3B9C650f7Ae48E,
uniswapQuoter: 0xEd1f6473345F45b75F8179591dd5bA1888cf2FB3,
sushiswapFactory: address(0),
sushiswapRouter: 0x93c31c9C729A249b2877F7699e178F4720407733,
uniswapQuoter: address(0)
sushiswapQuoter: 0x039e87AB90205F9d87c5b40d4B28e2Be45dA4a20
});
return SepoliaConfig;
}

function getModeSepoliaConfig() public pure returns (NetworkConfig memory) {
NetworkConfig memory SepoliaConfig = NetworkConfig({
weth: address(0),
usdc: address(0),
uniswapFactory: 0x879A0F1E8402E37ECC56C53C55B6E02EB704eDD4,
uniswapRouter: 0x9eE1289c21321E212994B23Bf0b4Cdc453C17EEE,
uniswapQuoter: address(0),
sushiswapFactory: address(0),
sushiswapRouter: address(0),
uniswapQuoter: address(0)
sushiswapQuoter: address(0)
});
return SepoliaConfig;
}

function getModeMainnetConfig() public pure returns (NetworkConfig memory) {
NetworkConfig memory SepoliaConfig = NetworkConfig({
weth: address(0),
usdc: 0xd988097fb8612cc24eeC14542bC03424c656005f,
uniswapFactory: address(0),
uniswapRouter: address(0),
uniswapQuoter: address(0),
sushiswapFactory: address(0),
sushiswapRouter: 0xf2614A233c7C3e7f08b1F887Ba133a13f1eb2c55,
uniswapQuoter: address(0)
sushiswapQuoter: address(0)
});
return SepoliaConfig;
}
Expand All @@ -78,12 +88,14 @@ contract HelperConfig is Script {
function getAnvilConfig() public pure returns (NetworkConfig memory) {
console2.log("Testing On Anvil Network");
NetworkConfig memory AnvilConfig = NetworkConfig({
weth: address(0),
usdc: address(1),
uniswapFactory: address(2),
uniswapRouter: address(3),
uniswapQuoter: address(6),
sushiswapFactory: address(4),
sushiswapRouter: address(5),
uniswapQuoter: address(6)
sushiswapQuoter: address(6)
});
return AnvilConfig;
}
Expand Down
6 changes: 5 additions & 1 deletion script/Swap.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,16 @@ contract Swap is Script {

function run() public {
vm.startBroadcast();
bool success = IERC20(currentConfig.usdc).transfer(address(arbitrage), 2000000);
bool success = IERC20(currentConfig.usdc).transfer(
address(arbitrage),
2000000
);

console.log("transfer usdc to arbitrage contract", success);

uint256 amountOut = arbitrage._swapOnV3(
currentConfig.uniswapRouter,
currentConfig.uniswapQuoter,
currentConfig.usdc,
2000000,
0x4200000000000000000000000000000000000006,
Expand Down
126 changes: 94 additions & 32 deletions src/Arbitrage.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,27 +7,56 @@ import {ISwapRouter} from "@uniswap/v3-periphery/contracts/interfaces/ISwapRoute
import {ISwapRouter02} from "./interfaces/ISwapRouter02.sol";
import {IQuoterV2} from "@uniswap/v3-periphery/contracts/interfaces/IQuoterV2.sol";

/**
* @title Arbitrage.
* @author FlashArb-AI.
* @notice Earn arbitrage between two DEX's using balancer Flash Loans.
* @dev DEX's supported currently are the ones that share the same interface as uniswap(i.e. a fork).
*/

contract Arbitrage is IFlashLoanRecipient {
IVault private constant vault = IVault(0xBA12222222228d8Ba445958a75a0704d566BF2C8);
/// @notice Balancer Vault instance.
IVault private constant vault =
IVault(0xBA12222222228d8Ba445958a75a0704d566BF2C8);

address public owner;
IQuoterV2 public uniswapQuoter;

/// @notice Struct representing a swap / i.e. a buy or sell in this case.
struct Trade {
address[] routerPath;
address[] quoterPath;
address[] tokenPath;
uint24 fee;
}

constructor(address _quoter) {
event TokensSwapped(
address tokenIn,
address tokenOut,
uint256 amountIn,
uint256 minAmountOut,
uint256 quotedFee,
uint256 amountOut
);

constructor() {
owner = msg.sender;
uniswapQuoter = IQuoterV2(_quoter);
}

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}));
function executeTrade(
address[] memory _routerPath,
address[] memory _quoterPath,
address[] memory _tokenPath,
uint24 _fee,
uint256 _flashAmount
) external {
bytes memory data = abi.encode(
Trade({
routerPath: _routerPath,
quoterPath: _quoterPath,
tokenPath: _tokenPath,
fee: _fee
})
);

// Token to flash loan, by default we are flash loaning 1 token.
IERC20[] memory tokens = new IERC20[](1);
Expand Down Expand Up @@ -56,13 +85,22 @@ contract Arbitrage is IFlashLoanRecipient {

// 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);
_swapOnV3(
trade.routerPath[0],
trade.quoterPath[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.quoterPath[1],
trade.tokenPath[1],
IERC20(trade.tokenPath[1]).balanceOf(address(this)),
trade.tokenPath[0],
Expand All @@ -74,13 +112,17 @@ contract Arbitrage is IFlashLoanRecipient {
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)));
IERC20(trade.tokenPath[0]).transfer(
owner,
IERC20(trade.tokenPath[0]).balanceOf(address(this))
);
}

// -- INTERNAL FUNCTIONS -- //

function _swapOnV3(
address _router,
address _quoter,
address _tokenIn,
uint256 _amountIn,
address _tokenOut,
Expand All @@ -91,35 +133,55 @@ contract Arbitrage is IFlashLoanRecipient {
IERC20(_tokenIn).approve(_router, _amountIn);

// Setup swap parameters
ISwapRouter02.ExactInputSingleParams memory params = ISwapRouter02.ExactInputSingleParams({
tokenIn: _tokenIn,
tokenOut: _tokenOut,
fee: _fee,
recipient: address(this),
amountIn: _amountIn,
amountOutMinimum: _amountOut,
sqrtPriceLimitX96: 0
});
ISwapRouter02.ExactInputSingleParams memory params = ISwapRouter02
.ExactInputSingleParams({
tokenIn: _tokenIn,
tokenOut: _tokenOut,
fee: _fee,
recipient: address(this),
amountIn: _amountIn,
amountOutMinimum: _amountOut,
sqrtPriceLimitX96: 0
});

// Get fee amount
uint256 fee = getUniswapFeeQuote(_tokenIn, _tokenOut, _amountIn, _fee);
uint256 fee = getFeeQuote(
_quoter,
_tokenIn,
_tokenOut,
_amountIn,
_fee
);

// Perform swap
amountOut = ISwapRouter02(_router).exactInputSingle{value: fee}(params);

emit TokensSwapped(
_tokenIn,
_tokenOut,
_amountIn,
_amountOut,
fee,
amountOut
);
}

function getUniswapFeeQuote(address _tokenIn, address _tokenOut, uint256 amountIn, uint24 _fee)
public
returns (uint256 fee)
{
IQuoterV2.QuoteExactInputSingleParams memory params = IQuoterV2.QuoteExactInputSingleParams({
tokenIn: _tokenIn,
tokenOut: _tokenOut,
amountIn: amountIn,
fee: _fee,
sqrtPriceLimitX96: 0
});
(,,, fee) = uniswapQuoter.quoteExactInputSingle(params);
function getFeeQuote(
address _quoter,
address _tokenIn,
address _tokenOut,
uint256 amountIn,
uint24 _fee
) public returns (uint256 fee) {
IQuoterV2.QuoteExactInputSingleParams memory params = IQuoterV2
.QuoteExactInputSingleParams({
tokenIn: _tokenIn,
tokenOut: _tokenOut,
amountIn: amountIn,
fee: _fee,
sqrtPriceLimitX96: 0
});
(, , , fee) = IQuoterV2(_quoter).quoteExactInputSingle(params);
}

receive() external payable {}
Expand Down
Loading
Loading