Final contract
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity =0.8.20;
import '@cryptoalgebra/integral-core/contracts/interfaces/callback/IAlgebraFlashCallback.sol';
import '@cryptoalgebra/integral-periphery/contracts/base/PeripheryPayments.sol';
import '@cryptoalgebra/integral-periphery/contracts/base/PeripheryImmutableState.sol';
import '@cryptoalgebra/integral-periphery/contracts/libraries/PoolAddress.sol';
import '@cryptoalgebra/integral-periphery/contracts/libraries/CallbackValidation.sol';
import '@cryptoalgebra/integral-periphery/contracts/libraries/TransferHelper.sol';
import '@cryptoalgebra/integral-periphery/contracts/interfaces/ISwapRouter.sol';
/// @title Flash contract implementation
/// @notice An example contract using the Algebra flash function
contract PairFlash is IAlgebraFlashCallback, PeripheryImmutableState, PeripheryPayments {
ISwapRouter public immutable swapRouter;
constructor(
ISwapRouter _swapRouter,
address _factory,
address _WMATIC,
address _poolDeployer
) PeripheryImmutableState(_factory, _WMATIC, _poolDeployer) {
swapRouter = _swapRouter;
}
/// @param fee0 The fee from calling flash for token0
/// @param fee1 The fee from calling flash for token1
/// @param data The data needed in the callback passed as FlashCallbackData from `initFlash`
/// @notice implements the callback called from flash
/// @dev fails if the flash is not profitable, meaning the amountOut from the flash is less than the amount borrowed
function IAlgebraFlashCallback(
uint256 fee0,
uint256 fee1,
bytes calldata data
) external override {
FlashCallbackData memory decoded = abi.decode(data, (FlashCallbackData));
CallbackValidation.verifyCallback(poolDeployer, decoded.poolKey);
address token0 = decoded.poolKey.token0;
address token1 = decoded.poolKey.token1;
TransferHelper.safeApprove(token0, address(swapRouter), decoded.amount0);
TransferHelper.safeApprove(token1, address(swapRouter), decoded.amount1);
// profitable check
// exactInputSingle will fail if this amount not met
uint256 amount1Min = decoded.amount1 + fee1;
uint256 amount0Min = decoded.amount0 + fee0;
// call exactInputSingle for swapping token1 for token0 in pool
uint256 amountOut0 =
swapRouter.exactInputSingle(
ISwapRouter.ExactInputSingleParams({
tokenIn: token1,
tokenOut: token0,
recipient: address(this),
deadline: block.timestamp,
amountIn: decoded.amount1,
amountOutMinimum: amount0Min,
sqrtPriceLimitX96: 0
})
);
// call exactInputSingle for swapping token0 for token 1 in pool
uint256 amountOut1 =
swapRouter.exactInputSingle(
ISwapRouter.ExactInputSingleParams({
tokenIn: token0,
tokenOut: token1,
recipient: address(this),
deadline: block.timestamp,
amountIn: decoded.amount0,
amountOutMinimum: amount1Min,
sqrtPriceLimitX96: 0
})
);
// end up with amountOut0 of token0 from first swap and amountOut1 of token1 from second swap
uint256 amount0Owed = decoded.amount0 + fee0;
uint256 amount1Owed = decoded.amount1 + fee1;
TransferHelper.safeApprove(token0, address(this), amount0Owed);
TransferHelper.safeApprove(token1, address(this), amount1Owed);
if (amount0Owed > 0) pay(token0, address(this), msg.sender, amount0Owed);
if (amount1Owed > 0) pay(token1, address(this), msg.sender, amount1Owed);
// if profitable pay profits to payer
if (amountOut0 > amount0Owed) {
uint256 profit0 = amountOut0 - amount0Owed;
TransferHelper.safeApprove(token0, address(this), profit0);
pay(token0, address(this), decoded.payer, profit0);
}
if (amountOut1 > amount1Owed) {
uint256 profit1 = amountOut1 - amount1Owed;
TransferHelper.safeApprove(token0, address(this), profit1);
pay(token1, address(this), decoded.payer, profit1);
}
}
struct FlashParams {
address token0;
address token1;
uint256 amount0;
uint256 amount1;
}
struct FlashCallbackData {
uint256 amount0;
uint256 amount1;
address payer;
PoolAddress.PoolKey poolKey;
}
/// @param params The parameters necessary for flash and the callback, passed in as FlashParams
/// @notice Calls the pools flash function with data needed in `algebraFlashCallback`
function initFlash(FlashParams memory params) external {
PoolAddress.PoolKey memory poolKey =
PoolAddress.PoolKey({token0: params.token0, token1: params.token1});
IAlgebraPool pool = IAlgebraPool(PoolAddress.computeAddress(poolDeployer, poolKey));
// recipient of borrowed amounts
// amount of token0 requested to borrow
// amount of token1 requested to borrow
// need amount 0 and amount1 in callback to pay back pool
// recipient of flash should be THIS contract
pool.flash(
address(this),
params.amount0,
params.amount1,
abi.encode(
FlashCallbackData({
amount0: params.amount0,
amount1: params.amount1,
payer: msg.sender,
poolKey: poolKey,
})
)
);
}
}
Last updated