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

v3 refactor and pull latest periphery #418

Merged
merged 4 commits into from
Nov 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 2 additions & 30 deletions contracts/base/Dispatcher.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import {Commands} from '../libraries/Commands.sol';
import {Lock} from './Lock.sol';
import {ERC20} from 'solmate/src/tokens/ERC20.sol';
import {IAllowanceTransfer} from 'permit2/src/interfaces/IAllowanceTransfer.sol';
import {IERC721Permit} from '@uniswap/v3-periphery/contracts/interfaces/IERC721Permit.sol';
import {ActionConstants} from '@uniswap/v4-periphery/src/libraries/ActionConstants.sol';
import {CalldataDecoder} from '@uniswap/v4-periphery/src/libraries/CalldataDecoder.sol';
import {PoolKey} from '@uniswap/v4-core/src/types/PoolKey.sol';
Expand Down Expand Up @@ -237,37 +236,10 @@ abstract contract Dispatcher is Payments, V2SwapRouter, V3SwapRouter, V4SwapRout
_executeActions(inputs);
// This contract MUST be approved to spend the token since its going to be doing the call on the position manager
} else if (command == Commands.V3_POSITION_MANAGER_PERMIT) {
bytes4 selector;
assembly {
selector := calldataload(inputs.offset)
}
if (selector != IERC721Permit.permit.selector) {
revert InvalidAction(selector);
}

_checkV3PermitCall(inputs);
(success, output) = address(V3_POSITION_MANAGER).call(inputs);
} else if (command == Commands.V3_POSITION_MANAGER_CALL) {
bytes4 selector;
assembly {
selector := calldataload(inputs.offset)
}
if (!isValidAction(selector)) {
revert InvalidAction(selector);
}

uint256 tokenId;
assembly {
// tokenId is always the first parameter in the valid actions
tokenId := calldataload(add(inputs.offset, 0x04))
}
// If any other address that is not the owner wants to call this function, it also needs to be approved (in addition to this contract)
// This can be done in 2 ways:
// 1. This contract is permitted for the specific token and the caller is approved for ALL of the owner's tokens
// 2. This contract is permitted for ALL of the owner's tokens and the caller is permitted for the specific token
if (!isAuthorizedForToken(msgSender(), tokenId)) {
revert NotAuthorizedForToken(tokenId);
}

_checkV3PositionManagerCall(inputs, msgSender());
(success, output) = address(V3_POSITION_MANAGER).call(inputs);
} else if (command == Commands.V4_INITIALIZE_POOL) {
PoolKey calldata poolKey;
Expand Down
13 changes: 0 additions & 13 deletions contracts/interfaces/external/IWETH9.sol

This file was deleted.

2 changes: 1 addition & 1 deletion contracts/modules/PaymentsImmutables.sol
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.24;

import {IWETH9} from '../interfaces/external/IWETH9.sol';
import {IWETH9} from '@uniswap/v4-periphery/src/interfaces/external/IWETH9.sol';
import {IPermit2} from 'permit2/src/interfaces/IPermit2.sol';

struct PaymentsParameters {
Expand Down
42 changes: 40 additions & 2 deletions contracts/modules/V3ToV4Migrator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pragma solidity ^0.8.24;
import {MigratorImmutables} from '../modules/MigratorImmutables.sol';
import {INonfungiblePositionManager} from '@uniswap/v3-periphery/contracts/interfaces/INonfungiblePositionManager.sol';
import {Actions} from '@uniswap/v4-periphery/src/libraries/Actions.sol';
import {IERC721Permit} from '@uniswap/v3-periphery/contracts/interfaces/IERC721Permit.sol';
import {CalldataDecoder} from '@uniswap/v4-periphery/src/libraries/CalldataDecoder.sol';

/// @title V3 to V4 Migrator
Expand All @@ -16,19 +17,56 @@ abstract contract V3ToV4Migrator is MigratorImmutables {
error NotAuthorizedForToken(uint256 tokenId);

/// @dev validate if an action is decreaseLiquidity, collect, or burn
function isValidAction(bytes4 selector) internal pure returns (bool) {
function _isValidAction(bytes4 selector) private pure returns (bool) {
return selector == INonfungiblePositionManager.decreaseLiquidity.selector
|| selector == INonfungiblePositionManager.collect.selector
|| selector == INonfungiblePositionManager.burn.selector;
}

/// @dev the caller is authorized for the token if its the owner, spender, or operator
function isAuthorizedForToken(address caller, uint256 tokenId) internal view returns (bool) {
function _isAuthorizedForToken(address caller, uint256 tokenId) private view returns (bool) {
address owner = V3_POSITION_MANAGER.ownerOf(tokenId);
return caller == owner || V3_POSITION_MANAGER.getApproved(tokenId) == caller
|| V3_POSITION_MANAGER.isApprovedForAll(owner, caller);
}

/// @dev check that a call is to the ERC721 permit function
function _checkV3PermitCall(bytes calldata inputs) internal pure {
bytes4 selector;
assembly {
selector := calldataload(inputs.offset)
}

if (selector != IERC721Permit.permit.selector) {
revert InvalidAction(selector);
}
}

/// @dev check that the v3 position manager call is a safe call
function _checkV3PositionManagerCall(bytes calldata inputs, address caller) internal view {
bytes4 selector;
assembly {
selector := calldataload(inputs.offset)
}

if (!_isValidAction(selector)) {
revert InvalidAction(selector);
}

uint256 tokenId;
assembly {
// tokenId is always the first parameter in the valid actions
tokenId := calldataload(add(inputs.offset, 0x04))
}
// If any other address that is not the owner wants to call this function, it also needs to be approved (in addition to this contract)
// This can be done in 2 ways:
// 1. This contract is permitted for the specific token and the caller is approved for ALL of the owner's tokens
// 2. This contract is permitted for ALL of the owner's tokens and the caller is permitted for the specific token
if (!_isAuthorizedForToken(caller, tokenId)) {
revert NotAuthorizedForToken(tokenId);
}
}

/// @dev check that the v4 position manager call is a safe call
/// of the position-altering Actions, we only allow Actions.MINT
/// this is because, if a user could be tricked into approving the UniversalRouter for
Expand Down
2 changes: 1 addition & 1 deletion lib/v4-periphery
Submodule v4-periphery updated 122 files
2 changes: 1 addition & 1 deletion test/integration-tests/CheckOwnership.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ describe('Check Ownership', () => {
params: [ALICE_ADDRESS],
})
alice = await ethers.getSigner(ALICE_ADDRESS)
router = (await deployUniversalRouter()).connect(alice) as UniversalRouter
router = (await deployUniversalRouter(alice.address)).connect(alice) as UniversalRouter
usdcContract = new ethers.Contract(USDC.address, TOKEN_ABI, alice)
aliceUSDCBalance = await usdcContract.balanceOf(ALICE_ADDRESS)
planner = new RoutePlanner()
Expand Down
4 changes: 2 additions & 2 deletions test/integration-tests/UniswapMixed.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,8 @@ describe('Uniswap V2, V3, and V4 Tests:', () => {
usdcContract = new ethers.Contract(USDC.address, TOKEN_ABI, bob)
permit2 = PERMIT2.connect(bob) as IPermit2

v4PoolManager = (await deployV4PoolManager()).connect(bob) as PoolManager
router = (await deployUniversalRouter(v4PoolManager.address)).connect(bob) as UniversalRouter
v4PoolManager = (await deployV4PoolManager(bob.address)).connect(bob) as PoolManager
router = (await deployUniversalRouter(undefined, v4PoolManager.address)).connect(bob) as UniversalRouter

v4PositionManager = (await ethers.getContractAt('PositionManager', await router.V4_POSITION_MANAGER())).connect(
bob
Expand Down
2 changes: 1 addition & 1 deletion test/integration-tests/UniswapV2.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ describe('Uniswap V2 Tests:', () => {
wethContract = new ethers.Contract(WETH.address, TOKEN_ABI, bob)
usdcContract = new ethers.Contract(USDC.address, TOKEN_ABI, bob)
permit2 = PERMIT2.connect(bob) as IPermit2
router = (await deployUniversalRouter()) as UniversalRouter
router = (await deployUniversalRouter(bob.address)) as UniversalRouter
planner = new RoutePlanner()

// alice gives bob some tokens
Expand Down
2 changes: 1 addition & 1 deletion test/integration-tests/UniswapV3.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ describe('Uniswap V3 Tests:', () => {
wethContract = new ethers.Contract(WETH.address, TOKEN_ABI, bob)
usdcContract = new ethers.Contract(USDC.address, TOKEN_ABI, bob)
permit2 = PERMIT2.connect(bob) as IPermit2
router = (await deployUniversalRouter()) as UniversalRouter
router = (await deployUniversalRouter(bob.address)) as UniversalRouter
planner = new RoutePlanner()

// alice gives bob some tokens
Expand Down
4 changes: 2 additions & 2 deletions test/integration-tests/UniswapV4.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,8 @@ describe('Uniswap V4 Tests:', () => {
wethContract = new ethers.Contract(WETH.address, TOKEN_ABI, bob)
usdcContract = new ethers.Contract(USDC.address, TOKEN_ABI, bob)
permit2 = PERMIT2.connect(bob) as IPermit2
v4PoolManager = (await deployV4PoolManager()).connect(bob) as PoolManager
router = (await deployUniversalRouter(v4PoolManager.address)).connect(bob) as UniversalRouter
v4PoolManager = (await deployV4PoolManager(bob.address)).connect(bob) as PoolManager
router = (await deployUniversalRouter(undefined, v4PoolManager.address)).connect(bob) as UniversalRouter
v4PositionManager = (await ethers.getContractAt('PositionManager', await router.V4_POSITION_MANAGER())).connect(
bob
) as PositionManager
Expand Down
8 changes: 5 additions & 3 deletions test/integration-tests/UniversalRouter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ 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 { abi as WETH_ABI } from '../../artifacts/@uniswap/v4-periphery/src/interfaces/external/IWETH9.sol/IWETH9.json'

import deployUniversalRouter from './shared/deployUniversalRouter'
import {
Expand Down Expand Up @@ -45,7 +45,7 @@ describe('UniversalRouter', () => {
wethContract = new ethers.Contract(WETH.address, WETH_ABI, alice) as IWETH9
pair_DAI_WETH = await makePair(alice, DAI, WETH)
permit2 = PERMIT2.connect(alice) as IPermit2
router = (await deployUniversalRouter()).connect(alice) as UniversalRouter
router = (await deployUniversalRouter(alice.address)).connect(alice) as UniversalRouter
})

describe('#execute', () => {
Expand Down Expand Up @@ -120,7 +120,9 @@ describe('UniversalRouter', () => {
const sweepCalldata = routerInterface.encodeFunctionData('execute(bytes,bytes[])', [commands, inputs])

const reentrantWETH = await (await ethers.getContractFactory('ReenteringWETH')).deploy()
router = (await deployUniversalRouter(undefined, reentrantWETH.address)).connect(alice) as UniversalRouter
router = (await deployUniversalRouter(alice.address, undefined, reentrantWETH.address)).connect(
alice
) as UniversalRouter
await reentrantWETH.setParameters(router.address, sweepCalldata)

planner = new RoutePlanner()
Expand Down
2 changes: 1 addition & 1 deletion test/integration-tests/V3ToV4Migration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ describe('V3 to V4 Migration Tests:', () => {
wethContract = new ethers.Contract(WETH.address, TOKEN_ABI, bob)
usdcContract = new ethers.Contract(USDC.address, TOKEN_ABI, bob)
v3NFTPositionManager = V3_NFT_POSITION_MANAGER.connect(bob) as INonfungiblePositionManager
router = (await deployUniversalRouter()) as UniversalRouter
router = (await deployUniversalRouter(bob.address)) as UniversalRouter
v4PositionManagerAddress = await router.V4_POSITION_MANAGER()
v4PositionManager = (await ethers.getContractAt('PositionManager', v4PositionManagerAddress)) as PositionManager

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ describe('Check Ownership Gas', () => {
params: [ALICE_ADDRESS],
})
alice = await ethers.getSigner(ALICE_ADDRESS)
router = (await deployUniversalRouter()).connect(alice) as UniversalRouter
router = (await deployUniversalRouter(alice.address)).connect(alice) as UniversalRouter
planner = new RoutePlanner()
})

Expand Down
4 changes: 2 additions & 2 deletions test/integration-tests/gas-tests/Payments.gas.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import deployUniversalRouter from '../shared/deployUniversalRouter'
import { RoutePlanner, CommandType } from '../shared/planner'
import snapshotGasCost from '@uniswap/snapshot-gas-cost'
const { ethers } = hre
import WETH_ABI from '../../../artifacts/contracts/interfaces/external/IWETH9.sol/IWETH9.json'
import WETH_ABI from '../../../artifacts/@uniswap/v4-periphery/src/interfaces/external/IWETH9.sol/IWETH9.json'
import { BigNumber } from 'ethers'
import { ADDRESS_THIS } from '@uniswap/router-sdk'

Expand All @@ -32,7 +32,7 @@ describe('Payments Gas Tests', () => {
bob = (await ethers.getSigners())[1]
daiContract = new ethers.Contract(DAI.address, TOKEN_ABI, alice)
wethContract = new ethers.Contract(WETH.address, new ethers.utils.Interface(WETH_ABI.abi), alice)
router = (await deployUniversalRouter()).connect(alice) as UniversalRouter
router = (await deployUniversalRouter(alice.address)).connect(alice) as UniversalRouter
planner = new RoutePlanner()
})

Expand Down
2 changes: 1 addition & 1 deletion test/integration-tests/gas-tests/Uniswap.gas.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ describe('Uniswap Gas Tests', () => {
daiContract = new ethers.Contract(DAI.address, TOKEN_ABI, bob)
wethContract = new ethers.Contract(WETH.address, TOKEN_ABI, bob)
permit2 = PERMIT2.connect(bob) as IPermit2
router = (await deployUniversalRouter()).connect(bob) as UniversalRouter
router = (await deployUniversalRouter(bob.address)).connect(bob) as UniversalRouter
pair_DAI_WETH = await makePair(bob, DAI, WETH)
pair_DAI_USDC = await makePair(bob, DAI, USDC)
pair_USDC_WETH = await makePair(bob, USDC, WETH)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { UniversalRouter, IWETH9, ERC20 } from '../../../typechain'
import { expect } from '../shared/expect'
import { ALICE_ADDRESS } 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 { abi as WETH_ABI } from '../../../artifacts/@uniswap/v4-periphery/src/interfaces/external/IWETH9.sol/IWETH9.json'
import { resetFork, WETH, DAI } from '../shared/mainnetForkHelpers'
import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'
import hre from 'hardhat'
Expand All @@ -27,7 +27,7 @@ describe('UniversalRouter Gas Tests', () => {
})
daiContract = new ethers.Contract(DAI.address, TOKEN_ABI, alice) as ERC20
wethContract = new ethers.Contract(WETH.address, WETH_ABI, alice) as IWETH9
router = (await deployUniversalRouter()).connect(alice) as UniversalRouter
router = (await deployUniversalRouter(alice.address)).connect(alice) as UniversalRouter
planner = new RoutePlanner()
})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ describe('Uniswap UX Tests gas:', () => {
usdcContract = ERC20__factory.connect(USDC.address, alice)

permit2 = PERMIT2.connect(alice) as IPermit2
router = (await deployUniversalRouter()).connect(bob) as UniversalRouter
router = (await deployUniversalRouter(bob.address)).connect(bob) as UniversalRouter

planner = new RoutePlanner()

Expand Down Expand Up @@ -466,7 +466,7 @@ describe('Uniswap UX Tests gas:', () => {
}

// Launch SwapRouter03
const router2 = (await deployUniversalRouter()).connect(bob) as UniversalRouter
const router2 = (await deployUniversalRouter(bob.address)).connect(bob) as UniversalRouter
const router2ApprovalTx = (await approveSwapRouter02(bob, USDC, router2.address))!
totalGas = totalGas.add(router2ApprovalTx.gasUsed)

Expand All @@ -477,7 +477,7 @@ describe('Uniswap UX Tests gas:', () => {
}

// Launch SwapRouter04
const router3 = (await deployUniversalRouter()).connect(bob) as UniversalRouter
const router3 = (await deployUniversalRouter(bob.address)).connect(bob) as UniversalRouter
const router3ApprovalTx = (await approveSwapRouter02(bob, USDC, router3.address))!
totalGas = totalGas.add(router3ApprovalTx.gasUsed)

Expand Down Expand Up @@ -506,7 +506,7 @@ describe('Uniswap UX Tests gas:', () => {
}

// Launch Universal Router v2
const router2 = (await deployUniversalRouter()).connect(bob) as UniversalRouter
const router2 = (await deployUniversalRouter(bob.address)).connect(bob) as UniversalRouter

// Do 5 simple swaps
for (let i = 0; i < 5; i++) {
Expand All @@ -520,7 +520,7 @@ describe('Uniswap UX Tests gas:', () => {
}

// Launch Universal Router v3
const router3 = (await deployUniversalRouter()).connect(bob) as UniversalRouter
const router3 = (await deployUniversalRouter(bob.address)).connect(bob) as UniversalRouter

// Do 5 simple swaps
for (let i = 0; i < 5; i++) {
Expand Down Expand Up @@ -552,7 +552,7 @@ describe('Uniswap UX Tests gas:', () => {
}

// Launch Universal Router v2
const router2 = (await deployUniversalRouter()).connect(bob) as UniversalRouter
const router2 = (await deployUniversalRouter(bob.address)).connect(bob) as UniversalRouter
MAX_PERMIT.spender = router2.address
let calldata2 = await getPermitSignature(MAX_PERMIT, bob, permit2)
planner.addCommand(CommandType.PERMIT2_PERMIT, [MAX_PERMIT, calldata2])
Expand All @@ -565,7 +565,7 @@ describe('Uniswap UX Tests gas:', () => {
}

// Launch Universal Router v3
const router3 = (await deployUniversalRouter()).connect(bob) as UniversalRouter
const router3 = (await deployUniversalRouter(bob.address)).connect(bob) as UniversalRouter
MAX_PERMIT.spender = router3.address
let calldata3 = await getPermitSignature(MAX_PERMIT, bob, permit2)
planner.addCommand(CommandType.PERMIT2_PERMIT, [MAX_PERMIT, calldata3])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ describe('V3 to V4 Migration Gas Tests', () => {
wethContract = new ethers.Contract(WETH.address, TOKEN_ABI, bob)
usdcContract = new ethers.Contract(USDC.address, TOKEN_ABI, bob)
v3NFTPositionManager = V3_NFT_POSITION_MANAGER.connect(bob) as INonfungiblePositionManager
router = (await deployUniversalRouter()).connect(bob) as UniversalRouter
router = (await deployUniversalRouter(bob.address)).connect(bob) as UniversalRouter
v4PositionManagerAddress = await router.V4_POSITION_MANAGER()
v4PositionManager = (await ethers.getContractAt('PositionManager', v4PositionManagerAddress)) as PositionManager
planner = new RoutePlanner()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`UniversalRouter Gas Tests gas: bytecode size 1`] = `19595`;
exports[`UniversalRouter Gas Tests gas: bytecode size 1`] = `19619`;
Loading
Loading