UniswapFlashswapHandler

Git Source

Inherits: IonHandlerBase, IUniswapV3SwapCallback

This contract allows for easy creation and closing of leverage positions through Uniswap flashswaps--flashloan not necessary! In terms of creation, this may be a more desirable path than directly minting from an LST provider since market prices tend to be slightly lower than provider exchange rates. DEXes also provide an avenue for atomic deleveraging since the LST -> ETH exchange can be made.

When using the UniswapFlashSwapHandler, the IUniswapV3Pool pool fed to the constructor should be the WETH/[LST] pool. Unlike Balancer flashloans, there is no concern here that somebody else could initiate a flashswap, then direct the callback to be called on this contract. Uniswap enforces that callback is only called on msg.sender.

State Variables

MIN_SQRT_RATIO

The minimum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MIN_TICK)

uint160 internal constant MIN_SQRT_RATIO = 4_295_128_739;

MAX_SQRT_RATIO

The maximum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MAX_TICK)

uint160 internal constant MAX_SQRT_RATIO = 1_461_446_703_485_210_103_287_273_052_203_988_822_378_723_970_342;

UNISWAP_POOL

IUniswapV3Pool public immutable UNISWAP_POOL;

WETH_IS_TOKEN0

bool private immutable WETH_IS_TOKEN0;

Functions

constructor

Creates a new UniswapFlashSwapHandler instance.

constructor(IUniswapV3Pool _pool, bool _wethIsToken0);

Parameters

NameTypeDescription

_pool

IUniswapV3Pool

Pool to perform the flashswap on.

_wethIsToken0

bool

Whether WETH is token0 or token1 in the pool.

flashswapLeverage

Transfer collateral from user -> initate swap for collateral from WETH on Uniswap (contract will receive collateral first) -> deposit all collateral into IonPool -> borrow WETH from IonPool -> complete swap by sending WETH to Uniswap.

function flashswapLeverage(
    uint256 initialDeposit,
    uint256 resultingAdditionalCollateral,
    uint256 maxResultingAdditionalDebt,
    uint160 sqrtPriceLimitX96,
    uint256 deadline,
    bytes32[] memory proof
)
    external
    checkDeadline(deadline)
    onlyWhitelistedBorrowers(proof);

Parameters

NameTypeDescription

initialDeposit

uint256

in collateral terms. [WAD]

resultingAdditionalCollateral

uint256

in collateral terms. [WAD]

maxResultingAdditionalDebt

uint256

in WETH terms. This value also allows the user to control slippage of the swap. [WAD]

sqrtPriceLimitX96

uint160

for the swap. Recommended value is the current exchange rate to ensure the swap never costs more than a direct mint would. Passing the current exchange rate means swapping beyond that point is worse than direct minting.

deadline

uint256

timestamp for which the transaction must be executed. This prevents txs that have sat in the mempool for too long to be executed.

proof

bytes32[]

that the user is whitelisted.

_flashswapLeverage

function _flashswapLeverage(
    uint256 initialDeposit,
    uint256 resultingAdditionalCollateral,
    uint256 maxResultingAdditionalDebt,
    uint160 sqrtPriceLimitX96
)
    internal;

Parameters

NameTypeDescription

initialDeposit

uint256

in terms of swETH

resultingAdditionalCollateral

uint256

in terms of swETH. How much collateral to add to the position in the vault.

maxResultingAdditionalDebt

uint256

in terms of WETH. How much debt to add to the position in the vault.

sqrtPriceLimitX96

uint160

for the swap. Recommended value is the current exchange rate to ensure the swap never costs more than a direct mint would.

flashswapDeleverage

Initiate swap for WETH from collateral (contract will receive WETH first) -> repay debt on IonPool -> withdraw (and gem-exit) collateral from IonPool -> complete swap by sending collateral to Uniswap.

The two function parameters must be chosen carefully. If maxCollateralToRemove's ETH valuation were higher then debtToRemove, it would theoretically be possible to sell more collateral then was required for debtToRemove to be repaid (even if debtToRemove is worth nowhere near that valuation) due to the slippage of the sell. maxCollateralToRemove is essentially a slippage guard here.

function flashswapDeleverage(
    uint256 maxCollateralToRemove,
    uint256 debtToRemove,
    uint160 sqrtPriceLimitX96,
    uint256 deadline
)
    external
    checkDeadline(deadline);

Parameters

NameTypeDescription

maxCollateralToRemove

uint256

he max amount of collateral user is willing to sell to repay debtToRemove debt. [WAD]

debtToRemove

uint256

The desired amount of debt to remove. [WAD]

sqrtPriceLimitX96

uint160

for the swap. Can be set to 0 to set max bounds.

deadline

uint256

_initiateFlashSwap

Handles swap intiation logic. This function can only initiate exact output swaps.

function _initiateFlashSwap(
    bool zeroForOne,
    uint256 amountOut,
    address recipient,
    uint160 sqrtPriceLimitX96,
    FlashSwapData memory data
)
    private
    returns (uint256 amountIn);

Parameters

NameTypeDescription

zeroForOne

bool

Direction of the swap.

amountOut

uint256

Desired amount of output.

recipient

address

of output tokens.

sqrtPriceLimitX96

uint160

of the swap.

data

FlashSwapData

Arbitrary data to be passed through swap callback.

uniswapV3SwapCallback

From the perspective of the pool i.e. Negative amount means pool is sending. This function is intended to never be called directly. It should only be called by the Uniswap pool during a swap initiated by this contract.

One thing to note from a security perspective is that the pool only calls the callback on msg.sender. So a theoretical attacker cannot call this function by directing where to call the callback.

function uniswapV3SwapCallback(int256 amount0Delta, int256 amount1Delta, bytes calldata _data) external override;

Parameters

NameTypeDescription

amount0Delta

int256

change in token0

amount1Delta

int256

change in token1

_data

bytes

arbitrary data

Errors

InvalidUniswapPool

error InvalidUniswapPool();

InvalidZeroLiquidityRegionSwap

error InvalidZeroLiquidityRegionSwap();

InvalidSqrtPriceLimitX96

error InvalidSqrtPriceLimitX96(uint160 sqrtPriceLimitX96);

FlashswapRepaymentTooExpensive

error FlashswapRepaymentTooExpensive(uint256 amountIn, uint256 maxAmountIn);

CallbackOnlyCallableByPool

error CallbackOnlyCallableByPool(address unauthorizedCaller);

OutputAmountNotReceived

error OutputAmountNotReceived(uint256 amountReceived, uint256 amountRequired);

Structs

FlashSwapData

struct FlashSwapData {
    address user;
    uint256 changeInCollateralOrDebt;
    bool zeroForOne;
}