Skip to content

Commit

Permalink
WIP: map recipient for PERMIT2_TRANSFER_FROM (Uniswap#205)
Browse files Browse the repository at this point in the history
* map recipient for PERMIT2_TRANSFER_FROM

* lint

---------

Co-authored-by: Alice Henshaw <[email protected]>
  • Loading branch information
ewilz and hensha256 authored Jan 30, 2023
1 parent 3ae144d commit e76a51a
Show file tree
Hide file tree
Showing 5 changed files with 69 additions and 5 deletions.
2 changes: 1 addition & 1 deletion contracts/base/Dispatcher.sol
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ abstract contract Dispatcher is Payments, V2SwapRouter, V3SwapRouter, Callbacks
} else if (command == Commands.PERMIT2_TRANSFER_FROM) {
(address token, address recipient, uint160 amount) =
abi.decode(inputs, (address, address, uint160));
permit2TransferFrom(token, msg.sender, recipient, amount);
permit2TransferFrom(token, msg.sender, recipient.map(), amount);
} else if (command == Commands.PERMIT2_PERMIT_BATCH) {
(IAllowanceTransfer.PermitBatch memory permitBatch, bytes memory data) =
abi.decode(inputs, (IAllowanceTransfer.PermitBatch, bytes));
Expand Down
37 changes: 36 additions & 1 deletion test/integration-tests/UniversalRouter.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { UniversalRouter, Permit2, ERC20, MockLooksRareRewardsDistributor, ERC721 } from '../../typechain'
import { UniversalRouter, Permit2, ERC20, IWETH9, MockLooksRareRewardsDistributor, ERC721 } from '../../typechain'
import { BigNumber, BigNumberish } from 'ethers'
import { Pair } from '@uniswap/v2-sdk'
import { expect } from './shared/expect'
import { abi as ROUTER_ABI } from '../../artifacts/contracts/UniversalRouter.sol/UniversalRouter.json'
import { abi as TOKEN_ABI } from '../../artifacts/solmate/src/tokens/ERC20.sol/ERC20.json'
import { abi as WETH_ABI } from '../../artifacts/contracts/interfaces/external/IWETH9.sol/IWETH9.json'

import NFTX_ZAP_ABI from './shared/abis/NFTXZap.json'
import deployUniversalRouter, { deployPermit2 } from './shared/deployUniversalRouter'
import {
Expand Down Expand Up @@ -43,6 +45,7 @@ describe('UniversalRouter', () => {
let router: UniversalRouter
let permit2: Permit2
let daiContract: ERC20
let wethContract: IWETH9
let mockLooksRareToken: ERC20
let mockLooksRareRewardsDistributor: MockLooksRareRewardsDistributor
let pair_DAI_WETH: Pair
Expand All @@ -65,6 +68,7 @@ describe('UniversalRouter', () => {
mockLooksRareToken.address
)) as MockLooksRareRewardsDistributor
daiContract = new ethers.Contract(DAI.address, TOKEN_ABI, alice) as ERC20
wethContract = new ethers.Contract(WETH.address, WETH_ABI, alice) as IWETH9
pair_DAI_WETH = await makePair(alice, DAI, WETH)
permit2 = (await deployPermit2()).connect(alice) as Permit2
router = (
Expand All @@ -79,7 +83,9 @@ describe('UniversalRouter', () => {
beforeEach(async () => {
planner = new RoutePlanner()
await daiContract.approve(permit2.address, MAX_UINT)
await wethContract.approve(permit2.address, MAX_UINT)
await permit2.approve(DAI.address, router.address, MAX_UINT160, DEADLINE)
await permit2.approve(WETH.address, router.address, MAX_UINT160, DEADLINE)
})

it('reverts if block.timestamp exceeds the deadline', async () => {
Expand Down Expand Up @@ -250,6 +256,35 @@ describe('UniversalRouter', () => {
const covenBalanceAfter = await cryptoCovens.balanceOf(alice.address)
expect(covenBalanceAfter.sub(covenBalanceBefore)).to.eq(1)
})

it('completes a trade for WETH --> ETH --> Seaport NFT', async () => {
const calldata = seaportInterface.encodeFunctionData('fulfillAdvancedOrder', [
advancedOrder,
[],
OPENSEA_CONDUIT_KEY,
alice.address,
])

planner.addCommand(CommandType.PERMIT2_TRANSFER_FROM, [WETH.address, ADDRESS_THIS, value])
planner.addCommand(CommandType.UNWRAP_WETH, [ADDRESS_THIS, value])
planner.addCommand(CommandType.SEAPORT, [value.toString(), calldata])

const { commands, inputs } = planner
const covenBalanceBefore = await cryptoCovens.balanceOf(alice.address)
const wethBalanceBefore = await wethContract.balanceOf(alice.address)
const ethBalanceBefore = await ethers.provider.getBalance(alice.address)

const receipt = await (await router['execute(bytes,bytes[],uint256)'](commands, inputs, DEADLINE)).wait()
const gasSpent = receipt.gasUsed.mul(receipt.effectiveGasPrice)

const covenBalanceAfter = await cryptoCovens.balanceOf(alice.address)
const wethBalanceAfter = await wethContract.balanceOf(alice.address)
const ethBalanceAfter = await ethers.provider.getBalance(alice.address)

expect(covenBalanceAfter.sub(covenBalanceBefore)).to.eq(1)
expect(wethBalanceBefore.sub(wethBalanceAfter)).to.eq(value)
expect(ethBalanceBefore.sub(ethBalanceAfter)).to.eq(gasSpent)
})
})
})

Expand Down
24 changes: 23 additions & 1 deletion test/integration-tests/gas-tests/UniversalRouter.gas.test.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
import { UniversalRouter, Permit2 } from '../../../typechain'
import { UniversalRouter, Permit2, IWETH9 } from '../../../typechain'
import { expect } from '../shared/expect'
import type { Contract } from '@ethersproject/contracts'
import {
ALICE_ADDRESS,
ADDRESS_THIS,
DEADLINE,
MAX_UINT,
MAX_UINT160,
OPENSEA_CONDUIT_KEY,
SOURCE_MSG_SENDER,
} from '../shared/constants'
import { abi as TOKEN_ABI } from '../../../artifacts/solmate/src/tokens/ERC20.sol/ERC20.json'
import { abi as WETH_ABI } from '../../../artifacts/contracts/interfaces/external/IWETH9.sol/IWETH9.json'
import snapshotGasCost from '@uniswap/snapshot-gas-cost'
import { resetFork, WETH, DAI } from '../shared/mainnetForkHelpers'
import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'
Expand All @@ -33,6 +35,7 @@ describe('UniversalRouter Gas Tests', () => {
let router: UniversalRouter
let permit2: Permit2
let daiContract: Contract
let wethContract: IWETH9

beforeEach(async () => {
await resetFork()
Expand All @@ -42,6 +45,7 @@ describe('UniversalRouter Gas Tests', () => {
params: [ALICE_ADDRESS],
})
daiContract = new ethers.Contract(DAI.address, TOKEN_ABI, alice)
wethContract = new ethers.Contract(WETH.address, WETH_ABI, alice) as IWETH9
permit2 = (await deployPermit2()).connect(alice) as Permit2
router = (await deployUniversalRouter(permit2)).connect(alice) as UniversalRouter
planner = new RoutePlanner()
Expand All @@ -58,7 +62,9 @@ describe('UniversalRouter Gas Tests', () => {
beforeEach(async () => {
;({ advancedOrder, value } = getAdvancedOrderParams(seaportOrders[0]))
await daiContract.approve(permit2.address, MAX_UINT)
await wethContract.approve(permit2.address, MAX_UINT)
await permit2.approve(DAI.address, router.address, MAX_UINT160, DEADLINE)
await permit2.approve(WETH.address, router.address, MAX_UINT160, DEADLINE)
})

it('gas: ETH --> Seaport NFT', async () => {
Expand Down Expand Up @@ -95,5 +101,21 @@ describe('UniversalRouter Gas Tests', () => {
const { commands, inputs } = planner
await snapshotGasCost(router['execute(bytes,bytes[],uint256)'](commands, inputs, DEADLINE, { value }))
})

it('gas: WETH --> ETH --> Seaport NFT', async () => {
const calldata = seaportInterface.encodeFunctionData('fulfillAdvancedOrder', [
advancedOrder,
[],
OPENSEA_CONDUIT_KEY,
alice.address,
])

planner.addCommand(CommandType.PERMIT2_TRANSFER_FROM, [WETH.address, ADDRESS_THIS, value])
planner.addCommand(CommandType.UNWRAP_WETH, [ADDRESS_THIS, value])
planner.addCommand(CommandType.SEAPORT, [value.toString(), calldata])

const { commands, inputs } = planner
await snapshotGasCost(router['execute(bytes,bytes[],uint256)'](commands, inputs, DEADLINE, { value }))
})
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ Object {
exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Split routes gas: ERC20 --> ERC20 split V2 and V2 different routes, each two hop, with explicit permit 1`] = `
Object {
"calldataByteLength": 1220,
"gasUsed": 316708,
"gasUsed": 316866,
}
`;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`UniversalRouter Gas Tests gas: bytecode size 1`] = `17259`;
exports[`UniversalRouter Gas Tests gas: bytecode size 1`] = `17267`;

exports[`UniversalRouter Gas Tests trading for NFTs gas: ERC20 --> ETH --> Seaport NFT 1`] = `
Object {
Expand All @@ -15,3 +15,10 @@ Object {
"gasUsed": 202513,
}
`;

exports[`UniversalRouter Gas Tests trading for NFTs gas: WETH --> ETH --> Seaport NFT 1`] = `
Object {
"calldataByteLength": 2340,
"gasUsed": 245130,
}
`;

0 comments on commit e76a51a

Please sign in to comment.