Alert Source Discuss
🚧 Stagnant Standards Track: ERC

ERC-5143: Slippage Protection for Tokenized Vault

An extension of EIP-4626 supporting improved EOA interactions.

Authors Hadrien Croubois (@amxx)
Created 2022-06-09
Discussion Link https://ethereum-magicians.org/t/eip-5143-slippage-protection-for-tokenized-vaults/9554
Requires EIP-20, EIP-4626

Abstract

The following standard extends the EIP-4626 Tokenized Vault standard with functions dedicated to the safe interaction between EOAs and the vault when price is subject to slippage.

Motivation

EIP-4626 security considerations section states that:

“If implementors intend to support EOA account access directly, they should consider adding an additional function call for deposit/mint/withdraw/redeem with the means to accommodate slippage loss or unexpected deposit/withdrawal limits, since they have no other means to revert the transaction if the exact output amount is not achieved.”

Yet, EIP-4626 does not standardize the corresponding function signatures and behaviors. For improved interroperability, and better support by wallets, it is essential that this optional functions are also standardized.

Specification

This ERC is an extension of EIP-4626. Any contract implementing it MUST also implement EIP-4626.

Methods

deposit

Overloaded version of ERC-4626’s deposit.

Mints shares Vault shares to receiver by depositing exactly assets of underlying tokens.

MUST emit the Deposit event.

MUST support EIP-20 approve / transferFrom on asset as a deposit flow. MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the deposit execution, and are accounted for during deposit.

MUST revert if all of assets cannot be deposited (due to deposit limit being reached, slippage, the user not approving enough underlying tokens to the Vault contract, etc). MUST revert if depositing assets underlying asset mints less then minShares shares.

Note that most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.

- name: deposit
  type: function
  stateMutability: nonpayable

  inputs:
    - name: assets
      type: uint256
    - name: receiver
      type: address
    - name: minShares
      type: uint256

  outputs:
    - name: shares
      type: uint256

mint

Overloaded version of ERC-4626’s mint.

Mints exactly shares Vault shares to receiver by depositing assets of underlying tokens.

MUST emit the Deposit event.

MUST support ERC-20 approve / transferFrom on asset as a mint flow. MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the mint execution, and are accounted for during mint.

MUST revert if all of shares cannot be minted (due to deposit limit being reached, slippage, the user not approving enough underlying tokens to the Vault contract, etc). MUST revert if minting shares shares cost more then maxAssets underlying tokens.

Note that most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.

- name: mint
  type: function
  stateMutability: nonpayable

  inputs:
    - name: shares
      type: uint256
    - name: receiver
      type: address
    - name: maxAssets
      type: uint256

  outputs:
    - name: assets
      type: uint256

withdraw

Overloaded version of ERC-4626’s withdraw.

Burns shares from owner and sends exactly assets of underlying tokens to receiver.

MUST emit the Withdraw event.

MUST support a withdraw flow where the shares are burned from owner directly where owner is msg.sender or msg.sender has ERC-20 approval over the shares of owner. MAY support an additional flow in which the shares are transferred to the Vault contract before the withdraw execution, and are accounted for during withdraw.

MUST revert if all of assets cannot be withdrawn (due to withdrawal limit being reached, slippage, the owner not having enough shares, etc). MUST revert if withdrawing assets underlying tokens requires burning more then maxShares shares.

Note that some implementations will require pre-requesting to the Vault before a withdrawal may be performed. Those methods should be performed separately.

- name: withdraw
  type: function
  stateMutability: nonpayable

  inputs:
    - name: assets
      type: uint256
    - name: receiver
      type: address
    - name: owner
      type: address
    - name: maxShares
      type: uint256

  outputs:
    - name: shares
      type: uint256

redeem

Overloaded version of ERC-4626’s redeem.

Burns exactly shares from owner and sends assets of underlying tokens to receiver.

MUST emit the Withdraw event.

MUST support a redeem flow where the shares are burned from owner directly where owner is msg.sender or msg.sender has ERC-20 approval over the shares of owner. MAY support an additional flow in which the shares are transferred to the Vault contract before the redeem execution, and are accounted for during redeem.

MUST revert if all of shares cannot be redeemed (due to withdrawal limit being reached, slippage, the owner not having enough shares, etc). MUST revert if redeeming shares shares sends less than minAssets underlying tokens to receiver.

Note that some implementations will require pre-requesting to the Vault before a withdrawal may be performed. Those methods should be performed separately.

- name: redeem
  type: function
  stateMutability: nonpayable

  inputs:
    - name: shares
      type: uint256
    - name: receiver
      type: address
    - name: owner
      type: address
    - name: minAssets
      type: uint256

  outputs:
    - name: assets
      type: uint256

Rationale

This ERC’s functions do not replace ERC-4626 equivalent mechanisms. They are additional (overloaded) methods designed to protect EOAs interacting with the vault.

When smart contracts interact with an ERC-4626 vault, they can preview any operation using the dedicated functions before executing the operation. This can be done atomically, with no risk of price change. This is not true of EOA, which will preview their operations on a UI, sign a transaction, and have it mined later. Between the preview and the transaction being executed, the blockchain state might change, resulting in unexpected outcomes. In particular, frontrunning make EOA’s interractons with an ERC-4626 vault possibly risky.

Other projects in the DeFi spaces, such as decentralized exchanges, already include similar mechanisms so a user can request its transaction reverts if the resulting exchange rate is not considered good enough.

Implementing This ERC on top of an ERC-4626 contract can be done very easily. It just requires calling the corresponding ERC-4626 function and adding a revert check on the returned value.

Alternative approaches

This ERC aims at solving the security concerns (describes in the motivation section) at the vault level. For completeness, we have to mention that these issues can also be addressed using a generic ERC-4626 router, similar to how Uniswap V2 & V3 use a router to provide good user workflows on top of the Uniswap pairs. The router approach is possibly more versatile and leaves more room for evolutions (the router can be replaced at any point) but it also leads to more expensive operations because the router needs to take temporary custody of the tokens going into the vault.

Reference Implementation

Given an existing ERC-4626 implementation

contract ERC5143 is ERC4626 {
    function deposit(uint256 assets, address receiver, uint256 minShares) public virtual returns (uint256) {
        uint256 shares = deposit(assets, receiver);
        require(shares >= minShares, "ERC5143: deposit slippage protection");
        return shares;
    }
    function mint(uint256 shares, address receiver, uint256 maxAssets) public virtual returns (uint256) {
        uint256 assets = mint(shares, receiver);
        require(assets <= maxAssets, "ERC5143: mint slippage protection");
        return assets;
    }
    function withdraw(uint256 assets, address receiver, address owner, uint256 maxShares) public virtual returns (uint256) {
        uint256 shares = withdraw(assets, receiver, owner);
        require(shares <= maxShares, "ERC5143: withdraw slippage protection");
        return shares;
    }
    function redeem(uint256 shares, address receiver, address owner, uint256 minAssets) public virtual returns (uint256) {
        uint256 assets = redeem(shares, receiver, owner);
        require(assets >= minAssets, "ERC5143: redeem slippage protection");
        return assets;
    }
}

Security Considerations

This ERC addresses one of the security consideration raised by ERC-4626. Other considerations still apply.

Copyright and related rights waived via CC0.

Citation

Please cite this document as:

Hadrien Croubois (@amxx), "ERC-5143: Slippage Protection for Tokenized Vault [DRAFT]," Ethereum Improvement Proposals, no. 5143, June 2022. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-5143.