Skip to main content

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:

  1. 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 a withdraw from an EVault (or borrow in case of short trades), but the only requirement is that during the swap, the Swapper holds enough of the input token to carry out the trade.
  2. 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.
  3. 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 the Swapper. It is the verifier that checks slippage and then skims the available assets for the user.In case of swap-to-repay mode, the Swapper contract is expected to repay the debt for the account, so SwapVerifier 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.