Skip to content

Instantly share code, notes, and snippets.

@liranmarkin
Last active October 17, 2024 04:43
Show Gist options
  • Save liranmarkin/3c2c687dbb4765c77380c50a0b7488e3 to your computer and use it in GitHub Desktop.
Save liranmarkin/3c2c687dbb4765c77380c50a0b7488e3 to your computer and use it in GitHub Desktop.
pufETHArbitrage.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/interfaces/IERC4626.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@aave/core-v3/contracts/interfaces/IPool.sol";
import "@uniswap/v3-periphery/contracts/libraries/TransferHelper.sol";
interface IOneInchRouter {
struct SwapDescription {
IERC20 srcToken;
IERC20 dstToken;
address srcReceiver;
address dstReceiver;
uint256 amount;
uint256 minReturnAmount;
uint256 flags;
}
function swap(
address caller,
SwapDescription calldata desc,
bytes calldata data
) external returns (uint256 returnAmount, uint256 spentAmount);
}
contract PufETHArbitrage is Ownable {
IERC20 public constant WETH = IERC20(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);
IERC4626 public constant pufferVault = IERC4626(0xD9A442856C234a39a81a089C06451EBAa4306a72);
IERC20 public constant pufETH = IERC20(0xD9A442856C234a39a81a089C06451EBAa4306a72);
IPool public constant aavePool = IPool(0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2);
IOneInchRouter public constant oneInchRouter = IOneInchRouter(0x1111111254EEB25477B68fb85Ed929f73A960582);
// Add this event declaration at the contract level
event ArbitrageExecuted(
uint256 flashLoanAmount,
uint256 pufETHReceived,
uint256 ethReceived,
uint256 profit
);
constructor() Ownable(msg.sender) {}
function executeArbitrage(uint256 amount, bytes calldata swapData) external onlyOwner {
uint256 balanceBefore = address(this).balance;
// Step 1: Request flash loan
aavePool.flashLoanSimple(
address(this),
address(WETH),
amount,
abi.encode(amount, swapData),
0
);
// Step 6: Check profitability and transfer profit
uint256 profit = address(this).balance - balanceBefore;
require(profit > 0, "Arbitrage not profitable");
payable(owner()).transfer(profit);
// Log the arbitrage execution
emit ArbitrageExecuted(amount, 0, 0, profit);
}
function executeOperation(
address,
uint256 amount,
uint256 premium,
address,
bytes calldata params
) external returns (bool) {
require(msg.sender == address(aavePool), "Caller must be Aave pool");
(uint256 flashLoanAmount, bytes memory swapData) = abi.decode(params, (uint256, bytes));
uint256 totalDebt = flashLoanAmount + premium;
// Step 2: Swap WETH for pufETH using 1inch
TransferHelper.safeApprove(address(WETH), address(oneInchRouter), amount);
(uint256 pufETHReceived, ) = oneInchRouter.swap(
address(this),
IOneInchRouter.SwapDescription({
srcToken: WETH,
dstToken: pufETH,
srcReceiver: address(this),
dstReceiver: address(this),
amount: amount,
minReturnAmount: 0,
flags: 0
}),
swapData
);
// Step 3: Redeem pufETH for ETH
TransferHelper.safeApprove(address(pufETH), address(pufferVault), pufETHReceived);
uint256 ethReceived = pufferVault.redeem(pufETHReceived, address(this), address(this));
// Step 4: Check profitability
require(ethReceived > totalDebt, "Arbitrage not profitable");
// Step 5: Repay flash loan
TransferHelper.safeApprove(address(WETH), address(aavePool), totalDebt);
// Log the arbitrage details
emit ArbitrageExecuted(flashLoanAmount, pufETHReceived, ethReceived, ethReceived - totalDebt);
return true;
}
receive() external payable {}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment