Swaps
Directory: src/Swaps
Utilities for performing DEX swaps for EVK credit vault operations.
The Swapper
and SwapVerifier
contracts are helpers for executing swaps and swaps-to-repay operations on EVK vaults, using EVC batches.
Swapper Contract
General swapping algorithm
The general steps to use the Swapper
contract are following:
- provision the
Swapper
with tokens to sell: The amount provided will serve as an implicit limit for the trade. In most cases the token will be provided with awithdraw
from anEVault
(orborrow
in case of short trades), but the only requirement is that during the swap, theSwapper
holds enough of the input token to carry out the trade. - execute a call (or a series of calls) to
swap()
function: The assumption is that the calls will consume the provided tokens and buy the required output token. - call
SwapVerifier
to make sure the results are within accepted bounds: The verifiaction should be taking into account any expected slippage and price impact. This step is an essential security measure. For regular swaps,SwapVerifier
assumes that any token balance present in the output vault after the swap is the result of the swap. For this reason the bought token is not deposited for the user automatically by theSwapper
. It is the verifier that checks slippage and then skims the available assets for the user.In case of swap-to-repay mode, theSwapper
contract is expected to repay the debt for the account, soSwapVerifier
only checks that the resulting debt matches expectations
Implementation
The Swapper
contract should implement the ISwapper
interface. This ensures, that users could potentially provide their own implementations of the swapper contract in the UI, without needing to modify the FE code.
The main function is swap()
, which takes a swap definition in a SwapParams
struct. The params define a handler to use, swapping mode, bought and sold tokens, the amounts etc. See ISwapper natspec for details. Note, that some parameters might be ignored in certain modes or by certain handlers, while others (amountOut
) might have differrent semantics in certain modes.
The interface also defines helper functions like sweep
, deposit
, repay
and repayAndDeposit
which allow consuming the contract's balance.
Finally a multicall
function allows chaining all of the above to execute complex scenarios.
Swapping modes
The swaps can be performed in one of 3 modes:
Exact Input
In this mode, all of the provided input token is expected to be swapped for an unknown amount of the output token. The proceeds are expected to be sent to a vault, to be skimmed by the user, or back to the swapper contract. The latter option is useful when performing complex, multi-stage swaps, where the output token is accumulated in the swapper before being consumed at the end of the operation.
Note that the available handler (GenericHandler
) executes a payload encoded off-chain, so a lot of the parameters passed to the swap
funtion will be ignored and only the amount of input token encoded in the payload will be swapped, even if the swapper holds more.
Exact Output
In this mode, the swapper is expected to purchase a specified amount of the output token in exchange for an unknown amount of the input token. The input token amount provided to the swapper acts as an implicit slippage limit. Currently available handlers (Uniswap V2 and V3) will check the current balance of the output token held by the swapper and adjust the amount of token to buy to only purchase the remainder. This feature can be used to construct multi-stage swaps. For instance, in the first stage, most of the token is bought through a dex aggregator, which presumably offers better price, but doesn't provide exact output swaps. The proceeds are directed to the swapper contract. In the second stage another swap
call is executed, and the remainder is bought directly in Uniswap, where exact output trades are supported.
In exact output trades, the remaining, unused input token amount is automatically redeposited for the account specified in SwapParams.accountIn
in a vault specified in SwapParams.vaultIn
.
Note that the generic handler can also be invoked in exact output mode. The swap will be executed as per the request encoded off-chain, but swapper will attempt to return unused input token balance if any.
Target Debt (Swap & Repay)
In this mode, the swapper will check how much debt a provided account has (including current interest accrued), compare it with a target debt the user requests, and the amount already held by the swapper contract, and by exact output trade will buy exactly the amount required to repay the debt (down to the target amount). In most cases the target amount will be zero, which means the mode can be used to close debt positions without leaving any dust.
After buying the necessary amount, the swapper will automatically execute repay
on the liability vault for the user.
If at the time the swap is executed, the swapper contract holds more funds than necessary to repay to the target debt, the swap is not carried out, but instead the debt is repaid and any surplus of the output token is deposited for the account in the liability vault. This allows combined swaps to safely target high ratios of exact input swaps vs exact output remainders. The exact input swap doesn't know the exact amount of output that will be received and it can 'over-swap' more than is necessary to repay the debt. Because the surplus doesn't revert the transaction, high ratios of better priced exact input swaps can be targeted by the UI.
Finally, and similarly to exact output, any unused input will be redeposited for the accountIn
.
Note that the generic handler can also be invoked in target debt mode. In such a case there will be no on-chain modification to the swap payload, but swapper will attempt a repay and return of the unused input as per swap params.
Handlers
The swapper contract executes trades using external providers like 1Inch
or Uniswap
. Internal handler modules take care of interfacing with the provider. They are enumerated by a bytes32
encoded string. The available handlers are listed as constants in Swapper.sol
.
Generic Handler
Executes arbitrary call on arbitrary target address. Presumably they are calls to swap providers. For example a payload returned by 1Inch API on the aggregator contract or payloads created with Uniswap's Auto Router on SwapRouter02
Generic handler accepts all 3 swapping modes, although for most swap providers only exact input is natively available. Although the off-chain data will not be modified to match the actual on-chain conditions during execution, swapper will carry out post processing according to the swap mode: returning unused input token in exact output mode or repaying debt in target debt mode.
The data passed to the handler in SwapParams.data
should be an abi encoded tuple: target contract address and call data
Uniswap V2 Handler
Doesn't support exact input
. Executes exact output swaps on UniswapV2 router
Uniswap V3 Handler
Doesn't support exact input
. Executes exact output swaps on UniswapV3 router
Security And Trust Boundries
The Swapper
contract is not trusted. From the protocol's perspective, it is a black box. Provided with a token to sell, it is supposed to execute the swap and return the bought token either as a balance available for deposit or as repaid debt. No assumptions are made about how the swap is performed or about the security of the Swapper
code. In fact, the Swapper
has no access control and allows anyone to remove any token balance it holds at any time. The provided implementation is just a reference; users are generally free to use any swapper they choose.
The SwapVerifier
contract is part of the trusted codebase. Its responsibility is to verify the results of the Swapper
execution, i.e., that the sold and bought token balances meet the required limits. The checks are simple but are the cornerstone of the security of using the swap periphery. It is the user's responsibility to ensure that all executions of the Swapper
contract are always followed by proper verification with SwapVerifier
, presumably as the following item in an EVC batch.