-
Notifications
You must be signed in to change notification settings - Fork 158
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
Changes from 7 commits
05e4763
92c304e
7b6fa13
c79a450
ab404d8
69d2ff3
dc5ef93
7f77855
94d9a44
94ccc3b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"@coinbase/onchainkit": minor | ||
--- | ||
|
||
**feat**: add Permit2 approval process for UniversalRouter by @0xAlec #980 |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 = | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Keep alphabetical order. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fixed |
||
'0x000000000022D473030F116dDEE9F6B43aC78BA3'; | ||
export const UNIVERSALROUTER_CONTRACT_ADDRESS = | ||
'0x3fC91A3afd70395Cd496C647d5a6CC9D4B2b7FAD'; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,12 @@ | ||
import type { TransactionReceipt } from 'viem'; | ||
import type { Address, TransactionReceipt } from 'viem'; | ||
import { encodeFunctionData, parseAbi } from 'viem'; | ||
import type { Config } from 'wagmi'; | ||
import { waitForTransactionReceipt } from 'wagmi/actions'; | ||
import type { SendTransactionMutateAsync } from 'wagmi/query'; | ||
import { | ||
PERMIT2_CONTRACT_ADDRESS, | ||
UNIVERSALROUTER_CONTRACT_ADDRESS, | ||
} from '../constants'; | ||
import type { BuildSwapTransaction } from '../types'; | ||
|
||
export async function processSwapTransaction({ | ||
|
@@ -12,6 +17,7 @@ export async function processSwapTransaction({ | |
sendTransactionAsync, | ||
onStart, | ||
onSuccess, | ||
useAggregator, | ||
}: { | ||
swapTransaction: BuildSwapTransaction; | ||
config: Config; | ||
|
@@ -22,12 +28,15 @@ export async function processSwapTransaction({ | |
onSuccess: | ||
| ((txReceipt: TransactionReceipt) => void | Promise<void>) | ||
| undefined; | ||
useAggregator: boolean; | ||
}) { | ||
const { transaction, approveTransaction } = swapTransaction; | ||
const { transaction, approveTransaction, quote } = swapTransaction; | ||
|
||
// for swaps from ERC-20 tokens, | ||
// if there is an approveTransaction present, | ||
// request approval for the amount | ||
// for V1 API, `approveTx` will be an ERC-20 approval against the Router | ||
// for V2 API, `approveTx` will be an ERC-20 approval against the `Permit2` contract | ||
if (approveTransaction?.data) { | ||
setPendingTransaction(true); | ||
const approveTxHash = await sendTransactionAsync({ | ||
|
@@ -41,6 +50,39 @@ export async function processSwapTransaction({ | |
confirmations: 1, | ||
}); | ||
setPendingTransaction(false); | ||
|
||
// for the V2 API, we use Uniswap's `UniversalRouter`, which uses `Permit2` for ERC-20 approvals | ||
// this adds an additional transaction/step to the swap process | ||
// 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) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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
there can be an argument to decompose into two separate functions - |
||
setPendingTransaction(true); | ||
const permit2ContractAbi = parseAbi([ | ||
'function approve(address token, address spender, uint160 amount, uint48 expiration) external', | ||
]); | ||
const data = encodeFunctionData({ | ||
abi: permit2ContractAbi, | ||
functionName: 'approve', | ||
args: [ | ||
quote.from.address as Address, | ||
UNIVERSALROUTER_CONTRACT_ADDRESS, | ||
BigInt(quote.fromAmount), | ||
20_000_000_000_000, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why this number? what's the meaning behind. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. added a comment with a link to the above |
||
], | ||
}); | ||
const permitTxnHash = await sendTransactionAsync({ | ||
to: PERMIT2_CONTRACT_ADDRESS, | ||
data: data, | ||
value: 0n, | ||
}); | ||
await Promise.resolve(onStart?.(permitTxnHash)); | ||
await waitForTransactionReceipt(config, { | ||
hash: permitTxnHash, | ||
confirmations: 1, | ||
}); | ||
setPendingTransaction(false); | ||
} | ||
} | ||
|
||
// make the swap | ||
|
There was a problem hiding this comment.
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?There was a problem hiding this comment.
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 underexperimental
flag anyways so developers probably understand there will be breaking changesi can change this to
patch
if you think it's more suitableThere was a problem hiding this comment.
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.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed