Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add Permit2 approval process for UniversalRouter #980

Merged
merged 10 commits into from
Aug 6, 2024
Merged

Conversation

0xAlec
Copy link
Contributor

@0xAlec 0xAlec commented Aug 6, 2024

What changed? Why?

  • add Permit2 approval flow for V2 API
  • in our V2 API, we use Uniswap's UniversalRouter
  • this is different than the standard approval process, where you approve ERC-20 tokens for spending against the router directly
  • instead, for UniversalRouter ERC-20s are approved against an intermediary Permit2 contract
  • this adds an additional step in our approval flow, since we need to approve the UniversalRouter contract to spend the funds approved against Permit2
    • this is typically done as a (gasless) signature - but we implement this as a transaction to allow for batching by smart wallets
  • blockchain security recommended us only approve exact amounts to Permit2 (typically the approval is unlimited and only done once - because of the this constraint we need a transaction for each Permit2 approval, making the swap experience 3 separate transactions for EOAs)

read more: https://blog.uniswap.org/permit2-and-universal-router

Notes to reviewers

How has it been tested?
using https://github.com/ilikesymmetry/onchainkit-demo

Copy link

vercel bot commented Aug 6, 2024

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
onchainkit-playground ✅ Ready (Inspect) Visit Preview 💬 Add feedback Aug 6, 2024 7:33pm

@0xAlec 0xAlec changed the title permit2 feat: add Permit2 approval process for UniversalRouter Aug 6, 2024
@0xAlec 0xAlec marked this pull request as ready for review August 6, 2024 00:57
@@ -0,0 +1,5 @@
---
"@coinbase/onchainkit": minor
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why minor? is this causing a breaking changes?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it will be a breaking change for users of V2 API - but it is under experimental flag anyways so developers probably understand there will be breaking changes

i can change this to patch if you think it's more suitable

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's do patch as it is experimental.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

@@ -6,3 +6,7 @@ export const TOO_MANY_REQUESTS_ERROR_CODE = 'TOO_MANY_REQUESTS_ERROR';
export const UNCAUGHT_SWAP_QUOTE_ERROR_CODE = 'UNCAUGHT_SWAP_QUOTE_ERROR';
export const UNCAUGHT_SWAP_ERROR_CODE = 'UNCAUGHT_SWAP_ERROR';
export const USER_REJECTED_ERROR_CODE = 'USER_REJECTED';
export const PERMIT2_CONTRACT_ADDRESS =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keep alphabetical order.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

quote.from.address as Address,
UNIVERSALROUTER_CONTRACT_ADDRESS,
BigInt(quote.fromAmount),
20_000_000_000_000,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why this number? what's the meaning behind.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://docs.uniswap.org/contracts/permit2/reference/allowance-transfer

it's the deadline where the approval is no longer valid - since we're only approving the exact amount fromAmount it shouldn't be a security concern if this isn't granular

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like we should either have a comment here, or have a variable that better explain what's for.

This will help future developer undersand what's going on.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added a comment with a link to the above

// since we need to make an extra transaction to `Permit2` to allow the UniversalRouter to spend the approved funds
// this would typically be a (gasless) signature, but we're using a transaction here to allow batching for Smart Wallets
// read more: https://blog.uniswap.org/permit2-and-universal-router
if (!useAggregator) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like this code, has a lot of important pieces, and there is only one unit-test checking on this.

I wonder how this could be break down in 2~3 unit-tests. Or even the code break down a bit more.

Copy link
Contributor Author

@0xAlec 0xAlec Aug 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the single unit test checks for all of the code/cases in this conditional

    expect(setPendingTransaction).toHaveBeenCalledTimes(6);
    expect(setPendingTransaction).toHaveBeenCalledWith(true);
    expect(setPendingTransaction).toHaveBeenCalledWith(false);
    expect(sendTransactionAsyncPermit2).toHaveBeenCalledTimes(3);
    expect(waitForTransactionReceipt).toHaveBeenCalledTimes(3);

    expect(setLoading).toHaveBeenCalledTimes(1);
    expect(setLoading).toHaveBeenCalledWith(true);
    expect(onSuccess).toHaveBeenCalledTimes(1);
    expect(onSuccess).toHaveBeenCalledWith({});
    expect(onStart).toHaveBeenCalledTimes(3);
    expect(onStart).toHaveBeenNthCalledWith(1, 'approveTxHash');
    expect(onStart).toHaveBeenNthCalledWith(2, 'permit2TxHash');
    expect(onStart).toHaveBeenNthCalledWith(3, 'txHash');

there can be an argument to decompose into two separate functions - processSwapApprovals and processSwapTransaction, both of which are under processSwap parent function. I can take this action item as part of the Smart Wallet/Transaction component integration.

@Zizzamia Zizzamia merged commit 16c004b into main Aug 6, 2024
14 checks passed
@Zizzamia Zizzamia deleted the alec/permit-2 branch August 6, 2024 21:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

Successfully merging this pull request may close these issues.

2 participants