Skip to content

Commit

Permalink
merge dev
Browse files Browse the repository at this point in the history
  • Loading branch information
dianakocsis committed Oct 30, 2024
2 parents 05311d3 + 419b9e6 commit 965f265
Show file tree
Hide file tree
Showing 21 changed files with 263 additions and 722 deletions.
19 changes: 14 additions & 5 deletions contracts/base/Dispatcher.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ 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';
import {IPoolManager} from '@uniswap/v4-core/src/interfaces/IPoolManager.sol';

/// @title Decodes and Executes Commands
/// @notice Called by the UniversalRouter contract to efficiently decode and execute a singular command
Expand All @@ -24,8 +26,6 @@ abstract contract Dispatcher is Payments, V2SwapRouter, V3SwapRouter, V4SwapRout

error InvalidCommandType(uint256 commandType);
error BalanceTooLow();
error InvalidAction(bytes4 action);
error NotAuthorizedForToken(uint256 tokenId);

/// @notice Executes encoded commands along with provided inputs.
/// @param commands A set of concatenated commands, each 1 byte in length
Expand Down Expand Up @@ -283,12 +283,21 @@ abstract contract Dispatcher is Payments, V2SwapRouter, V3SwapRouter, V4SwapRout
}

(success, output) = address(V3_POSITION_MANAGER).call(inputs);
} else if (command == Commands.V4_POSITION_CALL) {
} else if (command == Commands.V4_INITIALIZE_POOL) {
PoolKey calldata poolKey;
uint160 sqrtPriceX96;
assembly {
poolKey := inputs.offset
sqrtPriceX96 := calldataload(add(inputs.offset, 0xa0))
}
(success, output) =
address(poolManager).call(abi.encodeCall(IPoolManager.initialize, (poolKey, sqrtPriceX96)));
} else if (command == Commands.V4_POSITION_MANAGER_CALL) {
// should only call modifyLiquidities() to mint
// do not permit or approve this contract over a v4 position or someone could use this command to decrease, burn, or transfer your position
_checkV4PositionManagerCall(inputs);
(success, output) = address(V4_POSITION_MANAGER).call{value: address(this).balance}(inputs);
} else {
// placeholder area for commands 0x13-0x20
// placeholder area for commands 0x15-0x20
revert InvalidCommandType(command);
}
}
Expand Down
5 changes: 3 additions & 2 deletions contracts/libraries/Commands.sol
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,9 @@ library Commands {
uint256 constant V4_SWAP = 0x10;
uint256 constant V3_POSITION_MANAGER_PERMIT = 0x11;
uint256 constant V3_POSITION_MANAGER_CALL = 0x12;
uint256 constant V4_POSITION_CALL = 0x13;
// COMMAND_PLACEHOLDER = 0x14 -> 0x20
uint256 constant V4_INITIALIZE_POOL = 0x13;
uint256 constant V4_POSITION_MANAGER_CALL = 0x14;
// COMMAND_PLACEHOLDER = 0x15 -> 0x20

// Command Types where 0x21<=value<=0x3f
uint256 constant EXECUTE_SUB_PLAN = 0x21;
Expand Down
5 changes: 3 additions & 2 deletions contracts/modules/MigratorImmutables.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ pragma solidity ^0.8.24;

import {INonfungiblePositionManager} from '@uniswap/v3-periphery/contracts/interfaces/INonfungiblePositionManager.sol';
import {IPositionManager} from '@uniswap/v4-periphery/src/interfaces/IPositionManager.sol';
import {IPoolManager} from '@uniswap/v4-core/src/interfaces/IPoolManager.sol';

struct MigratorParameters {
address v3PositionManager;
Expand All @@ -12,9 +13,9 @@ struct MigratorParameters {
/// @title Migrator Immutables
/// @notice Immutable state for liquidity-migration contracts
contract MigratorImmutables {
/// @notice v3PositionManager address
/// @notice v3 PositionManager address
INonfungiblePositionManager public immutable V3_POSITION_MANAGER;
/// @notice v4PositionManager address
/// @notice v4 PositionManager address
IPositionManager public immutable V4_POSITION_MANAGER;

constructor(MigratorParameters memory params) {
Expand Down
42 changes: 42 additions & 0 deletions contracts/modules/V3ToV4Migrator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,18 @@ 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 {CalldataDecoder} from '@uniswap/v4-periphery/src/libraries/CalldataDecoder.sol';

/// @title V3 to V4 Migrator
/// @notice A contract that migrates liquidity from Uniswap V3 to V4
abstract contract V3ToV4Migrator is MigratorImmutables {
using CalldataDecoder for bytes;

error InvalidAction(bytes4 action);
error OnlyMintAllowed();
error NotAuthorizedForToken(uint256 tokenId);

/// @dev validate if an action is decreaseLiquidity, collect, or burn
function isValidAction(bytes4 selector) internal pure returns (bool) {
return selector == INonfungiblePositionManager.decreaseLiquidity.selector
Expand All @@ -20,4 +28,38 @@ abstract contract V3ToV4Migrator is MigratorImmutables {
return caller == owner || V3_POSITION_MANAGER.getApproved(tokenId) == caller
|| V3_POSITION_MANAGER.isApprovedForAll(owner, caller);
}

/// @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
/// their position, an attacker could take their fees, or drain their entire position
function _checkV4PositionManagerCall(bytes calldata inputs) internal view {
bytes4 selector;
assembly {
selector := calldataload(inputs.offset)
}
if (selector != V4_POSITION_MANAGER.modifyLiquidities.selector) {
revert InvalidAction(selector);
}

// slice is `abi.encode(bytes unlockData, uint256 deadline)`
bytes calldata slice = inputs[4:];
// the first bytes(0) extracts the unlockData parameter from modifyLiquidities
// unlockData = `abi.encode(bytes actions, bytes[] params)`
// the second bytes(0) extracts the actions parameter from unlockData
bytes calldata actions = slice.toBytes(0).toBytes(0);

uint256 numActions = actions.length;

for (uint256 actionIndex = 0; actionIndex < numActions; actionIndex++) {
uint256 action = uint8(actions[actionIndex]);

if (
action == Actions.INCREASE_LIQUIDITY || action == Actions.DECREASE_LIQUIDITY
|| action == Actions.BURN_POSITION
) {
revert OnlyMintAllowed();
}
}
}
}
4 changes: 4 additions & 0 deletions deploy-addresses/base-sepolia.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"UniversalRouter": "0x95273d871c8156636e114b63797d78D7E1720d81",
"UnsupportedProtocol": "0x7B46ee9BaB49bd5b37117494689A035b0F187B59"
}
4 changes: 4 additions & 0 deletions deploy-addresses/op-sepolia.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"UniversalRouter": "0x78e7F1502A9e4115dEbd6876E0AC4FaEBDB96880",
"UnsupportedProtocol": "0xFC885F37F5A9FA8159c8dBb907fc1b0C2fB31323"
}
4 changes: 4 additions & 0 deletions deploy-addresses/unichain-sepolia.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"UniversalRouter": "0x5418a6d871d327a1cefe6deb97153f07cbf6030e",
"UnsupportedProtocol": "0x7B46ee9BaB49bd5b37117494689A035b0F187B59"
}
6 changes: 6 additions & 0 deletions foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,10 @@ quote_style = 'single'
[profile.lite.optimizer_details.yulDetails]
optimizerSteps = ''

[rpc_endpoints]
sepolia = "https://rpc.sepolia.org"
unichain_sepolia = "https://sepolia.unichain.org"
base_sepolia = "https://sepolia.base.org"
op_sepolia = "https://sepolia.optimism.io"

# See more config options https://github.com/foundry-rs/foundry/tree/master/config
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"access": "public",
"provenance": true
},
"version": "2.0.0-beta.1",
"version": "2.0.0-beta.2",
"keywords": [
"uniswap",
"router",
Expand Down
1 change: 0 additions & 1 deletion remappings.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
solmate/=lib/solmate/
permit2/=lib/permit2/
forge-std/=lib/forge-std/src/
@openzeppelin/contracts=node_modules/@openzeppelin/contracts
@uniswap/v3-core/=node_modules/@uniswap/v3-core/
@uniswap/v2-core/=node_modules/@uniswap/v2-core/
@uniswap/v3-periphery/=lib/v3-periphery/
Expand Down
23 changes: 23 additions & 0 deletions script/deployParameters/DeployBaseSepolia.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.26;

import {DeployUniversalRouter} from '../DeployUniversalRouter.s.sol';
import {RouterParameters} from 'contracts/base/RouterImmutables.sol';

contract DeployBaseSepolia is DeployUniversalRouter {
function setUp() public override {
params = RouterParameters({
permit2: 0x000000000022D473030F116dDEE9F6B43aC78BA3,
weth9: 0x4200000000000000000000000000000000000006,
v2Factory: 0x7Ae58f10f7849cA6F5fB71b7f45CB416c9204b1e,
v3Factory: 0x4752ba5DBc23f44D87826276BF6Fd6b1C372aD24,
pairInitCodeHash: 0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f,
poolInitCodeHash: 0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54,
v4PoolManager: 0x7Da1D65F8B249183667cdE74C5CBD46dD38AA829,
v3NFTPositionManager: 0x27F971cb582BF9E50F397e4d29a5C7A34f11faA2,
v4PositionManager: 0xcDbe7b1ed817eF0005ECe6a3e576fbAE2EA5EAFE
});

unsupported = 0x76870DEbef0BE25589A5CddCe9B1D99276C73B4e;
}
}
23 changes: 23 additions & 0 deletions script/deployParameters/DeployOPSepolia.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.26;

import {DeployUniversalRouter} from '../DeployUniversalRouter.s.sol';
import {RouterParameters} from 'contracts/base/RouterImmutables.sol';

contract DeployOPSepolia is DeployUniversalRouter {
function setUp() public override {
params = RouterParameters({
permit2: 0x000000000022D473030F116dDEE9F6B43aC78BA3,
weth9: 0x4200000000000000000000000000000000000006,
v2Factory: UNSUPPORTED_PROTOCOL,
v3Factory: 0x4752ba5DBc23f44D87826276BF6Fd6b1C372aD24,
pairInitCodeHash: 0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f,
poolInitCodeHash: 0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54,
v4PoolManager: 0xE5dF461803a59292c6c03978c17857479c40bc46,
v3NFTPositionManager: 0x27F971cb582BF9E50F397e4d29a5C7A34f11faA2,
v4PositionManager: 0xEf3853450006cE9FB12B540486c920c9a705F502
});

unsupported = 0xFC885F37F5A9FA8159c8dBb907fc1b0C2fB31323;
}
}
23 changes: 23 additions & 0 deletions script/deployParameters/DeployUnichainSepolia.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.26;

import {DeployUniversalRouter} from '../DeployUniversalRouter.s.sol';
import {RouterParameters} from 'contracts/base/RouterImmutables.sol';

contract DeployUnichainSepolia is DeployUniversalRouter {
function setUp() public override {
params = RouterParameters({
permit2: 0x000000000022D473030F116dDEE9F6B43aC78BA3,
weth9: 0x4200000000000000000000000000000000000006,
v2Factory: 0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f,
v3Factory: 0x1F98431c8aD98523631AE4a59f267346ea31F984,
pairInitCodeHash: 0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f,
poolInitCodeHash: 0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54,
v4PoolManager: 0xC81462Fec8B23319F288047f8A03A57682a35C1A,
v3NFTPositionManager: 0xB7F724d6dDDFd008eFf5cc2834edDE5F9eF0d075,
v4PositionManager: 0xB433cB9BcDF4CfCC5cAB7D34f90d1a7deEfD27b9
});

unsupported = 0x76870DEbef0BE25589A5CddCe9B1D99276C73B4e;
}
}
2 changes: 1 addition & 1 deletion test/integration-tests/UniswapV2.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ describe('Uniswap V2 Tests:', () => {

beforeEach(async () => {
// cancel the permit on DAI
await permit2.approve(DAI.address, ADDRESS_ZERO, 0, 0)
await permit2.approve(DAI.address, router.address, 0, 0)
})

it('Permit2 can silently fail', async () => {
Expand Down
Loading

0 comments on commit 965f265

Please sign in to comment.