Skip to content

Commit

Permalink
Merge branch 'main' into update-native-snaps
Browse files Browse the repository at this point in the history
  • Loading branch information
marktoda committed Oct 24, 2024
2 parents 8065052 + 01ddf3a commit 6178a8e
Show file tree
Hide file tree
Showing 17 changed files with 597 additions and 88 deletions.
4 changes: 3 additions & 1 deletion script/DeployPosm.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {PositionManager} from "../src/PositionManager.sol";
import {IAllowanceTransfer} from "permit2/src/interfaces/IAllowanceTransfer.sol";
import {IPositionDescriptor} from "../src/interfaces/IPositionDescriptor.sol";
import {PositionDescriptor} from "../src/PositionDescriptor.sol";
import {IWETH9} from "../src/interfaces/external/IWETH9.sol";

contract DeployPosmTest is Script {
function setUp() public {}
Expand All @@ -30,7 +31,8 @@ contract DeployPosmTest is Script {
IPoolManager(poolManager),
IAllowanceTransfer(permit2),
unsubscribeGasLimit,
IPositionDescriptor(address(positionDescriptor))
IPositionDescriptor(address(positionDescriptor)),
IWETH9(wrappedNative)
);
console2.log("PositionManager", address(posm));

Expand Down
2 changes: 1 addition & 1 deletion snapshots/BaseActionsRouterTest.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"BaseActionsRouter_mock10commands": "60677"
"BaseActionsRouter_mock10commands": "33725"
}
8 changes: 4 additions & 4 deletions snapshots/PaymentsTests.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"Payments_swap_settleFromCaller_takeAllToMsgSender": "129642",
"Payments_swap_settleFromCaller_takeAllToSpecifiedAddress": "131705",
"Payments_swap_settleWithBalance_takeAllToMsgSender": "123910",
"Payments_swap_settleWithBalance_takeAllToSpecifiedAddress": "124052"
"Payments_swap_settleFromCaller_takeAllToMsgSender": "104210",
"Payments_swap_settleFromCaller_takeAllToSpecifiedAddress": "104961",
"Payments_swap_settleWithBalance_takeAllToMsgSender": "95138",
"Payments_swap_settleWithBalance_takeAllToSpecifiedAddress": "95052"
}
78 changes: 39 additions & 39 deletions snapshots/PosMGasTest.json
Original file line number Diff line number Diff line change
@@ -1,41 +1,41 @@
{
"PositionManager_burn_empty": "50446",
"PositionManager_burn_empty_native": "50446",
"PositionManager_burn_nonEmpty_native_withClose": "125624",
"PositionManager_burn_nonEmpty_native_withTakePair": "125106",
"PositionManager_burn_nonEmpty_withClose": "132486",
"PositionManager_burn_nonEmpty_withTakePair": "131968",
"PositionManager_collect_native": "146344",
"PositionManager_collect_sameRange": "154922",
"PositionManager_collect_withClose": "154922",
"PositionManager_collect_withTakePair": "154287",
"PositionManager_decreaseLiquidity_native": "112020",
"PositionManager_decreaseLiquidity_withClose": "119803",
"PositionManager_decreaseLiquidity_withTakePair": "119168",
"PositionManager_decrease_burnEmpty": "135283",
"PositionManager_decrease_burnEmpty_native": "128420",
"PositionManager_decrease_sameRange_allLiquidity": "132490",
"PositionManager_decrease_take_take": "120423",
"PositionManager_increaseLiquidity_erc20_withClose": "159083",
"PositionManager_increaseLiquidity_erc20_withSettlePair": "158035",
"PositionManager_increaseLiquidity_native": "140898",
"PositionManager_increase_autocompoundExactUnclaimedFees": "136359",
"PositionManager_increase_autocompoundExcessFeesCredit": "177414",
"PositionManager_increase_autocompound_clearExcess": "148040",
"PositionManager_mint_native": "364771",
"PositionManager_mint_nativeWithSweep_withClose": "373294",
"PositionManager_mint_nativeWithSweep_withSettlePair": "372529",
"PositionManager_mint_onSameTickLower": "317643",
"PositionManager_mint_onSameTickUpper": "318313",
"PositionManager_mint_sameRange": "243882",
"PositionManager_mint_settleWithBalance_sweep": "419098",
"PositionManager_mint_warmedPool_differentRange": "323674",
"PositionManager_mint_withClose": "420196",
"PositionManager_mint_withSettlePair": "419266",
"PositionManager_multicall_initialize_mint": "456001",
"PositionManager_permit": "79076",
"PositionManager_permit_secondPosition": "61976",
"PositionManager_permit_twice": "44852",
"PositionManager_subscribe": "84348",
"PositionManager_unsubscribe": "59238"
"PositionManager_burn_empty": "15061",
"PositionManager_burn_empty_native": "15061",
"PositionManager_burn_nonEmpty_native_withClose": "51029",
"PositionManager_burn_nonEmpty_native_withTakePair": "50330",
"PositionManager_burn_nonEmpty_withClose": "47267",
"PositionManager_burn_nonEmpty_withTakePair": "46568",
"PositionManager_collect_native": "77416",
"PositionManager_collect_sameRange": "73654",
"PositionManager_collect_withClose": "73654",
"PositionManager_collect_withTakePair": "72955",
"PositionManager_decreaseLiquidity_native": "44201",
"PositionManager_decreaseLiquidity_withClose": "40439",
"PositionManager_decreaseLiquidity_withTakePair": "39740",
"PositionManager_decrease_burnEmpty": "49571",
"PositionManager_decrease_burnEmpty_native": "53333",
"PositionManager_decrease_sameRange_allLiquidity": "38726",
"PositionManager_decrease_take_take": "40547",
"PositionManager_increaseLiquidity_erc20_withClose": "49647",
"PositionManager_increaseLiquidity_erc20_withSettlePair": "48903",
"PositionManager_increaseLiquidity_native": "47802",
"PositionManager_increase_autocompoundExactUnclaimedFees": "62979",
"PositionManager_increase_autocompoundExcessFeesCredit": "78466",
"PositionManager_increase_autocompound_clearExcess": "72892",
"PositionManager_mint_native": "338359",
"PositionManager_mint_nativeWithSweep_withClose": "346306",
"PositionManager_mint_nativeWithSweep_withSettlePair": "345389",
"PositionManager_mint_onSameTickLower": "256851",
"PositionManager_mint_onSameTickUpper": "261521",
"PositionManager_mint_sameRange": "162190",
"PositionManager_mint_settleWithBalance_sweep": "384846",
"PositionManager_mint_warmedPool_differentRange": "262082",
"PositionManager_mint_withClose": "393304",
"PositionManager_mint_withSettlePair": "392474",
"PositionManager_multicall_initialize_mint": "426688",
"PositionManager_permit": "53780",
"PositionManager_permit_secondPosition": "29380",
"PositionManager_permit_twice": "7480",
"PositionManager_subscribe": "55708",
"PositionManager_unsubscribe": "26756"
}
26 changes: 13 additions & 13 deletions snapshots/QuoterTest.json
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
{
"Quoter_exactInputSingle_oneForZero_multiplePositions": "143930",
"Quoter_exactInputSingle_zeroForOne_multiplePositions": "149382",
"Quoter_exactOutputSingle_oneForZero": "78203",
"Quoter_exactOutputSingle_zeroForOne": "82626",
"Quoter_quoteExactInput_oneHop_1TickLoaded": "120491",
"Quoter_quoteExactInput_oneHop_initializedAfter": "145414",
"Quoter_quoteExactInput_oneHop_startingInitialized": "79437",
"Quoter_quoteExactInput_twoHops": "201179",
"Quoter_quoteExactOutput_oneHop_1TickLoaded": "119782",
"Quoter_quoteExactOutput_oneHop_2TicksLoaded": "149919",
"Quoter_quoteExactOutput_oneHop_initializedAfter": "119850",
"Quoter_quoteExactOutput_oneHop_startingInitialized": "96549",
"Quoter_quoteExactOutput_twoHops": "200630"
"Quoter_exactInputSingle_oneForZero_multiplePositions": "121454",
"Quoter_exactInputSingle_zeroForOne_multiplePositions": "126894",
"Quoter_exactOutputSingle_oneForZero": "55727",
"Quoter_exactOutputSingle_zeroForOne": "60138",
"Quoter_quoteExactInput_oneHop_1TickLoaded": "97723",
"Quoter_quoteExactInput_oneHop_initializedAfter": "122646",
"Quoter_quoteExactInput_oneHop_startingInitialized": "46181",
"Quoter_quoteExactInput_twoHops": "177431",
"Quoter_quoteExactOutput_oneHop_1TickLoaded": "97014",
"Quoter_quoteExactOutput_oneHop_2TicksLoaded": "127151",
"Quoter_quoteExactOutput_oneHop_initializedAfter": "97082",
"Quoter_quoteExactOutput_oneHop_startingInitialized": "52493",
"Quoter_quoteExactOutput_twoHops": "176882"
}
46 changes: 23 additions & 23 deletions snapshots/V4RouterTest.json
Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
{
"V4Router_Bytecode": "7063",
"V4Router_ExactIn1Hop_nativeIn": "115753",
"V4Router_ExactIn1Hop_nativeOut": "116070",
"V4Router_ExactIn1Hop_oneForZero": "124888",
"V4Router_ExactIn1Hop_zeroForOne": "130611",
"V4Router_ExactIn2Hops": "185452",
"V4Router_ExactIn2Hops_nativeIn": "170594",
"V4Router_ExactIn3Hops": "240296",
"V4Router_ExactIn3Hops_nativeIn": "225438",
"V4Router_ExactInputSingle": "129642",
"V4Router_ExactInputSingle_nativeIn": "114784",
"V4Router_ExactInputSingle_nativeOut": "115069",
"V4Router_ExactOut1Hop_nativeIn_sweepETH": "122016",
"V4Router_ExactOut1Hop_nativeOut": "117134",
"V4Router_ExactOut1Hop_oneForZero": "125952",
"V4Router_ExactOut1Hop_zeroForOne": "129897",
"V4Router_ExactOut2Hops": "183800",
"V4Router_ExactOut2Hops_nativeIn": "175919",
"V4Router_ExactOut3Hops": "237734",
"V4Router_ExactOut3Hops_nativeIn": "229853",
"V4Router_ExactOut3Hops_nativeOut": "217089",
"V4Router_ExactOutputSingle": "128925",
"V4Router_ExactOutputSingle_nativeIn_sweepETH": "121044",
"V4Router_ExactOutputSingle_nativeOut": "116236"
"V4Router_ExactIn1Hop_nativeIn": "90557",
"V4Router_ExactIn1Hop_nativeOut": "90874",
"V4Router_ExactIn1Hop_oneForZero": "99212",
"V4Router_ExactIn1Hop_zeroForOne": "104935",
"V4Router_ExactIn2Hops": "152841",
"V4Router_ExactIn2Hops_nativeIn": "144178",
"V4Router_ExactIn3Hops": "200750",
"V4Router_ExactIn3Hops_nativeIn": "192087",
"V4Router_ExactInputSingle": "104210",
"V4Router_ExactInputSingle_nativeIn": "89832",
"V4Router_ExactInputSingle_nativeOut": "90129",
"V4Router_ExactOut1Hop_nativeIn_sweepETH": "96628",
"V4Router_ExactOut1Hop_nativeOut": "91746",
"V4Router_ExactOut1Hop_oneForZero": "100084",
"V4Router_ExactOut1Hop_zeroForOne": "104029",
"V4Router_ExactOut2Hops": "152767",
"V4Router_ExactOut2Hops_nativeIn": "149311",
"V4Router_ExactOut3Hops": "201536",
"V4Router_ExactOut3Hops_nativeIn": "198080",
"V4Router_ExactOut3Hops_nativeOut": "193198",
"V4Router_ExactOutputSingle": "103301",
"V4Router_ExactOutputSingle_nativeIn_sweepETH": "95900",
"V4Router_ExactOutputSingle_nativeOut": "91104"
}
19 changes: 16 additions & 3 deletions src/PositionManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ pragma solidity 0.8.26;
import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol";
import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol";
import {PoolIdLibrary} from "@uniswap/v4-core/src/types/PoolId.sol";
import {Currency} from "@uniswap/v4-core/src/types/Currency.sol";
import {Currency, CurrencyLibrary} from "@uniswap/v4-core/src/types/Currency.sol";
import {BalanceDelta} from "@uniswap/v4-core/src/types/BalanceDelta.sol";
import {SafeCast} from "@uniswap/v4-core/src/libraries/SafeCast.sol";
import {Position} from "@uniswap/v4-core/src/libraries/Position.sol";
Expand All @@ -26,6 +26,8 @@ import {CalldataDecoder} from "./libraries/CalldataDecoder.sol";
import {Permit2Forwarder} from "./base/Permit2Forwarder.sol";
import {SlippageCheck} from "./libraries/SlippageCheck.sol";
import {PositionInfo, PositionInfoLibrary} from "./libraries/PositionInfoLibrary.sol";
import {NativeWrapper} from "./base/NativeWrapper.sol";
import {IWETH9} from "./interfaces/external/IWETH9.sol";

// 444444444
// 444444444444 444444
Expand Down Expand Up @@ -102,7 +104,8 @@ contract PositionManager is
ReentrancyLock,
BaseActionsRouter,
Notifier,
Permit2Forwarder
Permit2Forwarder,
NativeWrapper
{
using PoolIdLibrary for PoolKey;
using StateLibrary for IPoolManager;
Expand All @@ -126,12 +129,14 @@ contract PositionManager is
IPoolManager _poolManager,
IAllowanceTransfer _permit2,
uint256 _unsubscribeGasLimit,
IPositionDescriptor _tokenDescriptor
IPositionDescriptor _tokenDescriptor,
IWETH9 _weth9
)
BaseActionsRouter(_poolManager)
Permit2Forwarder(_permit2)
ERC721Permit_v4("Uniswap v4 Positions NFT", "UNI-V4-POSM")
Notifier(_unsubscribeGasLimit)
NativeWrapper(_weth9)
{
tokenDescriptor = _tokenDescriptor;
}
Expand Down Expand Up @@ -242,6 +247,14 @@ contract PositionManager is
(Currency currency, address to) = params.decodeCurrencyAndAddress();
_sweep(currency, _mapRecipient(to));
return;
} else if (action == Actions.WRAP) {
uint256 amount = params.decodeUint256();
_wrap(_mapWrapUnwrapAmount(CurrencyLibrary.ADDRESS_ZERO, amount, Currency.wrap(address(WETH9))));
return;
} else if (action == Actions.UNWRAP) {
uint256 amount = params.decodeUint256();
_unwrap(_mapWrapUnwrapAmount(Currency.wrap(address(WETH9)), amount, CurrencyLibrary.ADDRESS_ZERO));
return;
}
}
revert UnsupportedAction(action);
Expand Down
28 changes: 28 additions & 0 deletions src/base/DeltaResolver.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ abstract contract DeltaResolver is ImmutableState {
error DeltaNotPositive(Currency currency);
/// @notice Emitted trying to take a negative delta.
error DeltaNotNegative(Currency currency);
/// @notice Emitted when the contract does not have enough balance to wrap or unwrap.
error InsufficientBalance();

/// @notice Take an amount of currency out of the PoolManager
/// @param currency Currency to take
Expand Down Expand Up @@ -91,4 +93,30 @@ abstract contract DeltaResolver is ImmutableState {
return amount;
}
}

/// @notice Calculates the sanitized amount before wrapping/unwrapping.
/// @param inputCurrency The currency, either native or wrapped native, that this contract holds
/// @param amount The amount to wrap or unwrap. Can be CONTRACT_BALANCE, OPEN_DELTA or a specific amount
/// @param outputCurrency The currency after the wrap/unwrap that the user may owe a balance in on the poolManager
function _mapWrapUnwrapAmount(Currency inputCurrency, uint256 amount, Currency outputCurrency)
internal
view
returns (uint256)
{
// if wrapping, the balance in this contract is in ETH
// if unwrapping, the balance in this contract is in WETH
uint256 balance = inputCurrency.balanceOf(address(this));
if (amount == ActionConstants.CONTRACT_BALANCE) {
// return early to avoid unnecessary balance check
return balance;
}
if (amount == ActionConstants.OPEN_DELTA) {
// if wrapping, the open currency on the PoolManager is WETH.
// if unwrapping, the open currency on the PoolManager is ETH.
// note that we use the DEBT amount. Positive deltas can be taken and then wrapped.
amount = _getFullDebt(outputCurrency);
}
if (amount > balance) revert InsufficientBalance();
return amount;
}
}
34 changes: 34 additions & 0 deletions src/base/NativeWrapper.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

import {IWETH9} from "../interfaces/external/IWETH9.sol";
import {ActionConstants} from "../libraries/ActionConstants.sol";
import {ImmutableState} from "./ImmutableState.sol";

/// @title Native Wrapper
/// @notice Used for wrapping and unwrapping native
abstract contract NativeWrapper is ImmutableState {
/// @notice The address for WETH9
IWETH9 public immutable WETH9;

/// @notice Thrown when an unexpected address sends ETH to this contract
error InvalidEthSender();

constructor(IWETH9 _weth9) {
WETH9 = _weth9;
}

/// @dev The amount should already be <= the current balance in this contract.
function _wrap(uint256 amount) internal {
if (amount > 0) WETH9.deposit{value: amount}();
}

/// @dev The amount should already be <= the current balance in this contract.
function _unwrap(uint256 amount) internal {
if (amount > 0) WETH9.withdraw(amount);
}

receive() external payable {
if (msg.sender != address(WETH9) && msg.sender != address(poolManager)) revert InvalidEthSender();
}
}
13 changes: 13 additions & 0 deletions src/interfaces/external/IWETH9.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

/// @title Interface for WETH9
interface IWETH9 is IERC20 {
/// @notice Deposit ether to get wrapped ether
function deposit() external payable;

/// @notice Withdraw wrapped ether to get ether
function withdraw(uint256) external;
}
6 changes: 4 additions & 2 deletions src/libraries/Actions.sol
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,10 @@ library Actions {
uint256 constant CLOSE_CURRENCY = 0x17;
uint256 constant CLEAR_OR_TAKE = 0x18;
uint256 constant SWEEP = 0x19;
uint256 constant WRAP = 0x20;
uint256 constant UNWRAP = 0x21;

// minting/burning 6909s to close deltas
uint256 constant MINT_6909 = 0x20;
uint256 constant BURN_6909 = 0x21;
uint256 constant MINT_6909 = 0x22;
uint256 constant BURN_6909 = 0x23;
}
7 changes: 7 additions & 0 deletions src/libraries/CalldataDecoder.sol
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,13 @@ library CalldataDecoder {
}
}

/// @dev equivalent to: abi.decode(params, (uint256)) in calldata
function decodeUint256(bytes calldata params) internal pure returns (uint256 amount) {
assembly ("memory-safe") {
amount := calldataload(params.offset)
}
}

/// @dev equivalent to: abi.decode(params, (Currency, uint256, bool)) in calldata
function decodeCurrencyUint256AndBool(bytes calldata params)
internal
Expand Down
2 changes: 1 addition & 1 deletion test/PositionDescriptor.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ contract PositionDescriptorTest is Test, PosmTestSetup {
assertEq(token.name, "Uniswap - 0.3% - TEST/TEST - 1.0060<>1.0121");
assertEq(
token.description,
unicode"This NFT represents a liquidity position in a Uniswap v4 TEST-TEST pool. The owner of this NFT can modify or redeem the position.\n\nPool Manager Address: 0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f\nTEST Address: 0x5991a2df15a8f6a256d3ec51e99254cd3fb576a9\nTEST Address: 0x2e234dae75c793f67a35089c9d99245e1c58470b\nHook Address: No Hook\nFee Tier: 0.3%\nToken ID: 1\n\n⚠️ DISCLAIMER: Due diligence is imperative when assessing this NFT. Make sure currency addresses match the expected currencies, as currency symbols may be imitated."
unicode"This NFT represents a liquidity position in a Uniswap v4 TEST-TEST pool. The owner of this NFT can modify or redeem the position.\n\nPool Manager Address: 0x2e234dae75c793f67a35089c9d99245e1c58470b\nTEST Address: 0xf62849f9a0b5bf2913b396098f7c7019b51a820a\nTEST Address: 0xc7183455a4c133ae270771860664b6b7ec320bb1\nHook Address: No Hook\nFee Tier: 0.3%\nToken ID: 1\n\n⚠️ DISCLAIMER: Due diligence is imperative when assessing this NFT. Make sure currency addresses match the expected currencies, as currency symbols may be imitated."
);
}

Expand Down
7 changes: 7 additions & 0 deletions test/libraries/CalldataDecoder.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,13 @@ contract CalldataDecoderTest is Test {
assertEq(amount, _amount);
}

function test_fuzz_decodeUint256(uint256 _amount) public {
bytes memory params = abi.encode(_amount);
uint256 amount = decoder.decodeUint256(params);

assertEq(amount, _amount);
}

function _assertEq(PathKey[] memory path1, PathKey[] memory path2) internal pure {
assertEq(path1.length, path2.length);
for (uint256 i = 0; i < path1.length; i++) {
Expand Down
Loading

0 comments on commit 6178a8e

Please sign in to comment.