Uniswap/permit2

ERC4626 Support

Joeysantoro opened this issue · 5 comments

[EDIT]: only withdraw and redeem methods in ERC-4626 use the underlying ERC-20 approval, so I edited the below to reflect that. This also simplifies the permit2 integration.

Similar to Uniswap/universal-router#176, I believe ERC-4626 integration directly into permit2 is highly beneficial. The main reasons are:

  1. natural integration - ERC-4626 leverages the native ERC-20 approval system (and by extension permit and permit2) for mutable share exit methods (withdraw and redeem), so naturally benefits greatly
  2. gas savings - Integrations like universalRouter can save gas by directly plugging into ERC-4626 mutable methods rather than transferring shares around (which can be costly if the vault includes custom transfer logic, such as in Compound). New vaults could even default approve permit2 for further user gas savings.
  3. contract size reduction - New ERC-4626 contracts need not include EIP-2612 logic.

Given that permit2 is intended to be deployed only once to save user approvals on gas, I'd lobby for ERC-4626 inclusion in the canonical deployment, whereas on universal router it can be added in a later deployment.

The simplest integration would be to only replicate the single-use transferFrom logic in AllowanceTransfer.sol for both of the withdraw and redeem functions. Batch methods and SignatureTransfer could also be added for each, although I'm not sure if the extra complexity is warranted

Can I get a sense for the intended freeze date for the permit2 contract? If the timelines are possible, I would be able to have a tested PR ready before the end of next week.

Hey @Joeysantoro love the flag here for ERC-4626. We designed permit2 to support other token types (ERC721/ERC1155) as well but each token standard would have their own canonical permit2. So this permit2 contract is specifically for ERC20 tokens and we plan to launch separate permit2 instances for 721/1155. I'm not as familiar with this EIP so I'll need to look more into it, but I guess the question would be, how different is the approve/transfer methods for ERC-4626? Maybe we can also write a canonical ERC-4626 specific permit2 (similar to ERC721/ERC1155 pattern we've designed) or do you see natural benefit in supporting it at the ERC20 permit2 contract layer?

@snreynolds thanks for the quick reply! It makes most sense directly in the ERC20 permit2 contract specifically because the ERC-4626 contract directly uses the ERC-20 approval system (all ERC-4626 contracts are themselves ERC-20). Per EIP-4626:

All EIP-4626 tokenized Vaults MUST implement EIP-20 to represent shares.

Its easiest to imagine ERC-4626 as a cToken (even though Compound is not compliant, the following discussion assumes it is). Take cDAI for example, the contract has all basic ERC-20 functionality as well as the ability to deposit and withdraw. The ERC-4626 standard allows for approved addresses of a given cDAI holder to withdraw or redeem the cDAI on behalf of the holder. Taking Universal Router where a user holds cDAI and wishes to swap into ETH as an example:

  1. User approves permit2 once for cDAI
  2. User signs message permitting universalRouter to redeem
  3. User redeems cDAI to DAI to UniversalRouter via permit2 integration with ERC-4626 specifically for the redeem method
  4. UniversalRouter swaps DAI to ETH on UniV3

Without the permit2 integration for the withdraw method, step 3 would become:
3a. transfer cDAI to the UniversalRouter
3b. redeem cDAI directly on the router (without using the approval)

You can see how the withdrawal (and redeem) logic shares the ERC-20 approval system in the solmate example ERC-4626 base: https://github.com/transmissions11/solmate/blob/main/src/mixins/ERC4626.sol#L73-L84

+1 Supporting ERC4626 not only provides gas savings but can also provides better price execution. A use could simply unwrap and wrap two different 4626 tokens with the same underlying asset without even having to make a trade incurring slippage and swap fees.

Since all ERC4626 must be ERC20 compliant and they are fully-backed wrappers around ERC20 tokens as well I think it makes sense to include in canonical Permit2 contract as well. 4626 already has a lot of support and there will be a ton of volume between ERC20 and 4626 tokens.

hey @Joeysantoro looping back in here. I think we'd be down to support ERC4626 in this repo but in a different contract similar to how we are doing ERC721 and ERC1155 support which will be implemented this week. If you want to give it a go, essentially created a new Permit2_4626 contract that inherits new AllowanceTransfer and SignatureTransfer contracts that support ERC4626 I am happy to review and get it merged into this repo. We can then discuss integrating those contracts into Universal Router as per the discussion here.

cc @hensha256

Also if you want to chat more feel free to shoot me a TG @saraareynolds

@snreynolds great! Seeing the 721 or 1155 implementation will be helpful as well.

If you all are committed that the contracts should be separate, then either the 4626 contract should contain all ERC-20 methods as well, or users of each user of a 4626 contract should be expected to approve the ERC-20 permit2 instance for ERC-20 functionality and ERC-4626 instance for 4626 functionality

Both are somewhat clunky for integrating contracts such as the router. The former would have better UX (user only needs to approve in one place, but contracts will need to duplicate all ERC-20 permit2 functions across the ERC20 and ERC4626 instances). The latter would have better devX because the ERC-20 functionality is all in the same contract so it is less duplication.

Let me know if this makes sense and what your thoughts are