From 2434576aeb0aa031fc38b9582f25f3b78a63ece6 Mon Sep 17 00:00:00 2001 From: dianakocsis Date: Tue, 20 Aug 2024 13:52:11 -0400 Subject: [PATCH 01/45] position descriptor stuff --- lib/v4-core | 2 +- src/PositionDescriptor.sol | 129 ++++++ src/interfaces/IPositionDescriptor.sol | 14 + src/libraries/AddressStringUtil.sol | 34 ++ src/libraries/CurrencyRatioSortOrder.sol | 12 + src/libraries/Descriptor.sol | 489 +++++++++++++++++++++++ src/libraries/HexStrings.sol | 15 + src/libraries/NFTSVG.sol | 408 +++++++++++++++++++ src/libraries/SafeERC20Namer.sol | 93 +++++ test/PositionDescriptor.t.sol | 100 +++++ test/libraries/Descriptor.t.sol | 111 +++++ 11 files changed, 1406 insertions(+), 1 deletion(-) create mode 100644 src/PositionDescriptor.sol create mode 100644 src/interfaces/IPositionDescriptor.sol create mode 100644 src/libraries/AddressStringUtil.sol create mode 100644 src/libraries/CurrencyRatioSortOrder.sol create mode 100644 src/libraries/Descriptor.sol create mode 100644 src/libraries/HexStrings.sol create mode 100644 src/libraries/NFTSVG.sol create mode 100644 src/libraries/SafeERC20Namer.sol create mode 100644 test/PositionDescriptor.t.sol create mode 100644 test/libraries/Descriptor.t.sol diff --git a/lib/v4-core b/lib/v4-core index d3be0026a..e37e01097 160000 --- a/lib/v4-core +++ b/lib/v4-core @@ -1 +1 @@ -Subproject commit d3be0026a60a429c76d302b3aebbc20000191256 +Subproject commit e37e01097e8d949c8a2d9711960d086569cf4e62 diff --git a/src/PositionDescriptor.sol b/src/PositionDescriptor.sol new file mode 100644 index 000000000..14065c47a --- /dev/null +++ b/src/PositionDescriptor.sol @@ -0,0 +1,129 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.24; + +import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol"; +import {Currency, CurrencyLibrary} from "@uniswap/v4-core/src/types/Currency.sol"; +import {IHooks} from "@uniswap/v4-core/src/interfaces/IHooks.sol"; +import {StateLibrary} from "@uniswap/v4-core/src/libraries/StateLibrary.sol"; +import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol"; +import {PoolId, PoolIdLibrary} from "@uniswap/v4-core/src/types/PoolId.sol"; +import {PositionConfig} from "../src/libraries/PositionConfig.sol"; +import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; +import "./interfaces/IPositionDescriptor.sol"; +import "./libraries/Descriptor.sol"; +import "./interfaces/IPositionManager.sol"; +import "./libraries/CurrencyRatioSortOrder.sol"; +import "./libraries/SafeERC20Namer.sol"; + +import "forge-std/console2.sol"; + +/// @title Describes NFT token positions +/// @notice Produces a string containing the data URI for a JSON metadata string +contract PositionDescriptor is IPositionDescriptor { + using StateLibrary for IPoolManager; + using PoolIdLibrary for PoolKey; + using CurrencyLibrary for Currency; + + address private constant DAI = 0x6B175474E89094C44Da98b954EedeAC495271d0F; + address private constant USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48; + address private constant USDT = 0xdAC17F958D2ee523a2206206994597C13D831ec7; + address private constant TBTC = 0x8dAEBADE922dF735c38C80C7eBD708Af50815fAa; + address private constant WBTC = 0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599; + + address public immutable WETH9; + /// @dev A null-terminated string + bytes32 public immutable nativeCurrencyLabelBytes; + + IPoolManager public immutable poolManager; + + constructor(IPoolManager _poolManager, address _WETH9, bytes32 _nativeCurrencyLabel) { + poolManager = _poolManager; + WETH9 = _WETH9; + nativeCurrencyLabelBytes = _nativeCurrencyLabel; + } + + /// @notice Returns the native currency label as a string + function nativeCurrencyLabel() public view returns (string memory) { + uint256 len = 0; + while (len < 32 && nativeCurrencyLabelBytes[len] != 0) { + len++; + } + bytes memory b = new bytes(len); + for (uint256 i = 0; i < len; i++) { + b[i] = nativeCurrencyLabelBytes[i]; + } + return string(b); + } + + /// @inheritdoc IPositionDescriptor + function tokenURI(uint256 tokenId, PositionConfig calldata config) external view override returns (string memory) { + // no way of getting the config from just the tokenId. + // could get configId from tokenId, but no way to get config from configId because it is a hash + + (, int24 tick,,) = poolManager.getSlot0(config.poolKey.toId()); + + PoolKey memory poolKey = config.poolKey; + int24 tickLower = config.tickLower; + int24 tickUpper = config.tickUpper; + Currency currency0 = Currency(config.poolKey.currency0); + Currency currency1 = Currency(config.poolKey.currency1); + + bool _flipRatio = flipRatio(Currency.unwrap(currency0), Currency.unwrap(currency1)); + Currency quoteCurrency = !_flipRatio ? currency1 : currency0; + Currency baseCurrency = !_flipRatio ? currency0 : currency1; + + return Descriptor.constructTokenURI( + Descriptor.ConstructTokenURIParams({ + tokenId: tokenId, + quoteCurrency: quoteCurrency, + baseCurrency: baseCurrency, + quoteCurrencySymbol: Currency.unwrap(quoteCurrency) == WETH9 || quoteCurrency.isNative() + ? nativeCurrencyLabel() + : SafeERC20Namer.tokenSymbol(Currency.unwrap(quoteCurrency)), + baseCurrencySymbol: Currency.unwrap(baseCurrency) == WETH9 || baseCurrency.isNative() + ? nativeCurrencyLabel() + : SafeERC20Namer.tokenSymbol(Currency.unwrap(baseCurrency)), + quoteCurrencyDecimals: quoteCurrency.isNative() + ? 18 + : IERC20Metadata(Currency.unwrap(quoteCurrency)).decimals(), + baseCurrencyDecimals: baseCurrency.isNative() + ? 18 + : IERC20Metadata(Currency.unwrap(baseCurrency)).decimals(), + flipRatio: _flipRatio, + tickLower: tickLower, + tickUpper: tickUpper, + tickCurrent: tick, + tickSpacing: poolKey.tickSpacing, + fee: poolKey.fee, + poolManager: address(poolManager), + hooks: address(poolKey.hooks) + }) + ); + } + + function flipRatio(address currency0, address currency1) public view returns (bool) { + return currencyRatioPriority(currency0) > currencyRatioPriority(currency1); + } + + function currencyRatioPriority(address currency) public view returns (int256) { + if (currency == WETH9) { + return CurrencyRatioSortOrder.DENOMINATOR; + } + if (block.chainid == 1) { + if (currency == USDC) { + return CurrencyRatioSortOrder.NUMERATOR_MOST; + } else if (currency == USDT) { + return CurrencyRatioSortOrder.NUMERATOR_MORE; + } else if (currency == DAI) { + return CurrencyRatioSortOrder.NUMERATOR; + } else if (currency == TBTC) { + return CurrencyRatioSortOrder.DENOMINATOR_MORE; + } else if (currency == WBTC) { + return CurrencyRatioSortOrder.DENOMINATOR_MOST; + } else { + return 0; + } + } + return 0; + } +} diff --git a/src/interfaces/IPositionDescriptor.sol b/src/interfaces/IPositionDescriptor.sol new file mode 100644 index 000000000..fc8ad2134 --- /dev/null +++ b/src/interfaces/IPositionDescriptor.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.24; + +import "./IPositionManager.sol"; +import "../libraries/PositionConfig.sol"; + +/// @title Describes position NFT tokens via URI +interface IPositionDescriptor { + /// @notice Produces the URI describing a particular token ID + /// @dev Note this URI may be a data: URI with the JSON contents directly inlined + /// @param tokenId The ID of the token for which to produce a description, which may not be valid + /// @return The URI of the ERC721-compliant metadata + function tokenURI(uint256 tokenId, PositionConfig calldata config) external view returns (string memory); +} diff --git a/src/libraries/AddressStringUtil.sol b/src/libraries/AddressStringUtil.sol new file mode 100644 index 000000000..3edb6c16a --- /dev/null +++ b/src/libraries/AddressStringUtil.sol @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.24; + +library AddressStringUtil { + // converts an address to the uppercase hex string, extracting only len bytes (up to 20, multiple of 2) + function toAsciiString(address addr, uint256 len) internal pure returns (string memory) { + require(len % 2 == 0 && len > 0 && len <= 40, "AddressStringUtil: INVALID_LEN"); + + bytes memory s = new bytes(len); + uint256 addrNum = uint256(uint160(addr)); + for (uint256 i = 0; i < len / 2; i++) { + // shift right and truncate all but the least significant byte to extract the byte at position 19-i + uint8 b = uint8(addrNum >> (8 * (19 - i))); + // first hex character is the most significant 4 bits + uint8 hi = b >> 4; + // second hex character is the least significant 4 bits + uint8 lo = b - (hi << 4); + s[2 * i] = char(hi); + s[2 * i + 1] = char(lo); + } + return string(s); + } + + // hi and lo are only 4 bits and between 0 and 16 + // this method converts those values to the unicode/ascii code point for the hex representation + // uses upper case for the characters + function char(uint8 b) private pure returns (bytes1 c) { + if (b < 10) { + return bytes1(b + 0x30); + } else { + return bytes1(b + 0x37); + } + } +} diff --git a/src/libraries/CurrencyRatioSortOrder.sol b/src/libraries/CurrencyRatioSortOrder.sol new file mode 100644 index 000000000..dc5f58686 --- /dev/null +++ b/src/libraries/CurrencyRatioSortOrder.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.24; + +library CurrencyRatioSortOrder { + int256 constant NUMERATOR_MOST = 300; + int256 constant NUMERATOR_MORE = 200; + int256 constant NUMERATOR = 100; + + int256 constant DENOMINATOR_MOST = -300; + int256 constant DENOMINATOR_MORE = -200; + int256 constant DENOMINATOR = -100; +} diff --git a/src/libraries/Descriptor.sol b/src/libraries/Descriptor.sol new file mode 100644 index 000000000..99589e1ac --- /dev/null +++ b/src/libraries/Descriptor.sol @@ -0,0 +1,489 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.24; + +import {Currency, CurrencyLibrary} from "@uniswap/v4-core/src/types/Currency.sol"; +import {IHooks} from "@uniswap/v4-core/src/interfaces/IHooks.sol"; +import {TickMath} from "@uniswap/v4-core/src/libraries/TickMath.sol"; +import {FullMath} from "@uniswap/v4-core/src/libraries/FullMath.sol"; +import "@openzeppelin/contracts/utils/Strings.sol"; +import "@openzeppelin/contracts/utils/Base64.sol"; +import "./NFTSVG.sol"; +import "./HexStrings.sol"; + +import "forge-std/console2.sol"; + +library Descriptor { + using TickMath for int24; + using Strings for uint256; + using HexStrings for uint256; + + uint256 constant sqrt10X128 = 1076067327063303206878105757264492625226; + + struct ConstructTokenURIParams { + uint256 tokenId; + Currency quoteCurrency; + Currency baseCurrency; + string quoteCurrencySymbol; + string baseCurrencySymbol; + uint8 quoteCurrencyDecimals; + uint8 baseCurrencyDecimals; + bool flipRatio; + int24 tickLower; + int24 tickUpper; + int24 tickCurrent; + int24 tickSpacing; + uint24 fee; + address poolManager; + address hooks; + } + + function constructTokenURI(ConstructTokenURIParams calldata params) public pure returns (string memory) { + string memory name = generateName(params, feeToPercentString(params.fee)); + string memory descriptionPartOne = generateDescriptionPartOne( + escapeQuotes(params.quoteCurrencySymbol), + escapeQuotes(params.baseCurrencySymbol), + addressToString(params.poolManager) + ); + string memory descriptionPartTwo = generateDescriptionPartTwo( + params.tokenId.toString(), + escapeQuotes(params.baseCurrencySymbol), + addressToString(Currency.unwrap(params.quoteCurrency)), + addressToString(Currency.unwrap(params.baseCurrency)), + addressToString(params.hooks), + feeToPercentString(params.fee) + ); + //string memory image = Base64.encode(bytes(generateSVGImage(params))); + string memory image = ""; + + return string( + abi.encodePacked( + "data:application/json;base64,", + Base64.encode( + bytes( + abi.encodePacked( + '{"name":"', + name, + '", "description":"', + descriptionPartOne, + descriptionPartTwo, + '", "image": "', + "data:image/svg+xml;base64,", + image, + '"}' + ) + ) + ) + ) + ); + } + + function escapeQuotes(string memory symbol) internal pure returns (string memory) { + bytes memory symbolBytes = bytes(symbol); + uint8 quotesCount = 0; + // count the amount of double quotes (") in the symbol + for (uint8 i = 0; i < symbolBytes.length; i++) { + if (symbolBytes[i] == '"') { + quotesCount++; + } + } + if (quotesCount > 0) { + // create a new bytes array with enough space to hold the original bytes plus space for the backslashes to escape the quotes + bytes memory escapedBytes = new bytes(symbolBytes.length + quotesCount); + uint256 index; + for (uint8 i = 0; i < symbolBytes.length; i++) { + // add a '\' before any double quotes + if (symbolBytes[i] == '"') { + escapedBytes[index++] = "\\"; + } + // copy each byte from original string to the new array + escapedBytes[index++] = symbolBytes[i]; + } + return string(escapedBytes); + } + return symbol; + } + + function generateDescriptionPartOne( + string memory quoteCurrencySymbol, + string memory baseCurrencySymbol, + string memory poolManager + ) private pure returns (string memory) { + return string( + abi.encodePacked( + "This NFT represents a liquidity position in a Uniswap V4 ", + quoteCurrencySymbol, + "-", + baseCurrencySymbol, + " pool. ", + "The owner of this NFT can modify or redeem the position.\\n", + "\\nPool Manager Address: ", + poolManager, + "\\n", + quoteCurrencySymbol + ) + ); + } + + function generateDescriptionPartTwo( + string memory tokenId, + string memory baseCurrencySymbol, + string memory quoteCurrency, + string memory baseCurrency, + string memory hooks, + string memory feeTier + ) private pure returns (string memory) { + return string( + abi.encodePacked( + " Address: ", + quoteCurrency, + "\\n", + baseCurrencySymbol, + " Address: ", + baseCurrency, + "\\nHook Address: ", + hooks, + "\\nFee Tier: ", + feeTier, + "\\nToken ID: ", + tokenId, + "\\n\\n", + unicode"⚠️ DISCLAIMER: Due diligence is imperative when assessing this NFT. Make sure currency addresses match the expected currencies, as currency symbols may be imitated." + ) + ); + } + + function generateName(ConstructTokenURIParams calldata params, string memory feeTier) + private + pure + returns (string memory) + { + return string( + abi.encodePacked( + "Uniswap - ", + feeTier, + " - ", + escapeQuotes(params.quoteCurrencySymbol), + "/", + escapeQuotes(params.baseCurrencySymbol), + " - ", + tickToDecimalString( + !params.flipRatio ? params.tickLower : params.tickUpper, + params.tickSpacing, + params.baseCurrencyDecimals, + params.quoteCurrencyDecimals, + params.flipRatio + ), + "<>", + tickToDecimalString( + !params.flipRatio ? params.tickUpper : params.tickLower, + params.tickSpacing, + params.baseCurrencyDecimals, + params.quoteCurrencyDecimals, + params.flipRatio + ) + ) + ); + } + + struct DecimalStringParams { + // significant figures of decimal + uint256 sigfigs; + // length of decimal string + uint8 bufferLength; + // ending index for significant figures (funtion works backwards when copying sigfigs) + uint8 sigfigIndex; + // index of decimal place (0 if no decimal) + uint8 decimalIndex; + // start index for trailing/leading 0's for very small/large numbers + uint8 zerosStartIndex; + // end index for trailing/leading 0's for very small/large numbers + uint8 zerosEndIndex; + // true if decimal number is less than one + bool isLessThanOne; + // true if string should include "%" + bool isPercent; + } + + function generateDecimalString(DecimalStringParams memory params) private pure returns (string memory) { + bytes memory buffer = new bytes(params.bufferLength); + if (params.isPercent) { + buffer[buffer.length - 1] = "%"; + } + if (params.isLessThanOne) { + buffer[0] = "0"; + buffer[1] = "."; + } + + // add leading/trailing 0's + for (uint256 zerosCursor = params.zerosStartIndex; zerosCursor < params.zerosEndIndex + 1; zerosCursor++) { + // converts the ASCII code for 0 (which is 48) into a bytes1 to store in the buffer + buffer[zerosCursor] = bytes1(uint8(48)); + } + // add sigfigs + while (params.sigfigs > 0) { + if (params.decimalIndex > 0 && params.sigfigIndex == params.decimalIndex) { + buffer[params.sigfigIndex--] = "."; + } + buffer[params.sigfigIndex] = bytes1(uint8(48 + (params.sigfigs % 10))); + // can overflow when sigfigIndex = 0 + unchecked { + params.sigfigIndex--; + } + params.sigfigs /= 10; + } + return string(buffer); + } + + function tickToDecimalString( + int24 tick, + int24 tickSpacing, + uint8 baseCurrencyDecimals, + uint8 quoteCurrencyDecimals, + bool flipRatio + ) internal pure returns (string memory) { + if (tick == (TickMath.MIN_TICK / tickSpacing) * tickSpacing) { + return !flipRatio ? "MIN" : "MAX"; + } else if (tick == (TickMath.MAX_TICK / tickSpacing) * tickSpacing) { + return !flipRatio ? "MAX" : "MIN"; + } else { + uint160 sqrtRatioX96 = TickMath.getSqrtPriceAtTick(tick); + if (flipRatio) { + sqrtRatioX96 = uint160(uint256(1 << 192) / sqrtRatioX96); + } + return fixedPointToDecimalString(sqrtRatioX96, baseCurrencyDecimals, quoteCurrencyDecimals); + } + } + + function sigfigsRounded(uint256 value, uint8 digits) private pure returns (uint256, bool) { + bool extraDigit; + if (digits > 5) { + value = value / ((10 ** (digits - 5))); + } + bool roundUp = value % 10 > 4; + value = value / (10); + if (roundUp) { + value = value + 1; + } + // 99999 -> 100000 gives an extra sigfig + if (value == 100000) { + value /= 10; + extraDigit = true; + } + return (value, extraDigit); + } + + function adjustForDecimalPrecision(uint160 sqrtRatioX96, uint8 baseCurrencyDecimals, uint8 quoteCurrencyDecimals) + private + pure + returns (uint256 adjustedSqrtRatioX96) + { + uint256 difference = abs(int256(uint256(baseCurrencyDecimals)) - (int256(uint256(quoteCurrencyDecimals)))); + if (difference > 0 && difference <= 18) { + if (baseCurrencyDecimals > quoteCurrencyDecimals) { + adjustedSqrtRatioX96 = sqrtRatioX96 * (10 ** (difference / 2)); + if (difference % 2 == 1) { + adjustedSqrtRatioX96 = FullMath.mulDiv(adjustedSqrtRatioX96, sqrt10X128, 1 << 128); + } + } else { + adjustedSqrtRatioX96 = sqrtRatioX96 / (10 ** (difference / 2)); + if (difference % 2 == 1) { + adjustedSqrtRatioX96 = FullMath.mulDiv(adjustedSqrtRatioX96, 1 << 128, sqrt10X128); + } + } + } else { + adjustedSqrtRatioX96 = uint256(sqrtRatioX96); + } + } + + function abs(int256 x) private pure returns (uint256) { + return uint256(x >= 0 ? x : -x); + } + + // @notice Returns string that includes first 5 significant figures of a decimal number + // @param sqrtRatioX96 a sqrt price + function fixedPointToDecimalString(uint160 sqrtRatioX96, uint8 baseCurrencyDecimals, uint8 quoteCurrencyDecimals) + internal + pure + returns (string memory) + { + console2.log("fixedpointtodecimalstring"); + console2.log("sqrtRatioX96", sqrtRatioX96); + uint256 adjustedSqrtRatioX96 = + adjustForDecimalPrecision(sqrtRatioX96, baseCurrencyDecimals, quoteCurrencyDecimals); + console2.log("adjustedSqrtRatioX96", adjustedSqrtRatioX96); + uint256 value = FullMath.mulDiv(adjustedSqrtRatioX96, adjustedSqrtRatioX96, 1 << 64); + + bool priceBelow1 = adjustedSqrtRatioX96 < 2 ** 96; + if (priceBelow1) { + // 10 ** 43 is precision needed to retreive 5 sigfigs of smallest possible price + 1 for rounding + value = FullMath.mulDiv(value, 10 ** 44, 1 << 128); + } else { + // leave precision for 4 decimal places + 1 place for rounding + value = FullMath.mulDiv(value, 10 ** 5, 1 << 128); + } + + // get digit count + uint256 temp = value; + uint8 digits; + while (temp != 0) { + digits++; + temp /= 10; + } + // don't count extra digit kept for rounding + digits = digits - 1; + + // address rounding + (uint256 sigfigs, bool extraDigit) = sigfigsRounded(value, digits); + if (extraDigit) { + digits++; + } + + DecimalStringParams memory params; + if (priceBelow1) { + // 7 bytes ( "0." and 5 sigfigs) + leading 0's bytes + params.bufferLength = uint8(uint8(7) + (uint8(43) - digits)); + params.zerosStartIndex = 2; + params.zerosEndIndex = uint8(uint256(43) - digits + 1); + params.sigfigIndex = uint8(params.bufferLength - 1); + } else if (digits >= 9) { + // no decimal in price string + params.bufferLength = uint8(digits - 4); + params.zerosStartIndex = 5; + params.zerosEndIndex = uint8(params.bufferLength - 1); + params.sigfigIndex = 4; + } else { + // 5 sigfigs surround decimal + params.bufferLength = 6; + params.sigfigIndex = 5; + params.decimalIndex = uint8(digits - 5 + 1); + } + params.sigfigs = sigfigs; + params.isLessThanOne = priceBelow1; + params.isPercent = false; + + return generateDecimalString(params); + } + + // @notice Returns string as decimal percentage of fee amount. + // @param fee fee amount + function feeToPercentString(uint24 fee) internal pure returns (string memory) { + if (fee == 0) { + return "0%"; + } + uint24 temp = fee; + uint256 digits; + uint8 numSigfigs; + // iterates over each digit of fee by dividing temp by 10 in each iteration until temp becomes 0 + // calculates number of digits and number of significant figures (non-zero digits) + while (temp != 0) { + if (numSigfigs > 0) { + // count all digits preceding least significant figure + numSigfigs++; + } else if (temp % 10 != 0) { + numSigfigs++; + } + digits++; + temp /= 10; + } + + DecimalStringParams memory params; + uint256 nZeros; + if (digits >= 5) { + // represents fee greater than or equal to 1% + // if decimal > 1 (5th digit is the ones place) + uint256 decimalPlace = digits - numSigfigs >= 4 ? 0 : 1; + nZeros = digits - 5 < numSigfigs - 1 ? 0 : digits - 5 - (numSigfigs - 1); + params.zerosStartIndex = numSigfigs; + params.zerosEndIndex = uint8(params.zerosStartIndex + nZeros - 1); + params.sigfigIndex = uint8(params.zerosStartIndex - 1 + decimalPlace); + params.bufferLength = uint8(nZeros + numSigfigs + 1 + decimalPlace); + } else { + // represents fee less than 1% + // else if decimal < 1 + nZeros = 5 - digits; // number of zeros, inlcuding the zero before decimal + params.zerosStartIndex = 2; // leading zeros will start after the decimal point + params.zerosEndIndex = uint8(nZeros + params.zerosStartIndex - 1); // end index for leading zeros + params.bufferLength = uint8(nZeros + numSigfigs + 2); // total length of string buffer, including "0." and "%" + params.sigfigIndex = uint8(params.bufferLength - 2); // index of starting signficant figure + params.isLessThanOne = true; + } + params.sigfigs = uint256(fee) / (10 ** (digits - numSigfigs)); // the signficant figures of the fee + params.isPercent = true; + params.decimalIndex = digits > 4 ? uint8(digits - 4) : 0; // based on total number of digits in the fee + + return generateDecimalString(params); + } + + function addressToString(address addr) internal pure returns (string memory) { + return (uint256(uint160(addr))).toHexString(20); + } + + // function generateSVGImage(ConstructTokenURIParams memory params) internal pure returns (string memory svg) { + // NFTSVG.SVGParams memory svgParams = + // NFTSVG.SVGParams({ + // quoteCurrency: addressToString(Currency.unwrap(params.quoteCurrency)), + // baseCurrency: addressToString(Currency.unwrap(params.baseCurrency)), + // hooks: params.hooks, + // quoteCurrencySymbol: params.quoteCurrencySymbol, + // baseCurrencySymbol: params.baseCurrencySymbol, + // feeTier: feeToPercentString(params.fee), + // tickLower: params.tickLower, + // tickUpper: params.tickUpper, + // tickSpacing: params.tickSpacing, + // overRange: overRange(params.tickLower, params.tickUpper, params.tickCurrent), + // tokenId: params.tokenId, + // color0: currencyToColorHex(params.quoteCurrency.toId(), 136), + // color1: currencyToColorHex(params.baseCurrency.toId(), 136), + // color2: currencyToColorHex(params.quoteCurrency.toId(), 0), + // color3: currencyToColorHex(params.baseCurrency.toId(), 0), + // x1: scale(getCircleCoord(params.quoteCurrency.toId(), 16, params.tokenId), 0, 255, 16, 274), + // y1: scale(getCircleCoord(params.baseCurrency.toId(), 16, params.tokenId), 0, 255, 100, 484), + // x2: scale(getCircleCoord(params.quoteCurrency.toId(), 32, params.tokenId), 0, 255, 16, 274), + // y2: scale(getCircleCoord(params.baseCurrency.toId(), 32, params.tokenId), 0, 255, 100, 484), + // x3: scale(getCircleCoord(params.quoteCurrency.toId(), 48, params.tokenId), 0, 255, 16, 274), + // y3: scale(getCircleCoord(params.baseCurrency.toId(), 48, params.tokenId), 0, 255, 100, 484) + // }); + + // return NFTSVG.generateSVG(svgParams); + // } + + // function overRange( + // int24 tickLower, + // int24 tickUpper, + // int24 tickCurrent + // ) private pure returns (int8) { + // if (tickCurrent < tickLower) { + // return -1; + // } else if (tickCurrent > tickUpper) { + // return 1; + // } else { + // return 0; + // } + // } + + // function scale( + // uint256 n, + // uint256 inMn, + // uint256 inMx, + // uint256 outMn, + // uint256 outMx + // ) private pure returns (string memory) { + // return (n - (inMn) * (outMx - (outMn)) / (inMx - (inMn)) + (outMn)).toString(); + // } + + // function currencyToColorHex(uint256 currency, uint256 offset) internal pure returns (string memory str) { + // return string((currency >> offset).toHexStringNoPrefix(3)); + // } + + // function getCircleCoord( + // uint256 currency, + // uint256 offset, + // uint256 tokenId + // ) internal pure returns (uint256) { + // return (sliceTokenHex(currency, offset) * tokenId) % 255; + // } + + // function sliceTokenHex(uint256 currency, uint256 offset) internal pure returns (uint256) { + // return uint256(uint8(currency >> offset)); + // } +} diff --git a/src/libraries/HexStrings.sol b/src/libraries/HexStrings.sol new file mode 100644 index 000000000..334b32475 --- /dev/null +++ b/src/libraries/HexStrings.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.24; + +library HexStrings { + bytes16 internal constant ALPHABET = "0123456789abcdef"; + + function toHexStringNoPrefix(uint256 value, uint256 length) internal pure returns (string memory) { + bytes memory buffer = new bytes(2 * length); + for (uint256 i = buffer.length; i > 0; i--) { + buffer[i - 1] = ALPHABET[value & 0xf]; + value >>= 4; + } + return string(buffer); + } +} diff --git a/src/libraries/NFTSVG.sol b/src/libraries/NFTSVG.sol new file mode 100644 index 000000000..6188dda20 --- /dev/null +++ b/src/libraries/NFTSVG.sol @@ -0,0 +1,408 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.24; + +// import {Currency, CurrencyLibrary} from "@uniswap/v4-core/src/types/Currency.sol"; +// import {IHooks} from "@uniswap/v4-core/src/interfaces/IHooks.sol"; +// import '@openzeppelin/contracts/utils/Strings.sol'; +// import '@uniswap/v4-core/src/libraries/BitMath.sol'; +// import '@openzeppelin/contracts/utils/Base64.sol'; + +// /// @title NFTSVG +// /// @notice Provides a function for generating an SVG associated with a Uniswap NFT +// library NFTSVG { +// using Strings for uint256; + +// string constant curve1 = 'M1 1C41 41 105 105 145 145'; +// string constant curve2 = 'M1 1C33 49 97 113 145 145'; +// string constant curve3 = 'M1 1C33 57 89 113 145 145'; +// string constant curve4 = 'M1 1C25 65 81 121 145 145'; +// string constant curve5 = 'M1 1C17 73 73 129 145 145'; +// string constant curve6 = 'M1 1C9 81 65 137 145 145'; +// string constant curve7 = 'M1 1C1 89 57.5 145 145 145'; +// string constant curve8 = 'M1 1C1 97 49 145 145 145'; + +// struct SVGParams { +// string quoteCurrency; +// string baseCurrency; +// address hooks; +// string quoteCurrencySymbol; +// string baseCurrencySymbol; +// string feeTier; +// int24 tickLower; +// int24 tickUpper; +// int24 tickSpacing; +// int8 overRange; +// uint256 tokenId; +// string color0; +// string color1; +// string color2; +// string color3; +// string x1; +// string y1; +// string x2; +// string y2; +// string x3; +// string y3; +// } + +// function generateSVG(SVGParams memory params) internal pure returns (string memory svg) { +// /* +// address: "0xe8ab59d3bcde16a29912de83a90eb39628cfc163", +// msg: "Forged in SVG for Uniswap in 2021 by 0xe8ab59d3bcde16a29912de83a90eb39628cfc163", +// sig: "0x2df0e99d9cbfec33a705d83f75666d98b22dea7c1af412c584f7d626d83f02875993df740dc87563b9c73378f8462426da572d7989de88079a382ad96c57b68d1b", +// version: "2" +// */ +// return +// string( +// abi.encodePacked( +// generateSVGDefs(params), +// generateSVGBorderText( +// params.quoteCurrency, +// params.baseCurrency, +// params.quoteCurrencySymbol, +// params.baseCurrencySymbol +// ), +// generateSVGCardMantle(params.quoteCurrencySymbol, params.baseCurrencySymbol, params.feeTier), +// generageSvgCurve(params.tickLower, params.tickUpper, params.tickSpacing, params.overRange), +// generateSVGPositionDataAndLocationCurve( +// params.tokenId.toString(), +// params.tickLower, +// params.tickUpper +// ), +// generateSVGRareSparkle(params.tokenId, params.hooks), +// '' +// ) +// ); +// } + +// function generateSVGDefs(SVGParams memory params) private pure returns (string memory svg) { +// svg = string( +// abi.encodePacked( +// '", +// '', +// '" +// ) +// ) +// ), +// '"/>" +// ) +// ) +// ), +// '"/>" +// ) +// ) +// ), +// '" />', +// '" +// ) +// ) +// ), +// '" /> ', +// '', +// '', +// '', +// '', +// '', +// '', +// '', +// '', +// '', +// '', +// '', +// '', +// '', +// '', +// ' ', +// '', +// '', +// '' +// ) +// ); +// } + +// function generateSVGBorderText( +// string memory quoteCurrency, +// string memory baseCurrency, +// string memory quoteCurrencySymbol, +// string memory baseCurrencySymbol +// ) private pure returns (string memory svg) { +// svg = string( +// abi.encodePacked( +// '', +// '', +// baseCurrency, +// unicode' • ', +// baseCurrencySymbol, +// ' ', +// ' ', +// baseCurrency, +// unicode' • ', +// baseCurrencySymbol, +// ' ', +// '', +// quoteCurrency, +// unicode' • ', +// quoteCurrencySymbol, +// ' ', +// quoteCurrency, +// unicode' • ', +// quoteCurrencySymbol, +// ' ' +// ) +// ); +// } + +// function generateSVGCardMantle( +// string memory quoteCurrencySymbol, +// string memory baseCurrencySymbol, +// string memory feeTier +// ) private pure returns (string memory svg) { +// svg = string( +// abi.encodePacked( +// ' ', +// quoteCurrencySymbol, +// '/', +// baseCurrencySymbol, +// '', +// feeTier, +// '', +// '' +// ) +// ); +// } + +// function generageSvgCurve( +// int24 tickLower, +// int24 tickUpper, +// int24 tickSpacing, +// int8 overRange +// ) private pure returns (string memory svg) { +// string memory fade = overRange == 1 ? '#fade-up' : overRange == -1 ? '#fade-down' : '#none'; +// string memory curve = getCurve(tickLower, tickUpper, tickSpacing); +// svg = string( +// abi.encodePacked( +// '' +// '' +// '', +// '', +// '', +// '', +// generateSVGCurveCircle(overRange) +// ) +// ); +// } + +// function getCurve( +// int24 tickLower, +// int24 tickUpper, +// int24 tickSpacing +// ) internal pure returns (string memory curve) { +// int24 tickRange = (tickUpper - tickLower) / tickSpacing; +// if (tickRange <= 4) { +// curve = curve1; +// } else if (tickRange <= 8) { +// curve = curve2; +// } else if (tickRange <= 16) { +// curve = curve3; +// } else if (tickRange <= 32) { +// curve = curve4; +// } else if (tickRange <= 64) { +// curve = curve5; +// } else if (tickRange <= 128) { +// curve = curve6; +// } else if (tickRange <= 256) { +// curve = curve7; +// } else { +// curve = curve8; +// } +// } + +// function generateSVGCurveCircle(int8 overRange) internal pure returns (string memory svg) { +// string memory curvex1 = '73'; +// string memory curvey1 = '190'; +// string memory curvex2 = '217'; +// string memory curvey2 = '334'; +// if (overRange == 1 || overRange == -1) { +// svg = string( +// abi.encodePacked( +// '' +// ) +// ); +// } else { +// svg = string( +// abi.encodePacked( +// '', +// '' +// ) +// ); +// } +// } + +// function generateSVGPositionDataAndLocationCurve( +// string memory tokenId, +// int24 tickLower, +// int24 tickUpper +// ) private pure returns (string memory svg) { +// string memory tickLowerStr = tickToString(tickLower); +// string memory tickUpperStr = tickToString(tickUpper); +// uint256 str1length = bytes(tokenId).length + 4; +// uint256 str2length = bytes(tickLowerStr).length + 10; +// uint256 str3length = bytes(tickUpperStr).length + 10; +// (string memory xCoord, string memory yCoord) = rangeLocation(tickLower, tickUpper); +// svg = string( +// abi.encodePacked( +// ' ', +// '', +// 'ID: ', +// tokenId, +// '', +// ' ', +// '', +// 'Min Tick: ', +// tickLowerStr, +// '', +// ' ', +// '', +// 'Max Tick: ', +// tickUpperStr, +// '' +// '', +// '', +// '', +// '' +// ) +// ); +// } + +// function tickToString(int24 tick) private pure returns (string memory) { +// string memory sign = ''; +// if (tick < 0) { +// tick = tick * -1; +// sign = '-'; +// } +// return string(abi.encodePacked(sign, uint256(uint24(tick)).toString())); +// } + +// function rangeLocation(int24 tickLower, int24 tickUpper) internal pure returns (string memory, string memory) { +// int24 midPoint = (tickLower + tickUpper) / 2; +// if (midPoint < -125_000) { +// return ('8', '7'); +// } else if (midPoint < -75_000) { +// return ('8', '10.5'); +// } else if (midPoint < -25_000) { +// return ('8', '14.25'); +// } else if (midPoint < -5_000) { +// return ('10', '18'); +// } else if (midPoint < 0) { +// return ('11', '21'); +// } else if (midPoint < 5_000) { +// return ('13', '23'); +// } else if (midPoint < 25_000) { +// return ('15', '25'); +// } else if (midPoint < 75_000) { +// return ('18', '26'); +// } else if (midPoint < 125_000) { +// return ('21', '27'); +// } else { +// return ('24', '27'); +// } +// } + +// function generateSVGRareSparkle(uint256 tokenId, address hooks) private pure returns (string memory svg) { +// if (isRare(tokenId, hooks)) { +// svg = string( +// abi.encodePacked( +// '', +// '', +// '' +// ) +// ); +// } else { +// svg = ''; +// } +// } + +// function isRare(uint256 tokenId, address hooks) internal pure returns (bool) { +// bytes32 h = keccak256(abi.encodePacked(tokenId, hooks)); +// return uint256(h) < type(uint256).max / (1 + BitMath.mostSignificantBit(tokenId) * 2); +// } +// } diff --git a/src/libraries/SafeERC20Namer.sol b/src/libraries/SafeERC20Namer.sol new file mode 100644 index 000000000..5660a9d17 --- /dev/null +++ b/src/libraries/SafeERC20Namer.sol @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.24; + +import "./AddressStringUtil.sol"; + +// produces token descriptors from inconsistent or absent ERC20 symbol implementations that can return string or bytes32 +// this library will always produce a string symbol to represent the token +library SafeERC20Namer { + function bytes32ToString(bytes32 x) private pure returns (string memory) { + bytes memory bytesString = new bytes(32); + uint256 charCount = 0; + for (uint256 j = 0; j < 32; j++) { + bytes1 char = x[j]; + if (char != 0) { + bytesString[charCount] = char; + charCount++; + } + } + bytes memory bytesStringTrimmed = new bytes(charCount); + for (uint256 j = 0; j < charCount; j++) { + bytesStringTrimmed[j] = bytesString[j]; + } + return string(bytesStringTrimmed); + } + + // assumes the data is in position 2 + function parseStringData(bytes memory b) private pure returns (string memory) { + uint256 charCount = 0; + // first parse the charCount out of the data + for (uint256 i = 32; i < 64; i++) { + charCount <<= 8; + charCount += uint8(b[i]); + } + + bytes memory bytesStringTrimmed = new bytes(charCount); + for (uint256 i = 0; i < charCount; i++) { + bytesStringTrimmed[i] = b[i + 64]; + } + + return string(bytesStringTrimmed); + } + + // uses a heuristic to produce a token name from the address + // the heuristic returns the full hex of the address string in upper case + function addressToName(address token) private pure returns (string memory) { + return AddressStringUtil.toAsciiString(token, 40); + } + + // uses a heuristic to produce a token symbol from the address + // the heuristic returns the first 6 hex of the address string in upper case + function addressToSymbol(address token) private pure returns (string memory) { + return AddressStringUtil.toAsciiString(token, 6); + } + + // calls an external view token contract method that returns a symbol or name, and parses the output into a string + function callAndParseStringReturn(address token, bytes4 selector) private view returns (string memory) { + (bool success, bytes memory data) = token.staticcall(abi.encodeWithSelector(selector)); + // if not implemented, or returns empty data, return empty string + if (!success || data.length == 0) { + return ""; + } + // bytes32 data always has length 32 + if (data.length == 32) { + bytes32 decoded = abi.decode(data, (bytes32)); + return bytes32ToString(decoded); + } else if (data.length > 64) { + return abi.decode(data, (string)); + } + return ""; + } + + // attempts to extract the token symbol. if it does not implement symbol, returns a symbol derived from the address + function tokenSymbol(address token) internal view returns (string memory) { + // 0x95d89b41 = bytes4(keccak256("symbol()")) + string memory symbol = callAndParseStringReturn(token, 0x95d89b41); + if (bytes(symbol).length == 0) { + // fallback to 6 uppercase hex of address + return addressToSymbol(token); + } + return symbol; + } + + // attempts to extract the token name. if it does not implement name, returns a name derived from the address + function tokenName(address token) internal view returns (string memory) { + // 0x06fdde03 = bytes4(keccak256("name()")) + string memory name = callAndParseStringReturn(token, 0x06fdde03); + if (bytes(name).length == 0) { + // fallback to full hex of address + return addressToName(token); + } + return name; + } +} diff --git a/test/PositionDescriptor.t.sol b/test/PositionDescriptor.t.sol new file mode 100644 index 000000000..a0d576e6f --- /dev/null +++ b/test/PositionDescriptor.t.sol @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +import "forge-std/Test.sol"; +import {Deployers} from "@uniswap/v4-core/test/utils/Deployers.sol"; +import {PositionDescriptor} from "../src/PositionDescriptor.sol"; +import {CurrencyRatioSortOrder} from "../src/libraries/CurrencyRatioSortOrder.sol"; +import {IHooks} from "@uniswap/v4-core/src/interfaces/IHooks.sol"; +import {PosmTestSetup} from "./shared/PosmTestSetup.sol"; +import {LiquidityAmounts} from "@uniswap/v4-core/test/utils/LiquidityAmounts.sol"; +import {TickMath} from "@uniswap/v4-core/src/libraries/TickMath.sol"; +import {PositionConfig} from "../src/libraries/PositionConfig.sol"; + +import {ActionConstants} from "../src/libraries/ActionConstants.sol"; +import "forge-std/console2.sol"; + +contract PositionDescriptorTest is Test, Deployers, PosmTestSetup { + PositionDescriptor public positionDescriptor; + address public WETH9 = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; + address public DAI = 0x6B175474E89094C44Da98b954EedeAC495271d0F; + address public USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48; + address public USDT = 0xdAC17F958D2ee523a2206206994597C13D831ec7; + address public TBTC = 0x8dAEBADE922dF735c38C80C7eBD708Af50815fAa; + address public WBTC = 0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599; + bytes32 public nativeCurrencyLabel = "ETH"; + + function setUp() public { + deployFreshManager(); + // need to pass in WETH address and native currency label + positionDescriptor = new PositionDescriptor(manager, WETH9, nativeCurrencyLabel); + + (currency0, currency1) = deployAndMint2Currencies(); + (key,) = initPool(currency0, currency1, IHooks(address(0)), 3000, SQRT_PRICE_1_1, ZERO_BYTES); + deployAndApprovePosm(manager); + } + + function test_setup_succeeds() public view { + assertEq(address(positionDescriptor.poolManager()), address(manager)); + assertEq(positionDescriptor.WETH9(), WETH9); + assertEq(positionDescriptor.nativeCurrencyLabelBytes(), nativeCurrencyLabel); + } + + function test_nativeCurrencyLabel_succeeds() public view { + assertEq(positionDescriptor.nativeCurrencyLabel(), "ETH"); + } + + function test_currencyRatioPriority_mainnet_succeeds() public { + vm.chainId(1); + assertEq(positionDescriptor.currencyRatioPriority(WETH9), CurrencyRatioSortOrder.DENOMINATOR); + assertEq(positionDescriptor.currencyRatioPriority(USDC), CurrencyRatioSortOrder.NUMERATOR_MOST); + assertEq(positionDescriptor.currencyRatioPriority(USDT), CurrencyRatioSortOrder.NUMERATOR_MORE); + assertEq(positionDescriptor.currencyRatioPriority(DAI), CurrencyRatioSortOrder.NUMERATOR); + assertEq(positionDescriptor.currencyRatioPriority(TBTC), CurrencyRatioSortOrder.DENOMINATOR_MORE); + assertEq(positionDescriptor.currencyRatioPriority(WBTC), CurrencyRatioSortOrder.DENOMINATOR_MOST); + assertEq(positionDescriptor.currencyRatioPriority(makeAddr("ALICE")), 0); + } + + function test_currencyRatioPriority_notMainnet_succeeds() public { + assertEq(positionDescriptor.currencyRatioPriority(WETH9), CurrencyRatioSortOrder.DENOMINATOR); + assertEq(positionDescriptor.currencyRatioPriority(USDC), 0); + assertEq(positionDescriptor.currencyRatioPriority(USDT), 0); + assertEq(positionDescriptor.currencyRatioPriority(DAI), 0); + assertEq(positionDescriptor.currencyRatioPriority(TBTC), 0); + assertEq(positionDescriptor.currencyRatioPriority(WBTC), 0); + assertEq(positionDescriptor.currencyRatioPriority(makeAddr("ALICE")), 0); + } + + function test_flipRatio_succeeds() public { + vm.chainId(1); + // bc price = token1/token0 + assertTrue(positionDescriptor.flipRatio(USDC, DAI)); + assertTrue(positionDescriptor.flipRatio(WETH9, WBTC)); + assertTrue(positionDescriptor.flipRatio(DAI, WBTC)); + assertFalse(positionDescriptor.flipRatio(WBTC, DAI)); + } + + function test_tokenURI_succeeds() public { + // create v4 pool (already created in setup) + // mint a position + int24 tickLower = -int24(key.tickSpacing); + int24 tickUpper = int24(key.tickSpacing); + uint256 amount0Desired = 100e18; + uint256 amount1Desired = 100e18; + uint256 liquidityToAdd = LiquidityAmounts.getLiquidityForAmounts( + SQRT_PRICE_1_1, + TickMath.getSqrtPriceAtTick(tickLower), + TickMath.getSqrtPriceAtTick(tickUpper), + amount0Desired, + amount1Desired + ); + + PositionConfig memory config = PositionConfig({poolKey: key, tickLower: tickLower, tickUpper: tickUpper}); + uint256 tokenId = lpm.nextTokenId(); + mint(config, liquidityToAdd, ActionConstants.MSG_SENDER, ZERO_BYTES); + // call tokenURI + console2.log("tokenURI", positionDescriptor.tokenURI(tokenId, config)); + // decode json + // check that name and description are correct + } +} diff --git a/test/libraries/Descriptor.t.sol b/test/libraries/Descriptor.t.sol new file mode 100644 index 000000000..968eb4adb --- /dev/null +++ b/test/libraries/Descriptor.t.sol @@ -0,0 +1,111 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +import {Descriptor} from "../../src/libraries/Descriptor.sol"; +import {Test} from "forge-std/Test.sol"; +import {TickMath} from "@uniswap/v4-core/src/libraries/TickMath.sol"; +import "forge-std/console2.sol"; + +contract DescriptorTest is Test { + function test_feeToPercentString_succeeds() public { + assertEq(Descriptor.feeToPercentString(0), "0%"); + assertEq(Descriptor.feeToPercentString(1), "0.0001%"); + assertEq(Descriptor.feeToPercentString(30), "0.003%"); + assertEq(Descriptor.feeToPercentString(33), "0.0033%"); + assertEq(Descriptor.feeToPercentString(500), "0.05%"); + assertEq(Descriptor.feeToPercentString(2500), "0.25%"); + assertEq(Descriptor.feeToPercentString(3000), "0.3%"); + assertEq(Descriptor.feeToPercentString(10000), "1%"); + assertEq(Descriptor.feeToPercentString(17000), "1.7%"); + assertEq(Descriptor.feeToPercentString(100000), "10%"); + assertEq(Descriptor.feeToPercentString(150000), "15%"); + assertEq(Descriptor.feeToPercentString(102000), "10.2%"); + assertEq(Descriptor.feeToPercentString(1000000), "100%"); + assertEq(Descriptor.feeToPercentString(1005000), "100.5%"); + assertEq(Descriptor.feeToPercentString(10000000), "1000%"); + assertEq(Descriptor.feeToPercentString(12300000), "1230%"); + } + + function test_addressToString_succeeds() public { + assertEq(Descriptor.addressToString(address(0)), "0x0000000000000000000000000000000000000000"); + assertEq(Descriptor.addressToString(address(1)), "0x0000000000000000000000000000000000000001"); + assertEq( + Descriptor.addressToString(0x1111111111111111111111111111111111111111), + "0x1111111111111111111111111111111111111111" + ); + assertEq( + Descriptor.addressToString(0x1234AbcdEf1234abcDef1234aBCdEF1234ABCDEF), + "0x1234abcdef1234abcdef1234abcdef1234abcdef" + ); + } + + function test_escapeQuotes_succeeds() public { + assertEq(Descriptor.escapeQuotes(""), ""); + assertEq(Descriptor.escapeQuotes("a"), "a"); + assertEq(Descriptor.escapeQuotes("abc"), "abc"); + assertEq(Descriptor.escapeQuotes("a\"bc"), "a\\\"bc"); + assertEq(Descriptor.escapeQuotes("a\"b\"c"), "a\\\"b\\\"c"); + assertEq(Descriptor.escapeQuotes("a\"b\"c\""), "a\\\"b\\\"c\\\""); + assertEq(Descriptor.escapeQuotes("\"a\"b\"c\""), "\\\"a\\\"b\\\"c\\\""); + assertEq(Descriptor.escapeQuotes("\"a\"b\"c\"\""), "\\\"a\\\"b\\\"c\\\"\\\""); + } + + function test_tickToDecimalString_withTickSpacing10() public { + int24 tickSpacing = 10; + int24 minTick = (TickMath.MIN_TICK / tickSpacing) * tickSpacing; + int24 maxTick = (TickMath.MAX_TICK / tickSpacing) * tickSpacing; + assertEq(Descriptor.tickToDecimalString(minTick, tickSpacing, 18, 18, false), "MIN"); + assertEq(Descriptor.tickToDecimalString(maxTick, tickSpacing, 18, 18, false), "MAX"); + assertEq(Descriptor.tickToDecimalString(1, tickSpacing, 18, 18, false), "1.0001"); + int24 otherMinTick = (TickMath.MIN_TICK / 60) * 60; + assertEq(Descriptor.tickToDecimalString(otherMinTick, tickSpacing, 18, 18, false), "0.0000000000000000000000000000000000000029387"); + } + + function test_tickToDecimalString_withTickSpacing60() public { + int24 tickSpacing = 60; + int24 minTick = (TickMath.MIN_TICK / tickSpacing) * tickSpacing; + int24 maxTick = (TickMath.MAX_TICK / tickSpacing) * tickSpacing; + assertEq(Descriptor.tickToDecimalString(minTick, tickSpacing, 18, 18, false), "MIN"); + assertEq(Descriptor.tickToDecimalString(maxTick, tickSpacing, 18, 18, false), "MAX"); + assertEq(Descriptor.tickToDecimalString(-1, tickSpacing, 18, 18, false), "0.99990"); + int24 otherMinTick = (TickMath.MIN_TICK / 200) * 200; + assertEq(Descriptor.tickToDecimalString(otherMinTick, tickSpacing, 18, 18, false), "0.0000000000000000000000000000000000000029387"); + } + + function test_tickToDecimalString_withTickSpacing200() public { + int24 tickSpacing = 200; + int24 minTick = (TickMath.MIN_TICK / tickSpacing) * tickSpacing; + int24 maxTick = (TickMath.MAX_TICK / tickSpacing) * tickSpacing; + assertEq(Descriptor.tickToDecimalString(minTick, tickSpacing, 18, 18, false), "MIN"); + assertEq(Descriptor.tickToDecimalString(maxTick, tickSpacing, 18, 18, false), "MAX"); + assertEq(Descriptor.tickToDecimalString(0, tickSpacing, 18, 18, false), "1.0000"); + int24 otherMinTick = (TickMath.MIN_TICK / 60) * 60; + assertEq(Descriptor.tickToDecimalString(otherMinTick, tickSpacing, 18, 18, false), "0.0000000000000000000000000000000000000029387"); + } + + function test_tickToDecimalString_ratio_returnsInverseMediumNumbers() public { + int24 tickSpacing = 200; + assertEq(Descriptor.tickToDecimalString(10, tickSpacing, 18, 18, false), "1.0010"); + assertEq(Descriptor.tickToDecimalString(10, tickSpacing, 18, 18, true), "0.99900"); + } + + function test_tickToDecimalString_ratio_returnsInverseLargeNumbers() public { + int24 tickSpacing = 200; + assertEq(Descriptor.tickToDecimalString(487272, tickSpacing, 18, 18, false), "1448400000000000000000"); + assertEq(Descriptor.tickToDecimalString(487272, tickSpacing, 18, 18, true), "0.00000000000000000000069041"); + } + + function test_tickToDecimalString_ratio_returnsInverseSmallNumbers() public { + int24 tickSpacing = 200; + assertEq(Descriptor.tickToDecimalString(-387272, tickSpacing, 18, 18, false), "0.000000000000000015200"); + assertEq(Descriptor.tickToDecimalString(-387272, tickSpacing, 18, 18, true), "65791000000000000"); + } + + function test_tickToDecimalString_differentDecimals() public { + int24 tickSpacing = 200; + assertEq(Descriptor.tickToDecimalString(1000, tickSpacing, 18, 18, true), "0.90484"); + assertEq(Descriptor.tickToDecimalString(1000, tickSpacing, 18, 10, true), "90484000"); + assertEq(Descriptor.tickToDecimalString(1000, tickSpacing, 10, 18, true), "0.0000000090484"); + } + +} From c423b33a00b77fb0a9df8347a56dcef3bdd37079 Mon Sep 17 00:00:00 2001 From: Sara Reynolds Date: Tue, 27 Aug 2024 20:37:26 -0400 Subject: [PATCH 02/45] messy checkpoint --- .../PositionManager_burn_empty.snap | 2 +- .../PositionManager_burn_empty_native.snap | 2 +- ...anager_burn_nonEmpty_native_withClose.snap | 2 +- ...ger_burn_nonEmpty_native_withTakePair.snap | 2 +- ...sitionManager_burn_nonEmpty_withClose.snap | 2 +- ...ionManager_burn_nonEmpty_withTakePair.snap | 2 +- .../PositionManager_collect_native.snap | 2 +- .../PositionManager_collect_sameRange.snap | 2 +- .../PositionManager_collect_withClose.snap | 2 +- .../PositionManager_collect_withTakePair.snap | 2 +- ...itionManager_decreaseLiquidity_native.snap | 2 +- ...onManager_decreaseLiquidity_withClose.snap | 2 +- ...anager_decreaseLiquidity_withTakePair.snap | 2 +- .../PositionManager_decrease_burnEmpty.snap | 2 +- ...tionManager_decrease_burnEmpty_native.snap | 2 +- ...nager_decrease_sameRange_allLiquidity.snap | 2 +- .../PositionManager_decrease_take_take.snap | 2 +- ...ger_increaseLiquidity_erc20_withClose.snap | 2 +- ...ncreaseLiquidity_erc20_withSettlePair.snap | 2 +- ...itionManager_increaseLiquidity_native.snap | 2 +- ...crease_autocompoundExactUnclaimedFees.snap | 2 +- ...increase_autocompoundExcessFeesCredit.snap | 2 +- ...ger_increase_autocompound_clearExcess.snap | 2 +- .../PositionManager_mint_native.snap | 2 +- ...anager_mint_nativeWithSweep_withClose.snap | 2 +- ...r_mint_nativeWithSweep_withSettlePair.snap | 2 +- .../PositionManager_mint_onSameTickLower.snap | 2 +- .../PositionManager_mint_onSameTickUpper.snap | 2 +- .../PositionManager_mint_sameRange.snap | 2 +- ...nManager_mint_settleWithBalance_sweep.snap | 2 +- ...anager_mint_warmedPool_differentRange.snap | 2 +- .../PositionManager_mint_withClose.snap | 2 +- .../PositionManager_mint_withSettlePair.snap | 2 +- ...tionManager_multicall_initialize_mint.snap | 2 +- src/PositionManager.sol | 96 +++++++++++-------- src/libraries/CalldataDecoder.sol | 37 +++---- src/libraries/PoolKeyChecker.sol | 12 +++ test/libraries/CalldataDecoder.t.sol | 20 +--- test/mocks/MockCalldataDecoder.sol | 17 +--- test/position-managers/Execute.t.sol | 14 ++- .../position-managers/IncreaseLiquidity.t.sol | 21 ++-- test/position-managers/NativeToken.t.sol | 21 ++-- .../PositionManager.gas.t.sol | 50 +++++----- .../PositionManager.multicall.t.sol | 4 +- .../PositionManager.notifier.t.sol | 6 +- test/position-managers/PositionManager.t.sol | 10 +- test/shared/LiquidityOperations.sol | 10 +- 47 files changed, 177 insertions(+), 209 deletions(-) create mode 100644 src/libraries/PoolKeyChecker.sol diff --git a/.forge-snapshots/PositionManager_burn_empty.snap b/.forge-snapshots/PositionManager_burn_empty.snap index 492cae309..056337b54 100644 --- a/.forge-snapshots/PositionManager_burn_empty.snap +++ b/.forge-snapshots/PositionManager_burn_empty.snap @@ -1 +1 @@ -47167 \ No newline at end of file +54314 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_burn_empty_native.snap b/.forge-snapshots/PositionManager_burn_empty_native.snap index 6ad16fc4b..056337b54 100644 --- a/.forge-snapshots/PositionManager_burn_empty_native.snap +++ b/.forge-snapshots/PositionManager_burn_empty_native.snap @@ -1 +1 @@ -46984 \ No newline at end of file +54314 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_burn_nonEmpty_native_withClose.snap b/.forge-snapshots/PositionManager_burn_nonEmpty_native_withClose.snap index 4cbd25401..5e29bfc52 100644 --- a/.forge-snapshots/PositionManager_burn_nonEmpty_native_withClose.snap +++ b/.forge-snapshots/PositionManager_burn_nonEmpty_native_withClose.snap @@ -1 +1 @@ -123815 \ No newline at end of file +130590 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_burn_nonEmpty_native_withTakePair.snap b/.forge-snapshots/PositionManager_burn_nonEmpty_native_withTakePair.snap index 42940dd01..81735e9a1 100644 --- a/.forge-snapshots/PositionManager_burn_nonEmpty_native_withTakePair.snap +++ b/.forge-snapshots/PositionManager_burn_nonEmpty_native_withTakePair.snap @@ -1 +1 @@ -123322 \ No newline at end of file +130088 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_burn_nonEmpty_withClose.snap b/.forge-snapshots/PositionManager_burn_nonEmpty_withClose.snap index de0d9eda3..a8c93a104 100644 --- a/.forge-snapshots/PositionManager_burn_nonEmpty_withClose.snap +++ b/.forge-snapshots/PositionManager_burn_nonEmpty_withClose.snap @@ -1 +1 @@ -130893 \ No newline at end of file +137486 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_burn_nonEmpty_withTakePair.snap b/.forge-snapshots/PositionManager_burn_nonEmpty_withTakePair.snap index 5ffb63a66..0577ef974 100644 --- a/.forge-snapshots/PositionManager_burn_nonEmpty_withTakePair.snap +++ b/.forge-snapshots/PositionManager_burn_nonEmpty_withTakePair.snap @@ -1 +1 @@ -130400 \ No newline at end of file +136984 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_collect_native.snap b/.forge-snapshots/PositionManager_collect_native.snap index 4d3da98ca..0f476515b 100644 --- a/.forge-snapshots/PositionManager_collect_native.snap +++ b/.forge-snapshots/PositionManager_collect_native.snap @@ -1 +1 @@ -142433 \ No newline at end of file +148872 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_collect_sameRange.snap b/.forge-snapshots/PositionManager_collect_sameRange.snap index e65741c1f..60029dc33 100644 --- a/.forge-snapshots/PositionManager_collect_sameRange.snap +++ b/.forge-snapshots/PositionManager_collect_sameRange.snap @@ -1 +1 @@ -151281 \ No newline at end of file +157492 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_collect_withClose.snap b/.forge-snapshots/PositionManager_collect_withClose.snap index e65741c1f..60029dc33 100644 --- a/.forge-snapshots/PositionManager_collect_withClose.snap +++ b/.forge-snapshots/PositionManager_collect_withClose.snap @@ -1 +1 @@ -151281 \ No newline at end of file +157492 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_collect_withTakePair.snap b/.forge-snapshots/PositionManager_collect_withTakePair.snap index 8fb961f41..3d3b8e961 100644 --- a/.forge-snapshots/PositionManager_collect_withTakePair.snap +++ b/.forge-snapshots/PositionManager_collect_withTakePair.snap @@ -1 +1 @@ -150653 \ No newline at end of file +156876 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_decreaseLiquidity_native.snap b/.forge-snapshots/PositionManager_decreaseLiquidity_native.snap index 40772dc20..ad049b47d 100644 --- a/.forge-snapshots/PositionManager_decreaseLiquidity_native.snap +++ b/.forge-snapshots/PositionManager_decreaseLiquidity_native.snap @@ -1 +1 @@ -109421 \ No newline at end of file +114572 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_decreaseLiquidity_withClose.snap b/.forge-snapshots/PositionManager_decreaseLiquidity_withClose.snap index f7cbc3b60..b90b7b220 100644 --- a/.forge-snapshots/PositionManager_decreaseLiquidity_withClose.snap +++ b/.forge-snapshots/PositionManager_decreaseLiquidity_withClose.snap @@ -1 +1 @@ -116824 \ No newline at end of file +123035 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_decreaseLiquidity_withTakePair.snap b/.forge-snapshots/PositionManager_decreaseLiquidity_withTakePair.snap index 75b62f53c..3426f6af2 100644 --- a/.forge-snapshots/PositionManager_decreaseLiquidity_withTakePair.snap +++ b/.forge-snapshots/PositionManager_decreaseLiquidity_withTakePair.snap @@ -1 +1 @@ -116196 \ No newline at end of file +122419 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_decrease_burnEmpty.snap b/.forge-snapshots/PositionManager_decrease_burnEmpty.snap index 053a81869..551944bf9 100644 --- a/.forge-snapshots/PositionManager_decrease_burnEmpty.snap +++ b/.forge-snapshots/PositionManager_decrease_burnEmpty.snap @@ -1 +1 @@ -134968 \ No newline at end of file +140615 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_decrease_burnEmpty_native.snap b/.forge-snapshots/PositionManager_decrease_burnEmpty_native.snap index aec2b78e8..908d54aa1 100644 --- a/.forge-snapshots/PositionManager_decrease_burnEmpty_native.snap +++ b/.forge-snapshots/PositionManager_decrease_burnEmpty_native.snap @@ -1 +1 @@ -127708 \ No newline at end of file +133719 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_decrease_sameRange_allLiquidity.snap b/.forge-snapshots/PositionManager_decrease_sameRange_allLiquidity.snap index 05acb4ce7..9ec6ec94c 100644 --- a/.forge-snapshots/PositionManager_decrease_sameRange_allLiquidity.snap +++ b/.forge-snapshots/PositionManager_decrease_sameRange_allLiquidity.snap @@ -1 +1 @@ -129540 \ No newline at end of file +135751 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_decrease_take_take.snap b/.forge-snapshots/PositionManager_decrease_take_take.snap index 34d197ce2..0a8cf2a47 100644 --- a/.forge-snapshots/PositionManager_decrease_take_take.snap +++ b/.forge-snapshots/PositionManager_decrease_take_take.snap @@ -1 +1 @@ -117357 \ No newline at end of file +123568 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_increaseLiquidity_erc20_withClose.snap b/.forge-snapshots/PositionManager_increaseLiquidity_erc20_withClose.snap index 97c4af609..22ea7d07f 100644 --- a/.forge-snapshots/PositionManager_increaseLiquidity_erc20_withClose.snap +++ b/.forge-snapshots/PositionManager_increaseLiquidity_erc20_withClose.snap @@ -1 +1 @@ -155531 \ No newline at end of file +161742 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_increaseLiquidity_erc20_withSettlePair.snap b/.forge-snapshots/PositionManager_increaseLiquidity_erc20_withSettlePair.snap index db4bd78fa..f8883bb68 100644 --- a/.forge-snapshots/PositionManager_increaseLiquidity_erc20_withSettlePair.snap +++ b/.forge-snapshots/PositionManager_increaseLiquidity_erc20_withSettlePair.snap @@ -1 +1 @@ -154533 \ No newline at end of file +160768 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_increaseLiquidity_native.snap b/.forge-snapshots/PositionManager_increaseLiquidity_native.snap index 6058eccca..75cf7e513 100644 --- a/.forge-snapshots/PositionManager_increaseLiquidity_native.snap +++ b/.forge-snapshots/PositionManager_increaseLiquidity_native.snap @@ -1 +1 @@ -137331 \ No newline at end of file +143770 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_increase_autocompoundExactUnclaimedFees.snap b/.forge-snapshots/PositionManager_increase_autocompoundExactUnclaimedFees.snap index 09f17e7ee..b68cadf46 100644 --- a/.forge-snapshots/PositionManager_increase_autocompoundExactUnclaimedFees.snap +++ b/.forge-snapshots/PositionManager_increase_autocompoundExactUnclaimedFees.snap @@ -1 +1 @@ -133676 \ No newline at end of file +139887 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_increase_autocompoundExcessFeesCredit.snap b/.forge-snapshots/PositionManager_increase_autocompoundExcessFeesCredit.snap index 5d94cc32a..a33d558dd 100644 --- a/.forge-snapshots/PositionManager_increase_autocompoundExcessFeesCredit.snap +++ b/.forge-snapshots/PositionManager_increase_autocompoundExcessFeesCredit.snap @@ -1 +1 @@ -174592 \ No newline at end of file +180803 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_increase_autocompound_clearExcess.snap b/.forge-snapshots/PositionManager_increase_autocompound_clearExcess.snap index 1ba47495a..8943df7ea 100644 --- a/.forge-snapshots/PositionManager_increase_autocompound_clearExcess.snap +++ b/.forge-snapshots/PositionManager_increase_autocompound_clearExcess.snap @@ -1 +1 @@ -144548 \ No newline at end of file +150759 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_mint_native.snap b/.forge-snapshots/PositionManager_mint_native.snap index 35d11906e..1c41e3aa3 100644 --- a/.forge-snapshots/PositionManager_mint_native.snap +++ b/.forge-snapshots/PositionManager_mint_native.snap @@ -1 +1 @@ -341353 \ No newline at end of file +393206 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_mint_nativeWithSweep_withClose.snap b/.forge-snapshots/PositionManager_mint_nativeWithSweep_withClose.snap index b50b4e031..1b4b4f494 100644 --- a/.forge-snapshots/PositionManager_mint_nativeWithSweep_withClose.snap +++ b/.forge-snapshots/PositionManager_mint_nativeWithSweep_withClose.snap @@ -1 +1 @@ -349845 \ No newline at end of file +401698 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_mint_nativeWithSweep_withSettlePair.snap b/.forge-snapshots/PositionManager_mint_nativeWithSweep_withSettlePair.snap index 989fe3283..145d441c0 100644 --- a/.forge-snapshots/PositionManager_mint_nativeWithSweep_withSettlePair.snap +++ b/.forge-snapshots/PositionManager_mint_nativeWithSweep_withSettlePair.snap @@ -1 +1 @@ -349147 \ No newline at end of file +401000 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_mint_onSameTickLower.snap b/.forge-snapshots/PositionManager_mint_onSameTickLower.snap index 86727b2b3..d22d17a60 100644 --- a/.forge-snapshots/PositionManager_mint_onSameTickLower.snap +++ b/.forge-snapshots/PositionManager_mint_onSameTickLower.snap @@ -1 +1 @@ -319335 \ No newline at end of file +349839 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_mint_onSameTickUpper.snap b/.forge-snapshots/PositionManager_mint_onSameTickUpper.snap index efd69c9f9..a5e34f74d 100644 --- a/.forge-snapshots/PositionManager_mint_onSameTickUpper.snap +++ b/.forge-snapshots/PositionManager_mint_onSameTickUpper.snap @@ -1 +1 @@ -319977 \ No newline at end of file +350481 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_mint_sameRange.snap b/.forge-snapshots/PositionManager_mint_sameRange.snap index 57a5ab280..25cf02cd8 100644 --- a/.forge-snapshots/PositionManager_mint_sameRange.snap +++ b/.forge-snapshots/PositionManager_mint_sameRange.snap @@ -1 +1 @@ -245559 \ No newline at end of file +276063 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_mint_settleWithBalance_sweep.snap b/.forge-snapshots/PositionManager_mint_settleWithBalance_sweep.snap index 85d82e1f7..73fdf7206 100644 --- a/.forge-snapshots/PositionManager_mint_settleWithBalance_sweep.snap +++ b/.forge-snapshots/PositionManager_mint_settleWithBalance_sweep.snap @@ -1 +1 @@ -375377 \ No newline at end of file +447136 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_mint_warmedPool_differentRange.snap b/.forge-snapshots/PositionManager_mint_warmedPool_differentRange.snap index 79911f01a..c32356bc0 100644 --- a/.forge-snapshots/PositionManager_mint_warmedPool_differentRange.snap +++ b/.forge-snapshots/PositionManager_mint_warmedPool_differentRange.snap @@ -1 +1 @@ -325353 \ No newline at end of file +355857 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_mint_withClose.snap b/.forge-snapshots/PositionManager_mint_withClose.snap index 487cdc461..7948b03a5 100644 --- a/.forge-snapshots/PositionManager_mint_withClose.snap +++ b/.forge-snapshots/PositionManager_mint_withClose.snap @@ -1 +1 @@ -376653 \ No newline at end of file +448406 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_mint_withSettlePair.snap b/.forge-snapshots/PositionManager_mint_withSettlePair.snap index be6c44420..2ec2b4c2b 100644 --- a/.forge-snapshots/PositionManager_mint_withSettlePair.snap +++ b/.forge-snapshots/PositionManager_mint_withSettlePair.snap @@ -1 +1 @@ -375793 \ No newline at end of file +447546 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_multicall_initialize_mint.snap b/.forge-snapshots/PositionManager_multicall_initialize_mint.snap index e856a5c2f..e0b4353fe 100644 --- a/.forge-snapshots/PositionManager_multicall_initialize_mint.snap +++ b/.forge-snapshots/PositionManager_multicall_initialize_mint.snap @@ -1 +1 @@ -421127 \ No newline at end of file +492874 \ No newline at end of file diff --git a/src/PositionManager.sol b/src/PositionManager.sol index dfedc38c8..6feeab622 100644 --- a/src/PositionManager.sol +++ b/src/PositionManager.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.24; 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 {PoolId, PoolIdLibrary} from "@uniswap/v4-core/src/types/PoolId.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"; @@ -29,6 +29,7 @@ import {INotifier} from "./interfaces/INotifier.sol"; import {Permit2Forwarder} from "./base/Permit2Forwarder.sol"; import {SlippageCheckLibrary} from "./libraries/SlippageCheck.sol"; import {PositionConfigId, PositionConfigIdLibrary} from "./libraries/PositionConfigId.sol"; +import {PoolKeyChecker} from "./libraries/PoolKeyChecker.sol"; // 444444444 // 444444444444 444444 @@ -118,10 +119,23 @@ contract PositionManager is using CalldataDecoder for bytes; using SlippageCheckLibrary for BalanceDelta; using PositionConfigIdLibrary for PositionConfigId; + using PoolKeyChecker for PoolKey; /// @dev The ID of the next token that will be minted. Skips 0 uint256 public nextTokenId = 1; + // TODO: Move to custom type so this is packed in memory + struct PositionInfo { + // lower 25 bytes of the poolId + bytes25 poolId; + int24 tickLower; + int24 tickUpper; + bool hasSubscriber; + } + + mapping(uint256 tokenId => PositionInfo info) internal positionInfo; + mapping(bytes32 poolId => PoolKey poolKey) internal poolKeys; + mapping(uint256 tokenId => PositionConfigId configId) internal positionConfigs; /// @notice an internal getter for PositionConfigId to be used by Notifier @@ -186,25 +200,13 @@ contract PositionManager is function _handleAction(uint256 action, bytes calldata params) internal virtual override { if (action < Actions.SETTLE) { if (action == Actions.INCREASE_LIQUIDITY) { - ( - uint256 tokenId, - PositionConfig calldata config, - uint256 liquidity, - uint128 amount0Max, - uint128 amount1Max, - bytes calldata hookData - ) = params.decodeModifyLiquidityParams(); - _increase(tokenId, config, liquidity, amount0Max, amount1Max, hookData); + (uint256 tokenId, uint256 liquidity, uint128 amount0Max, uint128 amount1Max, bytes calldata hookData) = + params.decodeModifyLiquidityParams(); + _increase(tokenId, liquidity, amount0Max, amount1Max, hookData); } else if (action == Actions.DECREASE_LIQUIDITY) { - ( - uint256 tokenId, - PositionConfig calldata config, - uint256 liquidity, - uint128 amount0Min, - uint128 amount1Min, - bytes calldata hookData - ) = params.decodeModifyLiquidityParams(); - _decrease(tokenId, config, liquidity, amount0Min, amount1Min, hookData); + (uint256 tokenId, uint256 liquidity, uint128 amount0Min, uint128 amount1Min, bytes calldata hookData) = + params.decodeModifyLiquidityParams(); + _decrease(tokenId, liquidity, amount0Min, amount1Min, hookData); } else if (action == Actions.MINT_POSITION) { ( PositionConfig calldata config, @@ -217,14 +219,9 @@ contract PositionManager is _mint(config, liquidity, amount0Max, amount1Max, _mapRecipient(owner), hookData); } else if (action == Actions.BURN_POSITION) { // Will automatically decrease liquidity to 0 if the position is not already empty. - ( - uint256 tokenId, - PositionConfig calldata config, - uint128 amount0Min, - uint128 amount1Min, - bytes calldata hookData - ) = params.decodeBurnParams(); - _burn(tokenId, config, amount0Min, amount1Min, hookData); + (uint256 tokenId, uint128 amount0Min, uint128 amount1Min, bytes calldata hookData) = + params.decodeBurnParams(); + _burn(tokenId, amount0Min, amount1Min, hookData); } else { revert UnsupportedAction(action); } @@ -259,12 +256,13 @@ contract PositionManager is /// @dev Calling increase with 0 liquidity will credit the caller with any underlying fees of the position function _increase( uint256 tokenId, - PositionConfig calldata config, uint256 liquidity, uint128 amount0Max, uint128 amount1Max, bytes calldata hookData - ) internal onlyIfApproved(msgSender(), tokenId) onlyValidConfig(tokenId, config) { + ) internal onlyIfApproved(msgSender(), tokenId) { + PositionInfo memory info = positionInfo[tokenId]; + PositionConfig memory config = _toConfig(info); // Note: The tokenId is used as the salt for this position, so every minted position has unique storage in the pool manager. (BalanceDelta liquidityDelta, BalanceDelta feesAccrued) = _modifyLiquidity(config, liquidity.toInt256(), bytes32(tokenId), hookData); @@ -275,12 +273,13 @@ contract PositionManager is /// @dev Calling decrease with 0 liquidity will credit the caller with any underlying fees of the position function _decrease( uint256 tokenId, - PositionConfig calldata config, uint256 liquidity, uint128 amount0Min, uint128 amount1Min, bytes calldata hookData - ) internal onlyIfApproved(msgSender(), tokenId) onlyValidConfig(tokenId, config) { + ) internal onlyIfApproved(msgSender(), tokenId) { + PositionInfo memory info = positionInfo[tokenId]; + PositionConfig memory config = _toConfig(info); // Note: the tokenId is used as the salt. (BalanceDelta liquidityDelta, BalanceDelta feesAccrued) = _modifyLiquidity(config, -(liquidity.toInt256()), bytes32(tokenId), hookData); @@ -311,17 +310,29 @@ contract PositionManager is (liquidityDelta - feesAccrued).validateMaxIn(amount0Max, amount1Max); positionConfigs[tokenId].setConfigId(config.toId()); + PositionInfo memory info = PositionInfo({ + poolId: bytes25(PoolId.unwrap(config.poolKey.toId())), + tickLower: config.tickLower, + tickUpper: config.tickUpper, + hasSubscriber: false + }); + positionInfo[tokenId] = info; + + if (poolKeys[info.poolId].isEmpty()) { + poolKeys[info.poolId] = config.poolKey; + } + emit MintPosition(tokenId, config); } /// @dev this is overloaded with ERC721Permit_v4._burn - function _burn( - uint256 tokenId, - PositionConfig calldata config, - uint128 amount0Min, - uint128 amount1Min, - bytes calldata hookData - ) internal onlyIfApproved(msgSender(), tokenId) onlyValidConfig(tokenId, config) { + function _burn(uint256 tokenId, uint128 amount0Min, uint128 amount1Min, bytes calldata hookData) + internal + onlyIfApproved(msgSender(), tokenId) + { + PositionInfo memory info = positionInfo[tokenId]; + PositionConfig memory config = _toConfig(info); + uint256 liquidity = uint256(getPositionLiquidity(tokenId, config)); // Can only call modify if there is non zero liquidity. @@ -333,6 +344,7 @@ contract PositionManager is } delete positionConfigs[tokenId]; + delete positionInfo[tokenId]; // Burn the token. _burn(tokenId); } @@ -384,7 +396,7 @@ contract PositionManager is } function _modifyLiquidity( - PositionConfig calldata config, + PositionConfig memory config, int256 liquidityChange, bytes32 salt, bytes calldata hookData @@ -422,7 +434,7 @@ contract PositionManager is } /// @inheritdoc IPositionManager - function getPositionLiquidity(uint256 tokenId, PositionConfig calldata config) + function getPositionLiquidity(uint256 tokenId, PositionConfig memory config) public view returns (uint128 liquidity) @@ -432,6 +444,10 @@ contract PositionManager is liquidity = poolManager.getPositionLiquidity(config.poolKey.toId(), positionId); } + function _toConfig(PositionInfo memory info) internal view returns (PositionConfig memory config) { + return PositionConfig({poolKey: poolKeys[info.poolId], tickLower: info.tickLower, tickUpper: info.tickUpper}); + } + /// @inheritdoc IPositionManager function getPositionConfigId(uint256 tokenId) external view returns (bytes32) { return positionConfigs[tokenId].getConfigId(); diff --git a/src/libraries/CalldataDecoder.sol b/src/libraries/CalldataDecoder.sol index 197987d38..0f032a344 100644 --- a/src/libraries/CalldataDecoder.sol +++ b/src/libraries/CalldataDecoder.sol @@ -48,23 +48,16 @@ library CalldataDecoder { function decodeModifyLiquidityParams(bytes calldata params) internal pure - returns ( - uint256 tokenId, - PositionConfig calldata config, - uint256 liquidity, - uint128 amount0, - uint128 amount1, - bytes calldata hookData - ) + returns (uint256 tokenId, uint256 liquidity, uint128 amount0, uint128 amount1, bytes calldata hookData) { assembly ("memory-safe") { tokenId := calldataload(params.offset) - config := add(params.offset, 0x20) - liquidity := calldataload(add(params.offset, 0x100)) - amount0 := calldataload(add(params.offset, 0x120)) - amount1 := calldataload(add(params.offset, 0x140)) + liquidity := calldataload(add(params.offset, 0x20)) + amount0 := calldataload(add(params.offset, 0x40)) + amount1 := calldataload(add(params.offset, 0x60)) } - hookData = params.toBytes(11); + + hookData = params.toBytes(4); } /// @dev equivalent to: abi.decode(params, (PositionConfig, uint256, uint128, uint128, address, bytes)) in calldata @@ -90,25 +83,19 @@ library CalldataDecoder { hookData = params.toBytes(11); } - /// @dev equivalent to: abi.decode(params, (uint256, PositionConfig, uint128, uint128, bytes)) in calldata + /// @dev equivalent to: abi.decode(params, (uint256, uint128, uint128, bytes)) in calldata function decodeBurnParams(bytes calldata params) internal pure - returns ( - uint256 tokenId, - PositionConfig calldata config, - uint128 amount0Min, - uint128 amount1Min, - bytes calldata hookData - ) + returns (uint256 tokenId, uint128 amount0Min, uint128 amount1Min, bytes calldata hookData) { assembly ("memory-safe") { tokenId := calldataload(params.offset) - config := add(params.offset, 0x20) - amount0Min := calldataload(add(params.offset, 0x100)) - amount1Min := calldataload(add(params.offset, 0x120)) + amount0Min := calldataload(add(params.offset, 0x20)) + amount1Min := calldataload(add(params.offset, 0x40)) } - hookData = params.toBytes(10); + + hookData = params.toBytes(3); } /// @dev equivalent to: abi.decode(params, (IV4Router.ExactInputParams)) diff --git a/src/libraries/PoolKeyChecker.sol b/src/libraries/PoolKeyChecker.sol new file mode 100644 index 000000000..949230bf3 --- /dev/null +++ b/src/libraries/PoolKeyChecker.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.24; + +import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol"; + +library PoolKeyChecker { + // On UniswapV4, the minimum tick spacing is 1. + // This means that if the tick spacing is 0, the pool key has not been set. + function isEmpty(PoolKey memory poolKey) internal pure returns (bool) { + return poolKey.tickSpacing == 0; + } +} diff --git a/test/libraries/CalldataDecoder.t.sol b/test/libraries/CalldataDecoder.t.sol index 293986974..bbf544514 100644 --- a/test/libraries/CalldataDecoder.t.sol +++ b/test/libraries/CalldataDecoder.t.sol @@ -19,44 +19,34 @@ contract CalldataDecoderTest is Test { function test_fuzz_decodeModifyLiquidityParams( uint256 _tokenId, - PositionConfig calldata _config, uint256 _liquidity, uint128 _amount0, uint128 _amount1, bytes calldata _hookData ) public view { - bytes memory params = abi.encode(_tokenId, _config, _liquidity, _amount0, _amount1, _hookData); - ( - uint256 tokenId, - PositionConfig memory config, - uint256 liquidity, - uint128 amount0, - uint128 amount1, - bytes memory hookData - ) = decoder.decodeModifyLiquidityParams(params); + bytes memory params = abi.encode(_tokenId, _liquidity, _amount0, _amount1, _hookData); + (uint256 tokenId, uint256 liquidity, uint128 amount0, uint128 amount1, bytes memory hookData) = + decoder.decodeModifyLiquidityParams(params); assertEq(tokenId, _tokenId); assertEq(liquidity, _liquidity); assertEq(amount0, _amount0); assertEq(amount1, _amount1); assertEq(hookData, _hookData); - _assertEq(_config, config); } function test_fuzz_decodeBurnParams( uint256 _tokenId, - PositionConfig calldata _config, uint128 _amount0Min, uint128 _amount1Min, bytes calldata _hookData ) public view { - bytes memory params = abi.encode(_tokenId, _config, _amount0Min, _amount1Min, _hookData); - (uint256 tokenId, PositionConfig memory config, uint128 amount0Min, uint128 amount1Min, bytes memory hookData) = + bytes memory params = abi.encode(_tokenId, _amount0Min, _amount1Min, _hookData); + (uint256 tokenId, uint128 amount0Min, uint128 amount1Min, bytes memory hookData) = decoder.decodeBurnParams(params); assertEq(tokenId, _tokenId); assertEq(hookData, _hookData); - _assertEq(_config, config); assertEq(amount0Min, _amount0Min); assertEq(amount1Min, _amount1Min); } diff --git a/test/mocks/MockCalldataDecoder.sol b/test/mocks/MockCalldataDecoder.sol index 695a526b2..7af17f698 100644 --- a/test/mocks/MockCalldataDecoder.sol +++ b/test/mocks/MockCalldataDecoder.sol @@ -13,14 +13,7 @@ contract MockCalldataDecoder { function decodeModifyLiquidityParams(bytes calldata params) external pure - returns ( - uint256 tokenId, - PositionConfig calldata config, - uint256 liquidity, - uint128 amount0, - uint128 amount1, - bytes calldata hookData - ) + returns (uint256 tokenId, uint256 liquidity, uint128 amount0, uint128 amount1, bytes calldata hookData) { return params.decodeModifyLiquidityParams(); } @@ -28,13 +21,7 @@ contract MockCalldataDecoder { function decodeBurnParams(bytes calldata params) external pure - returns ( - uint256 tokenId, - PositionConfig calldata config, - uint128 amount0Min, - uint128 amount1Min, - bytes calldata hookData - ) + returns (uint256 tokenId, uint128 amount0Min, uint128 amount1Min, bytes calldata hookData) { return params.decodeBurnParams(); } diff --git a/test/position-managers/Execute.t.sol b/test/position-managers/Execute.t.sol index 5417c71a1..09e843042 100644 --- a/test/position-managers/Execute.t.sol +++ b/test/position-managers/Execute.t.sol @@ -92,11 +92,11 @@ contract ExecuteTest is Test, PosmTestSetup, LiquidityFuzzers { planner.add( Actions.INCREASE_LIQUIDITY, - abi.encode(tokenId, config, liquidityToAdd, MAX_SLIPPAGE_INCREASE, MAX_SLIPPAGE_INCREASE, ZERO_BYTES) + abi.encode(tokenId, liquidityToAdd, MAX_SLIPPAGE_INCREASE, MAX_SLIPPAGE_INCREASE, ZERO_BYTES) ); planner.add( Actions.INCREASE_LIQUIDITY, - abi.encode(tokenId, config, liquidityToAdd2, MAX_SLIPPAGE_INCREASE, MAX_SLIPPAGE_INCREASE, ZERO_BYTES) + abi.encode(tokenId, liquidityToAdd2, MAX_SLIPPAGE_INCREASE, MAX_SLIPPAGE_INCREASE, ZERO_BYTES) ); bytes memory calls = planner.finalizeModifyLiquidityWithClose(config.poolKey); @@ -122,11 +122,11 @@ contract ExecuteTest is Test, PosmTestSetup, LiquidityFuzzers { planner.add( Actions.INCREASE_LIQUIDITY, - abi.encode(tokenId, config, liquidityToAdd, MAX_SLIPPAGE_INCREASE, MAX_SLIPPAGE_INCREASE, ZERO_BYTES) + abi.encode(tokenId, liquidityToAdd, MAX_SLIPPAGE_INCREASE, MAX_SLIPPAGE_INCREASE, ZERO_BYTES) ); planner.add( Actions.INCREASE_LIQUIDITY, - abi.encode(tokenId, config, liquidityToAdd2, MAX_SLIPPAGE_INCREASE, MAX_SLIPPAGE_INCREASE, ZERO_BYTES) + abi.encode(tokenId, liquidityToAdd2, MAX_SLIPPAGE_INCREASE, MAX_SLIPPAGE_INCREASE, ZERO_BYTES) ); bytes memory calls = planner.finalizeModifyLiquidityWithSettlePair(config.poolKey); @@ -159,7 +159,7 @@ contract ExecuteTest is Test, PosmTestSetup, LiquidityFuzzers { ); planner.add( Actions.INCREASE_LIQUIDITY, - abi.encode(tokenId, config, liquidityToAdd, MAX_SLIPPAGE_INCREASE, MAX_SLIPPAGE_INCREASE, ZERO_BYTES) + abi.encode(tokenId, liquidityToAdd, MAX_SLIPPAGE_INCREASE, MAX_SLIPPAGE_INCREASE, ZERO_BYTES) ); bytes memory calls = planner.finalizeModifyLiquidityWithClose(config.poolKey); @@ -197,9 +197,7 @@ contract ExecuteTest is Test, PosmTestSetup, LiquidityFuzzers { Plan memory planner = Planner.init(); planner.add( Actions.BURN_POSITION, - abi.encode( - tokenId, config, uint128(-delta.amount0()) - 1 wei, uint128(-delta.amount1()) - 1 wei, ZERO_BYTES - ) + abi.encode(tokenId, uint128(-delta.amount0()) - 1 wei, uint128(-delta.amount1()) - 1 wei, ZERO_BYTES) ); planner.add( Actions.MINT_POSITION, diff --git a/test/position-managers/IncreaseLiquidity.t.sol b/test/position-managers/IncreaseLiquidity.t.sol index 1feef6bc1..1a47ee724 100644 --- a/test/position-managers/IncreaseLiquidity.t.sol +++ b/test/position-managers/IncreaseLiquidity.t.sol @@ -122,12 +122,7 @@ contract IncreaseLiquidityTest is Test, PosmTestSetup, Fuzzers { planner.add( Actions.INCREASE_LIQUIDITY, abi.encode( - tokenIdAlice, - config, - liquidityDelta, - feesOwedAlice.amount0() / 2, - feesOwedAlice.amount1() / 2, - ZERO_BYTES + tokenIdAlice, liquidityDelta, feesOwedAlice.amount0() / 2, feesOwedAlice.amount1() / 2, ZERO_BYTES ) ); bytes memory calls = planner.finalizeModifyLiquidityWithTakePair(config.poolKey, address(alice)); @@ -189,9 +184,7 @@ contract IncreaseLiquidityTest is Test, PosmTestSetup, Fuzzers { Plan memory planner = Planner.init(); planner.add( Actions.INCREASE_LIQUIDITY, - abi.encode( - tokenIdAlice, config, liquidityDelta, feesOwedAlice.amount0(), feesOwedAlice.amount1(), ZERO_BYTES - ) + abi.encode(tokenIdAlice, liquidityDelta, feesOwedAlice.amount0(), feesOwedAlice.amount1(), ZERO_BYTES) ); bytes memory calls = planner.finalizeModifyLiquidityWithClose(config.poolKey); vm.startPrank(alice); @@ -248,7 +241,7 @@ contract IncreaseLiquidityTest is Test, PosmTestSetup, Fuzzers { Plan memory planner = Planner.init(); planner.add( Actions.INCREASE_LIQUIDITY, - abi.encode(tokenIdAlice, config, liquidityDelta, MAX_SLIPPAGE_INCREASE, MAX_SLIPPAGE_INCREASE, ZERO_BYTES) + abi.encode(tokenIdAlice, liquidityDelta, MAX_SLIPPAGE_INCREASE, MAX_SLIPPAGE_INCREASE, ZERO_BYTES) ); planner.add(Actions.CLEAR_OR_TAKE, abi.encode(config.poolKey.currency0, 18 wei)); // alice is willing to forfeit 18 wei planner.add(Actions.CLEAR_OR_TAKE, abi.encode(config.poolKey.currency1, 18 wei)); @@ -351,7 +344,7 @@ contract IncreaseLiquidityTest is Test, PosmTestSetup, Fuzzers { Plan memory planner = Planner.init(); planner.add( Actions.INCREASE_LIQUIDITY, - abi.encode(tokenIdAlice, config, liquidityDelta, MAX_SLIPPAGE_INCREASE, MAX_SLIPPAGE_INCREASE, ZERO_BYTES) + abi.encode(tokenIdAlice, liquidityDelta, MAX_SLIPPAGE_INCREASE, MAX_SLIPPAGE_INCREASE, ZERO_BYTES) ); planner.add(Actions.CLEAR_OR_TAKE, abi.encode(config.poolKey.currency0, 1 wei)); // alice is willing to forfeit 1 wei planner.add(Actions.CLEAR_OR_TAKE, abi.encode(config.poolKey.currency1, 1 wei)); @@ -698,7 +691,7 @@ contract IncreaseLiquidityTest is Test, PosmTestSetup, Fuzzers { Plan memory planner = Planner.init(); planner.add( Actions.INCREASE_LIQUIDITY, - abi.encode(tokenIdAlice, config, liquidityAlice, MAX_SLIPPAGE_INCREASE, MAX_SLIPPAGE_INCREASE, ZERO_BYTES) + abi.encode(tokenIdAlice, liquidityAlice, MAX_SLIPPAGE_INCREASE, MAX_SLIPPAGE_INCREASE, ZERO_BYTES) ); planner.add(Actions.SETTLE, abi.encode(currency0, ActionConstants.OPEN_DELTA, false)); planner.add(Actions.SETTLE, abi.encode(currency1, ActionConstants.OPEN_DELTA, false)); @@ -763,7 +756,7 @@ contract IncreaseLiquidityTest is Test, PosmTestSetup, Fuzzers { Plan memory planner = Planner.init(); planner.add( Actions.INCREASE_LIQUIDITY, - abi.encode(tokenId, config, liquidityDelta, MAX_SLIPPAGE_INCREASE, MAX_SLIPPAGE_INCREASE, ZERO_BYTES) + abi.encode(tokenId, liquidityDelta, MAX_SLIPPAGE_INCREASE, MAX_SLIPPAGE_INCREASE, ZERO_BYTES) ); planner.add(Actions.CLEAR_OR_TAKE, abi.encode(config.poolKey.currency0, maxClear)); planner.add(Actions.CLEAR_OR_TAKE, abi.encode(config.poolKey.currency1, maxClear)); @@ -792,7 +785,7 @@ contract IncreaseLiquidityTest is Test, PosmTestSetup, Fuzzers { Plan memory planner = Planner.init(); planner.add( Actions.INCREASE_LIQUIDITY, - abi.encode(tokenId, config, 100e18, MAX_SLIPPAGE_INCREASE, MAX_SLIPPAGE_INCREASE, ZERO_BYTES) + abi.encode(tokenId, 100e18, MAX_SLIPPAGE_INCREASE, MAX_SLIPPAGE_INCREASE, ZERO_BYTES) ); planner.add(Actions.CLEAR_OR_TAKE, abi.encode(config.poolKey.currency0, type(uint256).max)); planner.add(Actions.CLEAR_OR_TAKE, abi.encode(config.poolKey.currency1, type(uint256).max)); diff --git a/test/position-managers/NativeToken.t.sol b/test/position-managers/NativeToken.t.sol index 662ddda15..26b65081f 100644 --- a/test/position-managers/NativeToken.t.sol +++ b/test/position-managers/NativeToken.t.sol @@ -278,7 +278,7 @@ contract PositionManagerTest is Test, PosmTestSetup, LiquidityFuzzers { uint256 numDeltas = hook.numberDeltasReturned(); Plan memory planner = Planner.init(); planner.add( - Actions.BURN_POSITION, abi.encode(tokenId, config, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES) + Actions.BURN_POSITION, abi.encode(tokenId, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES) ); bytes memory calls = planner.finalizeModifyLiquidityWithTakePair(config.poolKey, address(this)); lpm.modifyLiquidities(calls, _deadline); @@ -380,7 +380,7 @@ contract PositionManagerTest is Test, PosmTestSetup, LiquidityFuzzers { Plan memory planner = Planner.init(); planner.add( - Actions.BURN_POSITION, abi.encode(tokenId, config, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES) + Actions.BURN_POSITION, abi.encode(tokenId, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES) ); bytes memory calls = planner.finalizeModifyLiquidityWithTakePair(config.poolKey, address(this)); lpm.modifyLiquidities(calls, _deadline); @@ -477,7 +477,7 @@ contract PositionManagerTest is Test, PosmTestSetup, LiquidityFuzzers { Plan memory planner = Planner.init(); planner.add( Actions.INCREASE_LIQUIDITY, - abi.encode(tokenId, config, liquidityToAdd, MAX_SLIPPAGE_INCREASE, MAX_SLIPPAGE_INCREASE, ZERO_BYTES) + abi.encode(tokenId, liquidityToAdd, MAX_SLIPPAGE_INCREASE, MAX_SLIPPAGE_INCREASE, ZERO_BYTES) ); planner.add(Actions.CLOSE_CURRENCY, abi.encode(nativeKey.currency0)); planner.add(Actions.CLOSE_CURRENCY, abi.encode(nativeKey.currency1)); @@ -528,7 +528,7 @@ contract PositionManagerTest is Test, PosmTestSetup, LiquidityFuzzers { Plan memory planner = Planner.init(); planner.add( Actions.INCREASE_LIQUIDITY, - abi.encode(tokenId, config, liquidityToAdd, MAX_SLIPPAGE_INCREASE, MAX_SLIPPAGE_INCREASE, ZERO_BYTES) + abi.encode(tokenId, liquidityToAdd, MAX_SLIPPAGE_INCREASE, MAX_SLIPPAGE_INCREASE, ZERO_BYTES) ); planner.add(Actions.SETTLE_PAIR, abi.encode(nativeKey.currency0, nativeKey.currency1)); // sweep the excess eth @@ -613,9 +613,7 @@ contract PositionManagerTest is Test, PosmTestSetup, LiquidityFuzzers { Plan memory planner = Planner.init(); planner.add( Actions.DECREASE_LIQUIDITY, - abi.encode( - tokenId, config, decreaseLiquidityDelta, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES - ) + abi.encode(tokenId, decreaseLiquidityDelta, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES) ); bytes memory calls = planner.finalizeModifyLiquidityWithTakePair(config.poolKey, address(this)); lpm.modifyLiquidities(calls, _deadline); @@ -676,8 +674,7 @@ contract PositionManagerTest is Test, PosmTestSetup, LiquidityFuzzers { uint256 balance1Before = currency1.balanceOfSelf(); Plan memory planner = Planner.init(); planner.add( - Actions.DECREASE_LIQUIDITY, - abi.encode(tokenId, config, 0, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES) + Actions.DECREASE_LIQUIDITY, abi.encode(tokenId, 0, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES) ); bytes memory calls = planner.finalizeModifyLiquidityWithTakePair(config.poolKey, address(this)); lpm.modifyLiquidities(calls, _deadline); @@ -711,8 +708,7 @@ contract PositionManagerTest is Test, PosmTestSetup, LiquidityFuzzers { Plan memory planner = Planner.init(); planner.add( - Actions.DECREASE_LIQUIDITY, - abi.encode(tokenId, config, 0, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES) + Actions.DECREASE_LIQUIDITY, abi.encode(tokenId, 0, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES) ); address alice = address(0xABCD); @@ -755,8 +751,7 @@ contract PositionManagerTest is Test, PosmTestSetup, LiquidityFuzzers { Plan memory planner = Planner.init(); planner.add( - Actions.DECREASE_LIQUIDITY, - abi.encode(tokenId, config, 0, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES) + Actions.DECREASE_LIQUIDITY, abi.encode(tokenId, 0, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES) ); bytes memory calls = planner.finalizeModifyLiquidityWithTakePair(config.poolKey, ActionConstants.MSG_SENDER); diff --git a/test/position-managers/PositionManager.gas.t.sol b/test/position-managers/PositionManager.gas.t.sol index 161321eac..e0bdec40e 100644 --- a/test/position-managers/PositionManager.gas.t.sol +++ b/test/position-managers/PositionManager.gas.t.sol @@ -175,7 +175,7 @@ contract PosMGasTest is Test, PosmTestSetup, GasSnapshot { Plan memory planner = Planner.init().add( Actions.INCREASE_LIQUIDITY, - abi.encode(tokenId, config, 10_000 ether, MAX_SLIPPAGE_INCREASE, MAX_SLIPPAGE_INCREASE, ZERO_BYTES) + abi.encode(tokenId, 10_000 ether, MAX_SLIPPAGE_INCREASE, MAX_SLIPPAGE_INCREASE, ZERO_BYTES) ); bytes memory calls = planner.finalizeModifyLiquidityWithClose(config.poolKey); @@ -189,7 +189,7 @@ contract PosMGasTest is Test, PosmTestSetup, GasSnapshot { Plan memory planner = Planner.init().add( Actions.INCREASE_LIQUIDITY, - abi.encode(tokenId, config, 10_000 ether, MAX_SLIPPAGE_INCREASE, MAX_SLIPPAGE_INCREASE, ZERO_BYTES) + abi.encode(tokenId, 10_000 ether, MAX_SLIPPAGE_INCREASE, MAX_SLIPPAGE_INCREASE, ZERO_BYTES) ); bytes memory calls = planner.finalizeModifyLiquidityWithSettlePair(config.poolKey); @@ -233,7 +233,7 @@ contract PosMGasTest is Test, PosmTestSetup, GasSnapshot { Plan memory planner = Planner.init().add( Actions.INCREASE_LIQUIDITY, - abi.encode(tokenIdAlice, config, liquidityDelta, MAX_SLIPPAGE_INCREASE, MAX_SLIPPAGE_INCREASE, ZERO_BYTES) + abi.encode(tokenIdAlice, liquidityDelta, MAX_SLIPPAGE_INCREASE, MAX_SLIPPAGE_INCREASE, ZERO_BYTES) ); // because its a perfect autocompound, the delta is exactly 0 and we dont need to "close" deltas bytes memory calls = planner.encode(); @@ -281,7 +281,7 @@ contract PosMGasTest is Test, PosmTestSetup, GasSnapshot { Plan memory planner = Planner.init(); planner.add( Actions.INCREASE_LIQUIDITY, - abi.encode(tokenIdAlice, config, liquidityDelta, MAX_SLIPPAGE_INCREASE, MAX_SLIPPAGE_INCREASE, ZERO_BYTES) + abi.encode(tokenIdAlice, liquidityDelta, MAX_SLIPPAGE_INCREASE, MAX_SLIPPAGE_INCREASE, ZERO_BYTES) ); planner.add(Actions.CLEAR_OR_TAKE, abi.encode(config.poolKey.currency0, halfTokensOwedAlice + 1 wei)); planner.add(Actions.CLEAR_OR_TAKE, abi.encode(config.poolKey.currency1, halfTokensOwedAlice + 1 wei)); @@ -328,7 +328,7 @@ contract PosMGasTest is Test, PosmTestSetup, GasSnapshot { Plan memory planner = Planner.init().add( Actions.INCREASE_LIQUIDITY, - abi.encode(tokenIdAlice, config, liquidityDelta, MAX_SLIPPAGE_INCREASE, MAX_SLIPPAGE_INCREASE, ZERO_BYTES) + abi.encode(tokenIdAlice, liquidityDelta, MAX_SLIPPAGE_INCREASE, MAX_SLIPPAGE_INCREASE, ZERO_BYTES) ); bytes memory calls = planner.finalizeModifyLiquidityWithClose(config.poolKey); @@ -344,7 +344,7 @@ contract PosMGasTest is Test, PosmTestSetup, GasSnapshot { Plan memory planner = Planner.init().add( Actions.DECREASE_LIQUIDITY, - abi.encode(tokenId, config, 10_000 ether, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES) + abi.encode(tokenId, 10_000 ether, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES) ); bytes memory calls = planner.finalizeModifyLiquidityWithClose(config.poolKey); @@ -358,7 +358,7 @@ contract PosMGasTest is Test, PosmTestSetup, GasSnapshot { Plan memory planner = Planner.init().add( Actions.DECREASE_LIQUIDITY, - abi.encode(tokenId, config, 10_000 ether, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES) + abi.encode(tokenId, 10_000 ether, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES) ); bytes memory calls = planner.finalizeModifyLiquidityWithTakePair(config.poolKey, address(this)); @@ -403,8 +403,7 @@ contract PosMGasTest is Test, PosmTestSetup, GasSnapshot { // Collect by calling decrease with 0. Plan memory planner = Planner.init().add( - Actions.DECREASE_LIQUIDITY, - abi.encode(tokenId, config, 0, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES) + Actions.DECREASE_LIQUIDITY, abi.encode(tokenId, 0, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES) ); bytes memory calls = planner.finalizeModifyLiquidityWithClose(config.poolKey); @@ -421,8 +420,7 @@ contract PosMGasTest is Test, PosmTestSetup, GasSnapshot { // Collect by calling decrease with 0. Plan memory planner = Planner.init().add( - Actions.DECREASE_LIQUIDITY, - abi.encode(tokenId, config, 0, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES) + Actions.DECREASE_LIQUIDITY, abi.encode(tokenId, 0, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES) ); bytes memory calls = planner.finalizeModifyLiquidityWithTakePair(config.poolKey, address(this)); @@ -462,7 +460,7 @@ contract PosMGasTest is Test, PosmTestSetup, GasSnapshot { Plan memory planner = Planner.init().add( Actions.DECREASE_LIQUIDITY, - abi.encode(tokenId, config, 10_000 ether, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES) + abi.encode(tokenId, 10_000 ether, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES) ); bytes memory calls = planner.finalizeModifyLiquidityWithClose(config.poolKey); @@ -483,8 +481,7 @@ contract PosMGasTest is Test, PosmTestSetup, GasSnapshot { donateRouter.donate(config.poolKey, 0.2e18, 0.2e18, ZERO_BYTES); Plan memory planner = Planner.init().add( - Actions.DECREASE_LIQUIDITY, - abi.encode(tokenId, config, 0, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES) + Actions.DECREASE_LIQUIDITY, abi.encode(tokenId, 0, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES) ); bytes memory calls = planner.finalizeModifyLiquidityWithClose(config.poolKey); @@ -497,7 +494,7 @@ contract PosMGasTest is Test, PosmTestSetup, GasSnapshot { mint(config, 10_000 ether, ActionConstants.MSG_SENDER, ZERO_BYTES); Plan memory planner = Planner.init().add( - Actions.BURN_POSITION, abi.encode(tokenId, config, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES) + Actions.BURN_POSITION, abi.encode(tokenId, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES) ); bytes memory calls = planner.finalizeModifyLiquidityWithClose(config.poolKey); @@ -510,7 +507,7 @@ contract PosMGasTest is Test, PosmTestSetup, GasSnapshot { mint(config, 10_000 ether, ActionConstants.MSG_SENDER, ZERO_BYTES); Plan memory planner = Planner.init().add( - Actions.BURN_POSITION, abi.encode(tokenId, config, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES) + Actions.BURN_POSITION, abi.encode(tokenId, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES) ); bytes memory calls = planner.finalizeModifyLiquidityWithTakePair(config.poolKey, address(this)); @@ -524,7 +521,7 @@ contract PosMGasTest is Test, PosmTestSetup, GasSnapshot { decreaseLiquidity(tokenId, config, 10_000 ether, ZERO_BYTES); Plan memory planner = Planner.init().add( - Actions.BURN_POSITION, abi.encode(tokenId, config, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES) + Actions.BURN_POSITION, abi.encode(tokenId, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES) ); // There is no need to include CLOSE commands. @@ -541,10 +538,10 @@ contract PosMGasTest is Test, PosmTestSetup, GasSnapshot { Plan memory planner = Planner.init().add( Actions.DECREASE_LIQUIDITY, - abi.encode(tokenId, config, 10_000 ether, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES) + abi.encode(tokenId, 10_000 ether, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES) ); planner.add( - Actions.BURN_POSITION, abi.encode(tokenId, config, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES) + Actions.BURN_POSITION, abi.encode(tokenId, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES) ); // We must include CLOSE commands. @@ -671,8 +668,7 @@ contract PosMGasTest is Test, PosmTestSetup, GasSnapshot { mintWithNative(SQRT_PRICE_1_1, configNative, 10_000 ether, ActionConstants.MSG_SENDER, ZERO_BYTES); Plan memory planner = Planner.init().add( - Actions.BURN_POSITION, - abi.encode(tokenId, configNative, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES) + Actions.BURN_POSITION, abi.encode(tokenId, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES) ); bytes memory calls = planner.finalizeModifyLiquidityWithClose(configNative.poolKey); @@ -685,8 +681,7 @@ contract PosMGasTest is Test, PosmTestSetup, GasSnapshot { mintWithNative(SQRT_PRICE_1_1, configNative, 10_000 ether, ActionConstants.MSG_SENDER, ZERO_BYTES); Plan memory planner = Planner.init().add( - Actions.BURN_POSITION, - abi.encode(tokenId, configNative, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES) + Actions.BURN_POSITION, abi.encode(tokenId, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES) ); bytes memory calls = planner.finalizeModifyLiquidityWithTakePair(configNative.poolKey, address(this)); @@ -700,8 +695,7 @@ contract PosMGasTest is Test, PosmTestSetup, GasSnapshot { decreaseLiquidity(tokenId, configNative, 10_000 ether, ZERO_BYTES); Plan memory planner = Planner.init().add( - Actions.BURN_POSITION, - abi.encode(tokenId, configNative, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES) + Actions.BURN_POSITION, abi.encode(tokenId, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES) ); // There is no need to include CLOSE commands. @@ -718,9 +712,9 @@ contract PosMGasTest is Test, PosmTestSetup, GasSnapshot { Plan memory planner = Planner.init().add( Actions.DECREASE_LIQUIDITY, - abi.encode(tokenId, configNative, 10_000 ether, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES) + abi.encode(tokenId, 10_000 ether, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES) ); - planner.add(Actions.BURN_POSITION, abi.encode(tokenId, configNative, 0 wei, 0 wei, ZERO_BYTES)); + planner.add(Actions.BURN_POSITION, abi.encode(tokenId, 0 wei, 0 wei, ZERO_BYTES)); // We must include CLOSE commands. bytes memory calls = planner.finalizeModifyLiquidityWithClose(configNative.poolKey); @@ -843,7 +837,7 @@ contract PosMGasTest is Test, PosmTestSetup, GasSnapshot { Plan memory plan = Planner.init(); plan.add( Actions.DECREASE_LIQUIDITY, - abi.encode(tokenId, config, 1e18, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES) + abi.encode(tokenId, 1e18, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES) ); bytes memory calls = plan.finalizeModifyLiquidityWithTake(config.poolKey, ActionConstants.MSG_SENDER); diff --git a/test/position-managers/PositionManager.multicall.t.sol b/test/position-managers/PositionManager.multicall.t.sol index 4f0ef8fa9..b09921073 100644 --- a/test/position-managers/PositionManager.multicall.t.sol +++ b/test/position-managers/PositionManager.multicall.t.sol @@ -127,7 +127,7 @@ contract PositionManagerMulticallTest is Test, Permit2SignatureHelpers, PosmTest Plan memory planner = Planner.init(); planner.add( Actions.DECREASE_LIQUIDITY, - abi.encode(tokenId, config, 100e18, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES) + abi.encode(tokenId, 100e18, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES) ); bytes memory actions = planner.finalizeModifyLiquidityWithClose(config.poolKey); @@ -157,7 +157,7 @@ contract PositionManagerMulticallTest is Test, Permit2SignatureHelpers, PosmTest Plan memory planner = Planner.init(); planner.add( Actions.DECREASE_LIQUIDITY, - abi.encode(tokenId, config, 100e18, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES) + abi.encode(tokenId, 100e18, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES) ); bytes memory actions = planner.encode(); diff --git a/test/position-managers/PositionManager.notifier.t.sol b/test/position-managers/PositionManager.notifier.t.sol index 28f5813f4..2d8cf44e3 100644 --- a/test/position-managers/PositionManager.notifier.t.sol +++ b/test/position-managers/PositionManager.notifier.t.sol @@ -115,7 +115,7 @@ contract PositionManagerNotifierTest is Test, PosmTestSetup, GasSnapshot { for (uint256 i = 0; i < 10; i++) { plan.add( Actions.INCREASE_LIQUIDITY, - abi.encode(tokenId, config, 10e18, MAX_SLIPPAGE_INCREASE, MAX_SLIPPAGE_INCREASE, ZERO_BYTES) + abi.encode(tokenId, 10e18, MAX_SLIPPAGE_INCREASE, MAX_SLIPPAGE_INCREASE, ZERO_BYTES) ); } @@ -290,7 +290,7 @@ contract PositionManagerNotifierTest is Test, PosmTestSetup, GasSnapshot { plan = Planner.init(); plan.add( Actions.INCREASE_LIQUIDITY, - abi.encode(tokenId, config, 10e18, MAX_SLIPPAGE_INCREASE, MAX_SLIPPAGE_INCREASE, ZERO_BYTES) + abi.encode(tokenId, 10e18, MAX_SLIPPAGE_INCREASE, MAX_SLIPPAGE_INCREASE, ZERO_BYTES) ); bytes memory actions2 = plan.finalizeModifyLiquidityWithSettlePair(config.poolKey); @@ -400,7 +400,7 @@ contract PositionManagerNotifierTest is Test, PosmTestSetup, GasSnapshot { for (uint256 i = 0; i < 10; i++) { plan.add( Actions.INCREASE_LIQUIDITY, - abi.encode(tokenId, config, 10e18, MAX_SLIPPAGE_INCREASE, MAX_SLIPPAGE_INCREASE, ZERO_BYTES) + abi.encode(tokenId, 10e18, MAX_SLIPPAGE_INCREASE, MAX_SLIPPAGE_INCREASE, ZERO_BYTES) ); } diff --git a/test/position-managers/PositionManager.t.sol b/test/position-managers/PositionManager.t.sol index ab4be3915..0732212c7 100644 --- a/test/position-managers/PositionManager.t.sol +++ b/test/position-managers/PositionManager.t.sol @@ -504,9 +504,7 @@ contract PositionManagerTest is Test, PosmTestSetup, LiquidityFuzzers { Plan memory planner = Planner.init(); planner.add( Actions.DECREASE_LIQUIDITY, - abi.encode( - tokenId, config, decreaseLiquidityDelta, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES - ) + abi.encode(tokenId, decreaseLiquidityDelta, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES) ); planner.add(Actions.CLEAR_OR_TAKE, abi.encode(key.currency0, type(uint256).max)); planner.add(Actions.CLEAR_OR_TAKE, abi.encode(key.currency1, type(uint256).max)); @@ -548,7 +546,7 @@ contract PositionManagerTest is Test, PosmTestSetup, LiquidityFuzzers { Plan memory planner = Planner.init(); planner.add( Actions.DECREASE_LIQUIDITY, - abi.encode(tokenId, config, liquidityToRemove, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES) + abi.encode(tokenId, liquidityToRemove, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES) ); planner.add(Actions.CLEAR_OR_TAKE, abi.encode(key.currency0, amount0 - 1 wei)); planner.add(Actions.CLEAR_OR_TAKE, abi.encode(key.currency1, amount1 - 1 wei)); @@ -877,7 +875,7 @@ contract PositionManagerTest is Test, PosmTestSetup, LiquidityFuzzers { Plan memory plan = Planner.init(); plan.add( Actions.DECREASE_LIQUIDITY, - abi.encode(tokenId, config, 1e18, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES) + abi.encode(tokenId, 1e18, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES) ); bytes memory calls = plan.finalizeModifyLiquidityWithTake(config.poolKey, ActionConstants.MSG_SENDER); @@ -909,7 +907,7 @@ contract PositionManagerTest is Test, PosmTestSetup, LiquidityFuzzers { Plan memory plan = Planner.init(); plan.add( Actions.DECREASE_LIQUIDITY, - abi.encode(tokenId, config, 1e18, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES) + abi.encode(tokenId, 1e18, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES) ); plan.add( Actions.MINT_POSITION, diff --git a/test/shared/LiquidityOperations.sol b/test/shared/LiquidityOperations.sol index 65ed085d6..5490872af 100644 --- a/test/shared/LiquidityOperations.sol +++ b/test/shared/LiquidityOperations.sol @@ -124,9 +124,7 @@ abstract contract LiquidityOperations is CommonBase { bytes memory hookData ) internal pure returns (bytes memory) { Plan memory planner = Planner.init(); - planner.add( - Actions.INCREASE_LIQUIDITY, abi.encode(tokenId, config, liquidityToAdd, amount0Max, amount1Max, hookData) - ); + planner.add(Actions.INCREASE_LIQUIDITY, abi.encode(tokenId, liquidityToAdd, amount0Max, amount1Max, hookData)); return planner.finalizeModifyLiquidityWithClose(config.poolKey); } @@ -151,7 +149,7 @@ abstract contract LiquidityOperations is CommonBase { ) internal pure returns (bytes memory) { Plan memory planner = Planner.init(); planner.add( - Actions.DECREASE_LIQUIDITY, abi.encode(tokenId, config, liquidityToRemove, amount0Min, amount1Min, hookData) + Actions.DECREASE_LIQUIDITY, abi.encode(tokenId, liquidityToRemove, amount0Min, amount1Min, hookData) ); return planner.finalizeModifyLiquidityWithClose(config.poolKey); } @@ -172,7 +170,7 @@ abstract contract LiquidityOperations is CommonBase { bytes memory hookData ) internal pure returns (bytes memory) { Plan memory planner = Planner.init(); - planner.add(Actions.DECREASE_LIQUIDITY, abi.encode(tokenId, config, 0, amount0Min, amount1Min, hookData)); + planner.add(Actions.DECREASE_LIQUIDITY, abi.encode(tokenId, 0, amount0Min, amount1Min, hookData)); return planner.finalizeModifyLiquidityWithClose(config.poolKey); } @@ -192,7 +190,7 @@ abstract contract LiquidityOperations is CommonBase { bytes memory hookData ) internal pure returns (bytes memory) { Plan memory planner = Planner.init(); - planner.add(Actions.BURN_POSITION, abi.encode(tokenId, config, amount0Min, amount1Min, hookData)); + planner.add(Actions.BURN_POSITION, abi.encode(tokenId, amount0Min, amount1Min, hookData)); // Close needed on burn in case there is liquidity left in the position. return planner.finalizeModifyLiquidityWithClose(config.poolKey); } From e49738935bf1de69308bc88296491370a1914139 Mon Sep 17 00:00:00 2001 From: Sara Reynolds Date: Tue, 27 Aug 2024 22:14:35 -0400 Subject: [PATCH 03/45] update subscribers --- .../PositionManager_burn_empty.snap | 2 +- .../PositionManager_burn_empty_native.snap | 2 +- ...anager_burn_nonEmpty_native_withClose.snap | 2 +- ...ger_burn_nonEmpty_native_withTakePair.snap | 2 +- ...sitionManager_burn_nonEmpty_withClose.snap | 2 +- ...ionManager_burn_nonEmpty_withTakePair.snap | 2 +- .../PositionManager_collect_native.snap | 2 +- .../PositionManager_collect_sameRange.snap | 2 +- .../PositionManager_collect_withClose.snap | 2 +- .../PositionManager_collect_withTakePair.snap | 2 +- ...itionManager_decreaseLiquidity_native.snap | 2 +- ...onManager_decreaseLiquidity_withClose.snap | 2 +- ...anager_decreaseLiquidity_withTakePair.snap | 2 +- .../PositionManager_decrease_burnEmpty.snap | 2 +- ...tionManager_decrease_burnEmpty_native.snap | 2 +- ...nager_decrease_sameRange_allLiquidity.snap | 2 +- .../PositionManager_decrease_take_take.snap | 2 +- ...ger_increaseLiquidity_erc20_withClose.snap | 2 +- ...ncreaseLiquidity_erc20_withSettlePair.snap | 2 +- ...itionManager_increaseLiquidity_native.snap | 2 +- ...crease_autocompoundExactUnclaimedFees.snap | 2 +- ...increase_autocompoundExcessFeesCredit.snap | 2 +- ...ger_increase_autocompound_clearExcess.snap | 2 +- .../PositionManager_mint_native.snap | 2 +- ...anager_mint_nativeWithSweep_withClose.snap | 2 +- ...r_mint_nativeWithSweep_withSettlePair.snap | 2 +- .../PositionManager_mint_onSameTickLower.snap | 2 +- .../PositionManager_mint_onSameTickUpper.snap | 2 +- .../PositionManager_mint_sameRange.snap | 2 +- ...nManager_mint_settleWithBalance_sweep.snap | 2 +- ...anager_mint_warmedPool_differentRange.snap | 2 +- .../PositionManager_mint_withClose.snap | 2 +- .../PositionManager_mint_withSettlePair.snap | 2 +- ...tionManager_multicall_initialize_mint.snap | 2 +- src/PositionManager.sol | 37 +--- src/base/Notifier.sol | 42 ++--- src/interfaces/INotifier.sol | 8 +- src/interfaces/IPositionManager.sol | 5 - src/interfaces/ISubscriber.sol | 14 +- src/libraries/PositionConfig.sol | 24 --- src/libraries/PositionConfigId.sol | 39 ----- test/libraries/PositionConfig.t.sol | 161 ------------------ test/mocks/MockBadSubscribers.sol | 12 +- test/mocks/MockSubscriber.sol | 9 +- test/position-managers/NativeToken.t.sol | 5 +- .../PositionManager.notifier.t.sol | 61 +++---- 46 files changed, 94 insertions(+), 391 deletions(-) delete mode 100644 src/libraries/PositionConfigId.sol delete mode 100644 test/libraries/PositionConfig.t.sol diff --git a/.forge-snapshots/PositionManager_burn_empty.snap b/.forge-snapshots/PositionManager_burn_empty.snap index 056337b54..a4dc34679 100644 --- a/.forge-snapshots/PositionManager_burn_empty.snap +++ b/.forge-snapshots/PositionManager_burn_empty.snap @@ -1 +1 @@ -54314 \ No newline at end of file +50221 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_burn_empty_native.snap b/.forge-snapshots/PositionManager_burn_empty_native.snap index 056337b54..a4dc34679 100644 --- a/.forge-snapshots/PositionManager_burn_empty_native.snap +++ b/.forge-snapshots/PositionManager_burn_empty_native.snap @@ -1 +1 @@ -54314 \ No newline at end of file +50221 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_burn_nonEmpty_native_withClose.snap b/.forge-snapshots/PositionManager_burn_nonEmpty_native_withClose.snap index 5e29bfc52..be563cf8c 100644 --- a/.forge-snapshots/PositionManager_burn_nonEmpty_native_withClose.snap +++ b/.forge-snapshots/PositionManager_burn_nonEmpty_native_withClose.snap @@ -1 +1 @@ -130590 \ No newline at end of file +126586 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_burn_nonEmpty_native_withTakePair.snap b/.forge-snapshots/PositionManager_burn_nonEmpty_native_withTakePair.snap index 81735e9a1..823c6fc90 100644 --- a/.forge-snapshots/PositionManager_burn_nonEmpty_native_withTakePair.snap +++ b/.forge-snapshots/PositionManager_burn_nonEmpty_native_withTakePair.snap @@ -1 +1 @@ -130088 \ No newline at end of file +126084 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_burn_nonEmpty_withClose.snap b/.forge-snapshots/PositionManager_burn_nonEmpty_withClose.snap index a8c93a104..a83b28e77 100644 --- a/.forge-snapshots/PositionManager_burn_nonEmpty_withClose.snap +++ b/.forge-snapshots/PositionManager_burn_nonEmpty_withClose.snap @@ -1 +1 @@ -137486 \ No newline at end of file +133482 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_burn_nonEmpty_withTakePair.snap b/.forge-snapshots/PositionManager_burn_nonEmpty_withTakePair.snap index 0577ef974..50e6cd8eb 100644 --- a/.forge-snapshots/PositionManager_burn_nonEmpty_withTakePair.snap +++ b/.forge-snapshots/PositionManager_burn_nonEmpty_withTakePair.snap @@ -1 +1 @@ -136984 \ No newline at end of file +132980 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_collect_native.snap b/.forge-snapshots/PositionManager_collect_native.snap index 0f476515b..2679b16b5 100644 --- a/.forge-snapshots/PositionManager_collect_native.snap +++ b/.forge-snapshots/PositionManager_collect_native.snap @@ -1 +1 @@ -148872 \ No newline at end of file +146839 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_collect_sameRange.snap b/.forge-snapshots/PositionManager_collect_sameRange.snap index 60029dc33..872ac116b 100644 --- a/.forge-snapshots/PositionManager_collect_sameRange.snap +++ b/.forge-snapshots/PositionManager_collect_sameRange.snap @@ -1 +1 @@ -157492 \ No newline at end of file +155459 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_collect_withClose.snap b/.forge-snapshots/PositionManager_collect_withClose.snap index 60029dc33..872ac116b 100644 --- a/.forge-snapshots/PositionManager_collect_withClose.snap +++ b/.forge-snapshots/PositionManager_collect_withClose.snap @@ -1 +1 @@ -157492 \ No newline at end of file +155459 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_collect_withTakePair.snap b/.forge-snapshots/PositionManager_collect_withTakePair.snap index 3d3b8e961..236cd2f3f 100644 --- a/.forge-snapshots/PositionManager_collect_withTakePair.snap +++ b/.forge-snapshots/PositionManager_collect_withTakePair.snap @@ -1 +1 @@ -156876 \ No newline at end of file +154843 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_decreaseLiquidity_native.snap b/.forge-snapshots/PositionManager_decreaseLiquidity_native.snap index ad049b47d..fad7910bd 100644 --- a/.forge-snapshots/PositionManager_decreaseLiquidity_native.snap +++ b/.forge-snapshots/PositionManager_decreaseLiquidity_native.snap @@ -1 +1 @@ -114572 \ No newline at end of file +112946 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_decreaseLiquidity_withClose.snap b/.forge-snapshots/PositionManager_decreaseLiquidity_withClose.snap index b90b7b220..fa15c9567 100644 --- a/.forge-snapshots/PositionManager_decreaseLiquidity_withClose.snap +++ b/.forge-snapshots/PositionManager_decreaseLiquidity_withClose.snap @@ -1 +1 @@ -123035 \ No newline at end of file +121002 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_decreaseLiquidity_withTakePair.snap b/.forge-snapshots/PositionManager_decreaseLiquidity_withTakePair.snap index 3426f6af2..aa85e5ecc 100644 --- a/.forge-snapshots/PositionManager_decreaseLiquidity_withTakePair.snap +++ b/.forge-snapshots/PositionManager_decreaseLiquidity_withTakePair.snap @@ -1 +1 @@ -122419 \ No newline at end of file +120386 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_decrease_burnEmpty.snap b/.forge-snapshots/PositionManager_decrease_burnEmpty.snap index 551944bf9..be5018bb5 100644 --- a/.forge-snapshots/PositionManager_decrease_burnEmpty.snap +++ b/.forge-snapshots/PositionManager_decrease_burnEmpty.snap @@ -1 +1 @@ -140615 \ No newline at end of file +136611 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_decrease_burnEmpty_native.snap b/.forge-snapshots/PositionManager_decrease_burnEmpty_native.snap index 908d54aa1..6bf2eef7d 100644 --- a/.forge-snapshots/PositionManager_decrease_burnEmpty_native.snap +++ b/.forge-snapshots/PositionManager_decrease_burnEmpty_native.snap @@ -1 +1 @@ -133719 \ No newline at end of file +129715 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_decrease_sameRange_allLiquidity.snap b/.forge-snapshots/PositionManager_decrease_sameRange_allLiquidity.snap index 9ec6ec94c..95a6b4a79 100644 --- a/.forge-snapshots/PositionManager_decrease_sameRange_allLiquidity.snap +++ b/.forge-snapshots/PositionManager_decrease_sameRange_allLiquidity.snap @@ -1 +1 @@ -135751 \ No newline at end of file +133718 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_decrease_take_take.snap b/.forge-snapshots/PositionManager_decrease_take_take.snap index 0a8cf2a47..769a445d4 100644 --- a/.forge-snapshots/PositionManager_decrease_take_take.snap +++ b/.forge-snapshots/PositionManager_decrease_take_take.snap @@ -1 +1 @@ -123568 \ No newline at end of file +121535 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_increaseLiquidity_erc20_withClose.snap b/.forge-snapshots/PositionManager_increaseLiquidity_erc20_withClose.snap index 22ea7d07f..244e0071d 100644 --- a/.forge-snapshots/PositionManager_increaseLiquidity_erc20_withClose.snap +++ b/.forge-snapshots/PositionManager_increaseLiquidity_erc20_withClose.snap @@ -1 +1 @@ -161742 \ No newline at end of file +159709 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_increaseLiquidity_erc20_withSettlePair.snap b/.forge-snapshots/PositionManager_increaseLiquidity_erc20_withSettlePair.snap index f8883bb68..f01d0684c 100644 --- a/.forge-snapshots/PositionManager_increaseLiquidity_erc20_withSettlePair.snap +++ b/.forge-snapshots/PositionManager_increaseLiquidity_erc20_withSettlePair.snap @@ -1 +1 @@ -160768 \ No newline at end of file +158735 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_increaseLiquidity_native.snap b/.forge-snapshots/PositionManager_increaseLiquidity_native.snap index 75cf7e513..5a2c5c6ca 100644 --- a/.forge-snapshots/PositionManager_increaseLiquidity_native.snap +++ b/.forge-snapshots/PositionManager_increaseLiquidity_native.snap @@ -1 +1 @@ -143770 \ No newline at end of file +141737 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_increase_autocompoundExactUnclaimedFees.snap b/.forge-snapshots/PositionManager_increase_autocompoundExactUnclaimedFees.snap index b68cadf46..61b699427 100644 --- a/.forge-snapshots/PositionManager_increase_autocompoundExactUnclaimedFees.snap +++ b/.forge-snapshots/PositionManager_increase_autocompoundExactUnclaimedFees.snap @@ -1 +1 @@ -139887 \ No newline at end of file +137854 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_increase_autocompoundExcessFeesCredit.snap b/.forge-snapshots/PositionManager_increase_autocompoundExcessFeesCredit.snap index a33d558dd..7b28887a9 100644 --- a/.forge-snapshots/PositionManager_increase_autocompoundExcessFeesCredit.snap +++ b/.forge-snapshots/PositionManager_increase_autocompoundExcessFeesCredit.snap @@ -1 +1 @@ -180803 \ No newline at end of file +178770 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_increase_autocompound_clearExcess.snap b/.forge-snapshots/PositionManager_increase_autocompound_clearExcess.snap index 8943df7ea..7050be040 100644 --- a/.forge-snapshots/PositionManager_increase_autocompound_clearExcess.snap +++ b/.forge-snapshots/PositionManager_increase_autocompound_clearExcess.snap @@ -1 +1 @@ -150759 \ No newline at end of file +148726 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_mint_native.snap b/.forge-snapshots/PositionManager_mint_native.snap index 1c41e3aa3..8ce6c1b53 100644 --- a/.forge-snapshots/PositionManager_mint_native.snap +++ b/.forge-snapshots/PositionManager_mint_native.snap @@ -1 +1 @@ -393206 \ No newline at end of file +370766 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_mint_nativeWithSweep_withClose.snap b/.forge-snapshots/PositionManager_mint_nativeWithSweep_withClose.snap index 1b4b4f494..cfbfa2d12 100644 --- a/.forge-snapshots/PositionManager_mint_nativeWithSweep_withClose.snap +++ b/.forge-snapshots/PositionManager_mint_nativeWithSweep_withClose.snap @@ -1 +1 @@ -401698 \ No newline at end of file +379258 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_mint_nativeWithSweep_withSettlePair.snap b/.forge-snapshots/PositionManager_mint_nativeWithSweep_withSettlePair.snap index 145d441c0..e619bc49b 100644 --- a/.forge-snapshots/PositionManager_mint_nativeWithSweep_withSettlePair.snap +++ b/.forge-snapshots/PositionManager_mint_nativeWithSweep_withSettlePair.snap @@ -1 +1 @@ -401000 \ No newline at end of file +378560 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_mint_onSameTickLower.snap b/.forge-snapshots/PositionManager_mint_onSameTickLower.snap index d22d17a60..3e24b7f4d 100644 --- a/.forge-snapshots/PositionManager_mint_onSameTickLower.snap +++ b/.forge-snapshots/PositionManager_mint_onSameTickLower.snap @@ -1 +1 @@ -349839 \ No newline at end of file +327399 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_mint_onSameTickUpper.snap b/.forge-snapshots/PositionManager_mint_onSameTickUpper.snap index a5e34f74d..90549c24e 100644 --- a/.forge-snapshots/PositionManager_mint_onSameTickUpper.snap +++ b/.forge-snapshots/PositionManager_mint_onSameTickUpper.snap @@ -1 +1 @@ -350481 \ No newline at end of file +328041 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_mint_sameRange.snap b/.forge-snapshots/PositionManager_mint_sameRange.snap index 25cf02cd8..f4d8890df 100644 --- a/.forge-snapshots/PositionManager_mint_sameRange.snap +++ b/.forge-snapshots/PositionManager_mint_sameRange.snap @@ -1 +1 @@ -276063 \ No newline at end of file +253623 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_mint_settleWithBalance_sweep.snap b/.forge-snapshots/PositionManager_mint_settleWithBalance_sweep.snap index 73fdf7206..2e3876632 100644 --- a/.forge-snapshots/PositionManager_mint_settleWithBalance_sweep.snap +++ b/.forge-snapshots/PositionManager_mint_settleWithBalance_sweep.snap @@ -1 +1 @@ -447136 \ No newline at end of file +424696 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_mint_warmedPool_differentRange.snap b/.forge-snapshots/PositionManager_mint_warmedPool_differentRange.snap index c32356bc0..27db49c26 100644 --- a/.forge-snapshots/PositionManager_mint_warmedPool_differentRange.snap +++ b/.forge-snapshots/PositionManager_mint_warmedPool_differentRange.snap @@ -1 +1 @@ -355857 \ No newline at end of file +333417 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_mint_withClose.snap b/.forge-snapshots/PositionManager_mint_withClose.snap index 7948b03a5..4db6eaef2 100644 --- a/.forge-snapshots/PositionManager_mint_withClose.snap +++ b/.forge-snapshots/PositionManager_mint_withClose.snap @@ -1 +1 @@ -448406 \ No newline at end of file +425966 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_mint_withSettlePair.snap b/.forge-snapshots/PositionManager_mint_withSettlePair.snap index 2ec2b4c2b..2e62d8631 100644 --- a/.forge-snapshots/PositionManager_mint_withSettlePair.snap +++ b/.forge-snapshots/PositionManager_mint_withSettlePair.snap @@ -1 +1 @@ -447546 \ No newline at end of file +425106 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_multicall_initialize_mint.snap b/.forge-snapshots/PositionManager_multicall_initialize_mint.snap index e0b4353fe..145400306 100644 --- a/.forge-snapshots/PositionManager_multicall_initialize_mint.snap +++ b/.forge-snapshots/PositionManager_multicall_initialize_mint.snap @@ -1 +1 @@ -492874 \ No newline at end of file +470411 \ No newline at end of file diff --git a/src/PositionManager.sol b/src/PositionManager.sol index 6feeab622..c3285c4df 100644 --- a/src/PositionManager.sol +++ b/src/PositionManager.sol @@ -20,7 +20,7 @@ import {IPositionManager} from "./interfaces/IPositionManager.sol"; import {Multicall_v4} from "./base/Multicall_v4.sol"; import {PoolInitializer} from "./base/PoolInitializer.sol"; import {DeltaResolver} from "./base/DeltaResolver.sol"; -import {PositionConfig, PositionConfigLibrary} from "./libraries/PositionConfig.sol"; +import {PositionConfig} from "./libraries/PositionConfig.sol"; import {BaseActionsRouter} from "./base/BaseActionsRouter.sol"; import {Actions} from "./libraries/Actions.sol"; import {Notifier} from "./base/Notifier.sol"; @@ -28,7 +28,6 @@ import {CalldataDecoder} from "./libraries/CalldataDecoder.sol"; import {INotifier} from "./interfaces/INotifier.sol"; import {Permit2Forwarder} from "./base/Permit2Forwarder.sol"; import {SlippageCheckLibrary} from "./libraries/SlippageCheck.sol"; -import {PositionConfigId, PositionConfigIdLibrary} from "./libraries/PositionConfigId.sol"; import {PoolKeyChecker} from "./libraries/PoolKeyChecker.sol"; // 444444444 @@ -111,20 +110,18 @@ contract PositionManager is using SafeTransferLib for *; using CurrencyLibrary for Currency; using PoolIdLibrary for PoolKey; - using PositionConfigLibrary for PositionConfig; using StateLibrary for IPoolManager; using TransientStateLibrary for IPoolManager; using SafeCast for uint256; using SafeCast for int256; using CalldataDecoder for bytes; using SlippageCheckLibrary for BalanceDelta; - using PositionConfigIdLibrary for PositionConfigId; using PoolKeyChecker for PoolKey; /// @dev The ID of the next token that will be minted. Skips 0 uint256 public nextTokenId = 1; - // TODO: Move to custom type so this is packed in memory + // TODO: Move to custom type, packed memory. struct PositionInfo { // lower 25 bytes of the poolId bytes25 poolId; @@ -136,11 +133,9 @@ contract PositionManager is mapping(uint256 tokenId => PositionInfo info) internal positionInfo; mapping(bytes32 poolId => PoolKey poolKey) internal poolKeys; - mapping(uint256 tokenId => PositionConfigId configId) internal positionConfigs; - - /// @notice an internal getter for PositionConfigId to be used by Notifier - function _positionConfigs(uint256 tokenId) internal view override returns (PositionConfigId storage) { - return positionConfigs[tokenId]; + /// @notice an internal getter for PositionInfo to be used by Notifier + function _positionInfo(uint256 tokenId) internal view override returns (PositionInfo storage) { + return positionInfo[tokenId]; } constructor(IPoolManager _poolManager, IAllowanceTransfer _permit2) @@ -166,14 +161,6 @@ contract PositionManager is _; } - /// @notice Reverts if the hash of the config does not equal the saved hash - /// @param tokenId the unique identifier of the ERC721 token - /// @param config the PositionConfig to check against - modifier onlyValidConfig(uint256 tokenId, PositionConfig calldata config) override { - if (positionConfigs[tokenId].getConfigId() != config.toId()) revert IncorrectPositionConfigForTokenId(tokenId); - _; - } - /// @inheritdoc IPositionManager function modifyLiquidities(bytes calldata unlockData, uint256 deadline) external @@ -308,7 +295,6 @@ contract PositionManager is _modifyLiquidity(config, liquidity.toInt256(), bytes32(tokenId), hookData); // Slippage checks should be done on the principal liquidityDelta which is the liquidityDelta - feesAccrued (liquidityDelta - feesAccrued).validateMaxIn(amount0Max, amount1Max); - positionConfigs[tokenId].setConfigId(config.toId()); PositionInfo memory info = PositionInfo({ poolId: bytes25(PoolId.unwrap(config.poolKey.toId())), @@ -343,7 +329,6 @@ contract PositionManager is (liquidityDelta - feesAccrued).validateMinOut(amount0Min, amount1Min); } - delete positionConfigs[tokenId]; delete positionInfo[tokenId]; // Burn the token. _burn(tokenId); @@ -412,8 +397,9 @@ contract PositionManager is hookData ); - if (positionConfigs[uint256(salt)].hasSubscriber()) { - _notifyModifyLiquidity(uint256(salt), config, liquidityChange, feesAccrued); + // TODO: Move outside this function since we've already loaded info. && other audit issues.. + if (positionInfo[uint256(salt)].hasSubscriber) { + _notifyModifyLiquidity(uint256(salt), liquidityChange, feesAccrued); } } @@ -430,7 +416,7 @@ contract PositionManager is /// @dev overrides solmate transferFrom in case a notification to subscribers is needed function transferFrom(address from, address to, uint256 id) public virtual override { super.transferFrom(from, to, id); - if (positionConfigs[id].hasSubscriber()) _notifyTransfer(id, from, to); + if (positionInfo[id].hasSubscriber) _notifyTransfer(id, from, to); } /// @inheritdoc IPositionManager @@ -447,9 +433,4 @@ contract PositionManager is function _toConfig(PositionInfo memory info) internal view returns (PositionConfig memory config) { return PositionConfig({poolKey: poolKeys[info.poolId], tickLower: info.tickLower, tickUpper: info.tickUpper}); } - - /// @inheritdoc IPositionManager - function getPositionConfigId(uint256 tokenId) external view returns (bytes32) { - return positionConfigs[tokenId].getConfigId(); - } } diff --git a/src/base/Notifier.sol b/src/base/Notifier.sol index 59c32e8aa..c708861c1 100644 --- a/src/base/Notifier.sol +++ b/src/base/Notifier.sol @@ -2,18 +2,16 @@ pragma solidity ^0.8.24; import {ISubscriber} from "../interfaces/ISubscriber.sol"; -import {PositionConfig} from "../libraries/PositionConfig.sol"; -import {PositionConfigId, PositionConfigIdLibrary} from "../libraries/PositionConfigId.sol"; import {BipsLibrary} from "../libraries/BipsLibrary.sol"; import {INotifier} from "../interfaces/INotifier.sol"; import {CustomRevert} from "@uniswap/v4-core/src/libraries/CustomRevert.sol"; import {BalanceDelta} from "@uniswap/v4-core/src/types/BalanceDelta.sol"; +import {PositionManager} from "../PositionManager.sol"; /// @notice Notifier is used to opt in to sending updates to external contracts about position modifications or transfers abstract contract Notifier is INotifier { using BipsLibrary for uint256; using CustomRevert for bytes4; - using PositionConfigIdLibrary for PositionConfigId; error AlreadySubscribed(address subscriber); @@ -30,27 +28,23 @@ abstract contract Notifier is INotifier { mapping(uint256 tokenId => ISubscriber subscriber) public subscriber; modifier onlyIfApproved(address caller, uint256 tokenId) virtual; - modifier onlyValidConfig(uint256 tokenId, PositionConfig calldata config) virtual; - function _positionConfigs(uint256 tokenId) internal view virtual returns (PositionConfigId storage); + function _positionInfo(uint256 tokenId) internal view virtual returns (PositionManager.PositionInfo storage); /// @inheritdoc INotifier - function subscribe(uint256 tokenId, PositionConfig calldata config, address newSubscriber, bytes calldata data) + function subscribe(uint256 tokenId, address newSubscriber, bytes calldata data) external payable onlyIfApproved(msg.sender, tokenId) - onlyValidConfig(tokenId, config) { - // will revert below if the user already has a subcriber - _positionConfigs(tokenId).setSubscribe(); + _positionInfo(tokenId).hasSubscriber = true; ISubscriber _subscriber = subscriber[tokenId]; if (_subscriber != NO_SUBSCRIBER) revert AlreadySubscribed(address(_subscriber)); subscriber[tokenId] = ISubscriber(newSubscriber); - bool success = _call( - address(newSubscriber), abi.encodeWithSelector(ISubscriber.notifySubscribe.selector, tokenId, config, data) - ); + bool success = + _call(address(newSubscriber), abi.encodeWithSelector(ISubscriber.notifySubscribe.selector, tokenId, data)); if (!success) { Wrap__SubsciptionReverted.selector.bubbleUpAndRevertWith(address(newSubscriber)); @@ -60,36 +54,24 @@ abstract contract Notifier is INotifier { } /// @inheritdoc INotifier - function unsubscribe(uint256 tokenId, PositionConfig calldata config, bytes calldata data) - external - payable - onlyIfApproved(msg.sender, tokenId) - onlyValidConfig(tokenId, config) - { - _positionConfigs(tokenId).setUnsubscribe(); + function unsubscribe(uint256 tokenId, bytes calldata data) external payable onlyIfApproved(msg.sender, tokenId) { + _positionInfo(tokenId).hasSubscriber = false; ISubscriber _subscriber = subscriber[tokenId]; delete subscriber[tokenId]; uint256 subscriberGasLimit = block.gaslimit.calculatePortion(BLOCK_LIMIT_BPS); - try _subscriber.notifyUnsubscribe{gas: subscriberGasLimit}(tokenId, config, data) {} catch {} + try _subscriber.notifyUnsubscribe{gas: subscriberGasLimit}(tokenId, data) {} catch {} emit Unsubscribed(tokenId, address(_subscriber)); } - function _notifyModifyLiquidity( - uint256 tokenId, - PositionConfig memory config, - int256 liquidityChange, - BalanceDelta feesAccrued - ) internal { + function _notifyModifyLiquidity(uint256 tokenId, int256 liquidityChange, BalanceDelta feesAccrued) internal { ISubscriber _subscriber = subscriber[tokenId]; bool success = _call( address(_subscriber), - abi.encodeWithSelector( - ISubscriber.notifyModifyLiquidity.selector, tokenId, config, liquidityChange, feesAccrued - ) + abi.encodeWithSelector(ISubscriber.notifyModifyLiquidity.selector, tokenId, liquidityChange, feesAccrued) ); if (!success) { @@ -118,6 +100,6 @@ abstract contract Notifier is INotifier { /// @inheritdoc INotifier function hasSubscriber(uint256 tokenId) external view returns (bool) { - return _positionConfigs(tokenId).hasSubscriber(); + return _positionInfo(tokenId).hasSubscriber; } } diff --git a/src/interfaces/INotifier.sol b/src/interfaces/INotifier.sol index 20c4d7f0d..e817c1bf0 100644 --- a/src/interfaces/INotifier.sol +++ b/src/interfaces/INotifier.sol @@ -14,22 +14,18 @@ interface INotifier { /// @notice Enables the subscriber to receive notifications for a respective position /// @param tokenId the ERC721 tokenId - /// @param config the corresponding PositionConfig for the tokenId /// @param subscriber the address to notify /// @param data caller-provided data that's forwarded to the subscriber contract /// @dev Calling subscribe when a position is already subscribed will revert /// @dev payable so it can be multicalled with NATIVE related actions - function subscribe(uint256 tokenId, PositionConfig calldata config, address subscriber, bytes calldata data) - external - payable; + function subscribe(uint256 tokenId, address subscriber, bytes calldata data) external payable; /// @notice Removes the subscriber from receiving notifications for a respective position /// @param tokenId the ERC721 tokenId - /// @param config the corresponding PositionConfig for the tokenId /// @param data caller-provided data that's forwarded to the subscriber contract /// @dev payable so it can be multicalled with NATIVE related actions /// @dev Must always allow a user to unsubscribe. In the case of a malicious subscriber, a user can always unsubscribe safely, ensuring liquidity is always modifiable. - function unsubscribe(uint256 tokenId, PositionConfig calldata config, bytes calldata data) external payable; + function unsubscribe(uint256 tokenId, bytes calldata data) external payable; /// @notice Returns whether a a position should call out to notify a subscribing contract on modification or transfer /// @param tokenId the ERC721 tokenId diff --git a/src/interfaces/IPositionManager.sol b/src/interfaces/IPositionManager.sol index 03213c747..ce5dee262 100644 --- a/src/interfaces/IPositionManager.sol +++ b/src/interfaces/IPositionManager.sol @@ -30,11 +30,6 @@ interface IPositionManager is INotifier { /// @return uint256 The next token ID function nextTokenId() external view returns (uint256); - /// @param tokenId the ERC721 tokenId - /// @return configId a truncated hash of the position's poolkey, tickLower, and tickUpper - /// @dev truncates the least significant bit of the hash - function getPositionConfigId(uint256 tokenId) external view returns (bytes32 configId); - /// @param tokenId the ERC721 tokenId /// @param config the corresponding PositionConfig for the tokenId /// @return liquidity the position's liquidity, as a liquidityAmount diff --git a/src/interfaces/ISubscriber.sol b/src/interfaces/ISubscriber.sol index 18d428c7b..316930a2e 100644 --- a/src/interfaces/ISubscriber.sol +++ b/src/interfaces/ISubscriber.sol @@ -7,23 +7,15 @@ import {BalanceDelta} from "@uniswap/v4-core/src/types/BalanceDelta.sol"; /// @notice Interface that a Subscriber contract should implement to receive updates from the v4 position manager interface ISubscriber { /// @param tokenId the token ID of the position - /// @param config details about the position /// @param data additional data passed in by the caller - function notifySubscribe(uint256 tokenId, PositionConfig memory config, bytes memory data) external; + function notifySubscribe(uint256 tokenId, bytes memory data) external; /// @param tokenId the token ID of the position - /// @param config details about the position /// @param data additional data passed in by the caller - function notifyUnsubscribe(uint256 tokenId, PositionConfig memory config, bytes memory data) external; + function notifyUnsubscribe(uint256 tokenId, bytes memory data) external; /// @param tokenId the token ID of the position - /// @param config details about the position /// @param liquidityChange the change in liquidity on the underlying position /// @param feesAccrued the fees to be collected from the position as a result of the modifyLiquidity call - function notifyModifyLiquidity( - uint256 tokenId, - PositionConfig memory config, - int256 liquidityChange, - BalanceDelta feesAccrued - ) external; + function notifyModifyLiquidity(uint256 tokenId, int256 liquidityChange, BalanceDelta feesAccrued) external; /// @param tokenId the token ID of the position /// @param previousOwner address of the old owner /// @param newOwner address of the new owner diff --git a/src/libraries/PositionConfig.sol b/src/libraries/PositionConfig.sol index 8f3d6ead9..ba9b85e1c 100644 --- a/src/libraries/PositionConfig.sol +++ b/src/libraries/PositionConfig.sol @@ -9,27 +9,3 @@ struct PositionConfig { int24 tickLower; int24 tickUpper; } - -/// @notice Library to calculate the PositionConfigId from the PositionConfig struct -library PositionConfigLibrary { - function toId(PositionConfig calldata config) internal pure returns (bytes32 id) { - // id = keccak256(abi.encodePacked(currency0, currency1, fee, tickSpacing, hooks, tickLower, tickUpper))) >> 1 - assembly ("memory-safe") { - let fmp := mload(0x40) - mstore(add(fmp, 0x34), calldataload(add(config, 0xc0))) // tickUpper: [0x51, 0x54) - mstore(add(fmp, 0x31), calldataload(add(config, 0xa0))) // tickLower: [0x4E, 0x51) - mstore(add(fmp, 0x2E), calldataload(add(config, 0x80))) // hooks: [0x3A, 0x4E) - mstore(add(fmp, 0x1A), calldataload(add(config, 0x60))) // tickSpacing: [0x37, 0x3A) - mstore(add(fmp, 0x17), calldataload(add(config, 0x40))) // fee: [0x34, 0x37) - mstore(add(fmp, 0x14), calldataload(add(config, 0x20))) // currency1: [0x20, 0x34) - mstore(fmp, calldataload(config)) // currency0: [0x0c, 0x20) - - id := shr(1, keccak256(add(fmp, 0x0c), 0x48)) // len is 72 bytes, truncate lower bit of the hash - - // now clean the memory we used - mstore(add(fmp, 0x40), 0) // fmp+0x40 held hooks (14 bytes), tickLower, tickUpper - mstore(add(fmp, 0x20), 0) // fmp+0x20 held currency1, fee, tickSpacing, hooks (6 bytes) - mstore(fmp, 0) // fmp held currency0 - } - } -} diff --git a/src/libraries/PositionConfigId.sol b/src/libraries/PositionConfigId.sol deleted file mode 100644 index 40127102a..000000000 --- a/src/libraries/PositionConfigId.sol +++ /dev/null @@ -1,39 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity ^0.8.24; - -/// @notice A configId is set per tokenId -/// The lower 255 bits are used to store the truncated hash of the corresponding PositionConfig -/// The upper bit is used to signal if the tokenId has a subscriber -struct PositionConfigId { - bytes32 id; -} - -library PositionConfigIdLibrary { - bytes32 constant MASK_UPPER_BIT = 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; - bytes32 constant DIRTY_UPPER_BIT = 0x8000000000000000000000000000000000000000000000000000000000000000; - - /// @notice returns the truncated hash of the PositionConfig for a given tokenId - function getConfigId(PositionConfigId storage _configId) internal view returns (bytes32 configId) { - configId = _configId.id & MASK_UPPER_BIT; - } - - /// @dev We only set the config on mint, guaranteeing that the most significant bit is unset, so we can just assign the entire 32 bytes to the id. - function setConfigId(PositionConfigId storage _configId, bytes32 configId) internal { - _configId.id = configId; - } - - function setSubscribe(PositionConfigId storage configId) internal { - configId.id |= DIRTY_UPPER_BIT; - } - - function setUnsubscribe(PositionConfigId storage configId) internal { - configId.id &= MASK_UPPER_BIT; - } - - function hasSubscriber(PositionConfigId storage configId) internal view returns (bool subscribed) { - bytes32 _id = configId.id; - assembly ("memory-safe") { - subscribed := shr(255, _id) - } - } -} diff --git a/test/libraries/PositionConfig.t.sol b/test/libraries/PositionConfig.t.sol deleted file mode 100644 index 187ee370e..000000000 --- a/test/libraries/PositionConfig.t.sol +++ /dev/null @@ -1,161 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.24; - -import "forge-std/Test.sol"; - -import {PositionConfig, PositionConfigLibrary} from "../../src/libraries/PositionConfig.sol"; -import {PositionConfigId, PositionConfigIdLibrary} from "../../src/libraries/PositionConfigId.sol"; - -contract PositionConfigTest is Test { - using PositionConfigLibrary for PositionConfig; - using PositionConfigIdLibrary for PositionConfigId; - - mapping(uint256 => PositionConfigId) internal testConfigs; - - bytes32 public constant UPPER_BIT_SET = 0x8000000000000000000000000000000000000000000000000000000000000000; - - function test_fuzz_toId(PositionConfig calldata config) public pure { - bytes32 expectedId = _calculateExpectedId(config); - assertEq(expectedId, config.toId()); - } - - function test_fuzz_setConfigId(uint256 tokenId, PositionConfig calldata config) public { - testConfigs[tokenId].setConfigId(config.toId()); - - bytes32 expectedConfigId = _calculateExpectedId(config); - - bytes32 actualConfigId = testConfigs[tokenId].id; - assertEq(expectedConfigId, actualConfigId); - } - - function test_fuzz_getConfigId(uint256 tokenId, PositionConfig calldata config) public { - bytes32 expectedId = _calculateExpectedId(config); - // set - testConfigs[tokenId] = PositionConfigId({id: expectedId}); - - assertEq(expectedId, testConfigs[tokenId].getConfigId()); - } - - function test_fuzz_setConfigId_getConfigId(uint256 tokenId, PositionConfig calldata config) public { - testConfigs[tokenId].setConfigId(config.toId()); - - bytes32 expectedId = _calculateExpectedId(config); - - assertEq(testConfigs[tokenId].getConfigId(), testConfigs[tokenId].id); - assertEq(testConfigs[tokenId].getConfigId(), expectedId); - } - - function test_fuzz_getConfigId_equal_afterSubscribe(uint256 tokenId, PositionConfig calldata config) public { - testConfigs[tokenId].setConfigId(config.toId()); - testConfigs[tokenId].setSubscribe(); - - assertEq(testConfigs[tokenId].getConfigId(), config.toId()); - } - - function test_fuzz_setSubscribe(uint256 tokenId) public { - testConfigs[tokenId].setSubscribe(); - bytes32 upperBitSet = testConfigs[tokenId].id; - - assertEq(upperBitSet, UPPER_BIT_SET); - } - - function test_fuzz_setConfigId_setSubscribe(uint256 tokenId, PositionConfig calldata config) public { - testConfigs[tokenId].setConfigId(config.toId()); - testConfigs[tokenId].setSubscribe(); - - bytes32 expectedConfig = _calculateExpectedId(config) | UPPER_BIT_SET; - - bytes32 _config = testConfigs[tokenId].id; - - assertEq(_config, expectedConfig); - } - - function test_fuzz_setUnsubscribe(uint256 tokenId) public { - testConfigs[tokenId].setSubscribe(); - bytes32 _config = testConfigs[tokenId].id; - assertEq(_config, UPPER_BIT_SET); - testConfigs[tokenId].setUnsubscribe(); - _config = testConfigs[tokenId].id; - assertEq(_config, 0); - } - - function test_hasSubscriber(uint256 tokenId) public { - testConfigs[tokenId].setSubscribe(); - assert(testConfigs[tokenId].hasSubscriber()); - testConfigs[tokenId].setUnsubscribe(); - assert(!testConfigs[tokenId].hasSubscriber()); - } - - function test_fuzz_setConfigId_setSubscribe_setUnsubscribe_getConfigId( - uint256 tokenId, - PositionConfig calldata config - ) public { - assertEq(testConfigs[tokenId].getConfigId(), 0); - - testConfigs[tokenId].setConfigId(config.toId()); - assertEq(testConfigs[tokenId].getConfigId(), config.toId()); - - testConfigs[tokenId].setSubscribe(); - assertEq(testConfigs[tokenId].getConfigId(), config.toId()); - assertEq(testConfigs[tokenId].hasSubscriber(), true); - - testConfigs[tokenId].setUnsubscribe(); - assertEq(testConfigs[tokenId].getConfigId(), config.toId()); - assertEq(testConfigs[tokenId].hasSubscriber(), false); - } - - function test_fuzz_setSubscribe_twice(uint256 tokenId, PositionConfig calldata config) public { - assertFalse(testConfigs[tokenId].hasSubscriber()); - - testConfigs[tokenId].setSubscribe(); - testConfigs[tokenId].setSubscribe(); - assertTrue(testConfigs[tokenId].hasSubscriber()); - - // It is known behavior that setting the config id just stores the id directly, meaning the upper most bit is unset. - // This is ok because setConfigId will only ever be called on mint. - testConfigs[tokenId].setConfigId(config.toId()); - assertFalse(testConfigs[tokenId].hasSubscriber()); - - testConfigs[tokenId].setSubscribe(); - testConfigs[tokenId].setSubscribe(); - assertTrue(testConfigs[tokenId].hasSubscriber()); - } - - function test_fuzz_setUnsubscribe_twice(uint256 tokenId, PositionConfig calldata config) public { - assertFalse(testConfigs[tokenId].hasSubscriber()); - - testConfigs[tokenId].setUnsubscribe(); - testConfigs[tokenId].setUnsubscribe(); - assertFalse(testConfigs[tokenId].hasSubscriber()); - - testConfigs[tokenId].setConfigId(config.toId()); - assertFalse(testConfigs[tokenId].hasSubscriber()); - - testConfigs[tokenId].setUnsubscribe(); - testConfigs[tokenId].setUnsubscribe(); - assertFalse(testConfigs[tokenId].hasSubscriber()); - - testConfigs[tokenId].setSubscribe(); - assertTrue(testConfigs[tokenId].hasSubscriber()); - - testConfigs[tokenId].setUnsubscribe(); - testConfigs[tokenId].setUnsubscribe(); - assertFalse(testConfigs[tokenId].hasSubscriber()); - } - - function _calculateExpectedId(PositionConfig calldata config) internal pure returns (bytes32 expectedId) { - expectedId = keccak256( - abi.encodePacked( - config.poolKey.currency0, - config.poolKey.currency1, - config.poolKey.fee, - config.poolKey.tickSpacing, - config.poolKey.hooks, - config.tickLower, - config.tickUpper - ) - ); - // truncate the upper bit - expectedId = expectedId >> 1; - } -} diff --git a/test/mocks/MockBadSubscribers.sol b/test/mocks/MockBadSubscribers.sol index 30bafba79..4e7c2255b 100644 --- a/test/mocks/MockBadSubscribers.sol +++ b/test/mocks/MockBadSubscribers.sol @@ -30,11 +30,11 @@ contract MockReturnDataSubscriber is ISubscriber { _; } - function notifySubscribe(uint256, PositionConfig memory, bytes memory) external onlyByPosm { + function notifySubscribe(uint256, bytes memory) external onlyByPosm { notifySubscribeCount++; } - function notifyUnsubscribe(uint256, PositionConfig memory, bytes memory) external onlyByPosm { + function notifyUnsubscribe(uint256, bytes memory) external onlyByPosm { notifyUnsubscribeCount++; uint256 _memPtr = memPtr; assembly { @@ -45,7 +45,7 @@ contract MockReturnDataSubscriber is ISubscriber { } } - function notifyModifyLiquidity(uint256, PositionConfig memory, int256, BalanceDelta) external onlyByPosm { + function notifyModifyLiquidity(uint256, int256, BalanceDelta) external onlyByPosm { notifyModifyLiquidityCount++; } @@ -77,17 +77,17 @@ contract MockRevertSubscriber is ISubscriber { _; } - function notifySubscribe(uint256, PositionConfig memory, bytes memory) external view onlyByPosm { + function notifySubscribe(uint256, bytes memory) external view onlyByPosm { if (shouldRevert) { revert TestRevert("notifySubscribe"); } } - function notifyUnsubscribe(uint256, PositionConfig memory, bytes memory) external view onlyByPosm { + function notifyUnsubscribe(uint256, bytes memory) external view onlyByPosm { revert TestRevert("notifyUnsubscribe"); } - function notifyModifyLiquidity(uint256, PositionConfig memory, int256, BalanceDelta) external view onlyByPosm { + function notifyModifyLiquidity(uint256, int256, BalanceDelta) external view onlyByPosm { revert TestRevert("notifyModifyLiquidity"); } diff --git a/test/mocks/MockSubscriber.sol b/test/mocks/MockSubscriber.sol index 032bea868..9ab7ae220 100644 --- a/test/mocks/MockSubscriber.sol +++ b/test/mocks/MockSubscriber.sol @@ -33,20 +33,17 @@ contract MockSubscriber is ISubscriber { _; } - function notifySubscribe(uint256, PositionConfig memory, bytes memory data) external onlyByPosm { + function notifySubscribe(uint256, bytes memory data) external onlyByPosm { notifySubscribeCount++; subscribeData = data; } - function notifyUnsubscribe(uint256, PositionConfig memory, bytes memory data) external onlyByPosm { + function notifyUnsubscribe(uint256, bytes memory data) external onlyByPosm { notifyUnsubscribeCount++; unsubscribeData = data; } - function notifyModifyLiquidity(uint256, PositionConfig memory, int256 _liquidityChange, BalanceDelta _feesAccrued) - external - onlyByPosm - { + function notifyModifyLiquidity(uint256, int256 _liquidityChange, BalanceDelta _feesAccrued) external onlyByPosm { notifyModifyLiquidityCount++; liquidityChange = _liquidityChange; feesAccrued = _feesAccrued; diff --git a/test/position-managers/NativeToken.t.sol b/test/position-managers/NativeToken.t.sol index 26b65081f..0c079f83d 100644 --- a/test/position-managers/NativeToken.t.sol +++ b/test/position-managers/NativeToken.t.sol @@ -32,12 +32,11 @@ import {MockSubscriber} from "../mocks/MockSubscriber.sol"; import {LiquidityFuzzers} from "../shared/fuzz/LiquidityFuzzers.sol"; import {PosmTestSetup} from "../shared/PosmTestSetup.sol"; import {Planner, Plan} from "../shared/Planner.sol"; -import {PositionConfig, PositionConfigLibrary} from "../../src/libraries/PositionConfig.sol"; +import {PositionConfig} from "../../src/libraries/PositionConfig.sol"; contract PositionManagerTest is Test, PosmTestSetup, LiquidityFuzzers { using FixedPointMathLib for uint256; using CurrencyLibrary for Currency; - using PositionConfigLibrary for PositionConfig; using Planner for Plan; using PoolIdLibrary for PoolKey; using StateLibrary for IPoolManager; @@ -782,7 +781,7 @@ contract PositionManagerTest is Test, PosmTestSetup, LiquidityFuzzers { bytes[] memory calls = new bytes[](2); calls[0] = abi.encodeWithSelector(lpm.modifyLiquidities.selector, actions, _deadline); - calls[1] = abi.encodeWithSelector(lpm.subscribe.selector, tokenId, config, sub, ZERO_BYTES); + calls[1] = abi.encodeWithSelector(lpm.subscribe.selector, tokenId, sub, ZERO_BYTES); lpm.multicall{value: 10e18}(calls); diff --git a/test/position-managers/PositionManager.notifier.t.sol b/test/position-managers/PositionManager.notifier.t.sol index 2d8cf44e3..9a88b22aa 100644 --- a/test/position-managers/PositionManager.notifier.t.sol +++ b/test/position-managers/PositionManager.notifier.t.sol @@ -53,7 +53,7 @@ contract PositionManagerNotifierTest is Test, PosmTestSetup, GasSnapshot { function test_subscribe_revertsWithEmptyPositionConfig() public { uint256 tokenId = lpm.nextTokenId(); vm.expectRevert("NOT_MINTED"); - lpm.subscribe(tokenId, config, address(sub), ZERO_BYTES); + lpm.subscribe(tokenId, address(sub), ZERO_BYTES); } function test_subscribe_revertsWhenNotApproved() public { @@ -63,22 +63,7 @@ contract PositionManagerNotifierTest is Test, PosmTestSetup, GasSnapshot { // this contract is not approved to operate on alice's liq vm.expectRevert(abi.encodeWithSelector(IPositionManager.NotApproved.selector, address(this))); - lpm.subscribe(tokenId, config, address(sub), ZERO_BYTES); - } - - function test_subscribe_reverts_withIncorrectConfig() public { - uint256 tokenId = lpm.nextTokenId(); - mint(config, 100e18, alice, ZERO_BYTES); - - // approve this contract to operate on alices liq - vm.startPrank(alice); - lpm.approve(address(this), tokenId); - vm.stopPrank(); - - PositionConfig memory incorrectConfig = PositionConfig({poolKey: key, tickLower: -300, tickUpper: 301}); - - vm.expectRevert(abi.encodeWithSelector(IPositionManager.IncorrectPositionConfigForTokenId.selector, tokenId)); - lpm.subscribe(tokenId, incorrectConfig, address(sub), ZERO_BYTES); + lpm.subscribe(tokenId, address(sub), ZERO_BYTES); } function test_subscribe_succeeds() public { @@ -90,7 +75,7 @@ contract PositionManagerNotifierTest is Test, PosmTestSetup, GasSnapshot { lpm.approve(address(this), tokenId); vm.stopPrank(); - lpm.subscribe(tokenId, config, address(sub), ZERO_BYTES); + lpm.subscribe(tokenId, address(sub), ZERO_BYTES); assertEq(lpm.hasSubscriber(tokenId), true); assertEq(address(lpm.subscriber(tokenId)), address(sub)); @@ -106,7 +91,7 @@ contract PositionManagerNotifierTest is Test, PosmTestSetup, GasSnapshot { lpm.approve(address(this), tokenId); vm.stopPrank(); - lpm.subscribe(tokenId, config, address(sub), ZERO_BYTES); + lpm.subscribe(tokenId, address(sub), ZERO_BYTES); assertEq(lpm.hasSubscriber(tokenId), true); assertEq(address(lpm.subscriber(tokenId)), address(sub)); @@ -140,7 +125,7 @@ contract PositionManagerNotifierTest is Test, PosmTestSetup, GasSnapshot { lpm.approve(address(this), tokenId); vm.stopPrank(); - lpm.subscribe(tokenId, config, address(sub), ZERO_BYTES); + lpm.subscribe(tokenId, address(sub), ZERO_BYTES); assertEq(lpm.hasSubscriber(tokenId), true); assertEq(address(lpm.subscriber(tokenId)), address(sub)); @@ -163,7 +148,7 @@ contract PositionManagerNotifierTest is Test, PosmTestSetup, GasSnapshot { lpm.approve(address(this), tokenId); vm.stopPrank(); - lpm.subscribe(tokenId, config, address(sub), ZERO_BYTES); + lpm.subscribe(tokenId, address(sub), ZERO_BYTES); assertEq(lpm.hasSubscriber(tokenId), true); assertEq(address(lpm.subscriber(tokenId)), address(sub)); @@ -182,7 +167,7 @@ contract PositionManagerNotifierTest is Test, PosmTestSetup, GasSnapshot { lpm.approve(address(this), tokenId); vm.stopPrank(); - lpm.subscribe(tokenId, config, address(sub), ZERO_BYTES); + lpm.subscribe(tokenId, address(sub), ZERO_BYTES); assertEq(lpm.hasSubscriber(tokenId), true); assertEq(address(lpm.subscriber(tokenId)), address(sub)); @@ -201,7 +186,7 @@ contract PositionManagerNotifierTest is Test, PosmTestSetup, GasSnapshot { lpm.approve(address(this), tokenId); vm.stopPrank(); - lpm.subscribe(tokenId, config, address(sub), ZERO_BYTES); + lpm.subscribe(tokenId, address(sub), ZERO_BYTES); assertEq(lpm.hasSubscriber(tokenId), true); assertEq(address(lpm.subscriber(tokenId)), address(sub)); @@ -220,9 +205,9 @@ contract PositionManagerNotifierTest is Test, PosmTestSetup, GasSnapshot { lpm.approve(address(this), tokenId); vm.stopPrank(); - lpm.subscribe(tokenId, config, address(sub), ZERO_BYTES); + lpm.subscribe(tokenId, address(sub), ZERO_BYTES); - lpm.unsubscribe(tokenId, config, ZERO_BYTES); + lpm.unsubscribe(tokenId, ZERO_BYTES); assertEq(sub.notifyUnsubscribeCount(), 1); assertEq(lpm.hasSubscriber(tokenId), false); @@ -238,10 +223,10 @@ contract PositionManagerNotifierTest is Test, PosmTestSetup, GasSnapshot { lpm.approve(address(this), tokenId); vm.stopPrank(); - lpm.subscribe(tokenId, config, address(badSubscriber), ZERO_BYTES); + lpm.subscribe(tokenId, address(badSubscriber), ZERO_BYTES); MockReturnDataSubscriber(badSubscriber).setReturnDataSize(0x600000); - lpm.unsubscribe(tokenId, config, ZERO_BYTES); + lpm.unsubscribe(tokenId, ZERO_BYTES); // the subscriber contract call failed bc it used too much gas assertEq(MockReturnDataSubscriber(badSubscriber).notifyUnsubscribeCount(), 0); @@ -262,7 +247,7 @@ contract PositionManagerNotifierTest is Test, PosmTestSetup, GasSnapshot { bytes[] memory calls = new bytes[](2); calls[0] = abi.encodeWithSelector(lpm.modifyLiquidities.selector, actions, _deadline); - calls[1] = abi.encodeWithSelector(lpm.subscribe.selector, tokenId, config, sub, ZERO_BYTES); + calls[1] = abi.encodeWithSelector(lpm.subscribe.selector, tokenId, sub, ZERO_BYTES); lpm.multicall(calls); @@ -297,7 +282,7 @@ contract PositionManagerNotifierTest is Test, PosmTestSetup, GasSnapshot { bytes[] memory calls = new bytes[](3); calls[0] = abi.encodeWithSelector(lpm.modifyLiquidities.selector, actions, _deadline); - calls[1] = abi.encodeWithSelector(lpm.subscribe.selector, tokenId, config, sub, ZERO_BYTES); + calls[1] = abi.encodeWithSelector(lpm.subscribe.selector, tokenId, sub, ZERO_BYTES); calls[2] = abi.encodeWithSelector(lpm.modifyLiquidities.selector, actions2, _deadline); lpm.multicall(calls); @@ -321,7 +306,7 @@ contract PositionManagerNotifierTest is Test, PosmTestSetup, GasSnapshot { vm.stopPrank(); vm.expectRevert(); - lpm.unsubscribe(tokenId, config, ZERO_BYTES); + lpm.unsubscribe(tokenId, ZERO_BYTES); } function test_subscribe_withData() public { @@ -335,7 +320,7 @@ contract PositionManagerNotifierTest is Test, PosmTestSetup, GasSnapshot { lpm.approve(address(this), tokenId); vm.stopPrank(); - lpm.subscribe(tokenId, config, address(sub), subData); + lpm.subscribe(tokenId, address(sub), subData); assertEq(lpm.hasSubscriber(tokenId), true); assertEq(address(lpm.subscriber(tokenId)), address(sub)); @@ -354,9 +339,9 @@ contract PositionManagerNotifierTest is Test, PosmTestSetup, GasSnapshot { lpm.approve(address(this), tokenId); vm.stopPrank(); - lpm.subscribe(tokenId, config, address(sub), ZERO_BYTES); + lpm.subscribe(tokenId, address(sub), ZERO_BYTES); - lpm.unsubscribe(tokenId, config, subData); + lpm.unsubscribe(tokenId, subData); assertEq(sub.notifyUnsubscribeCount(), 1); assertEq(lpm.hasSubscriber(tokenId), false); @@ -382,7 +367,7 @@ contract PositionManagerNotifierTest is Test, PosmTestSetup, GasSnapshot { abi.encodeWithSelector(MockRevertSubscriber.TestRevert.selector, "notifySubscribe") ) ); - lpm.subscribe(tokenId, config, address(revertSubscriber), ZERO_BYTES); + lpm.subscribe(tokenId, address(revertSubscriber), ZERO_BYTES); } function test_notifyModifyLiquidiy_wraps_revert() public { @@ -394,7 +379,7 @@ contract PositionManagerNotifierTest is Test, PosmTestSetup, GasSnapshot { lpm.approve(address(this), tokenId); vm.stopPrank(); - lpm.subscribe(tokenId, config, address(revertSubscriber), ZERO_BYTES); + lpm.subscribe(tokenId, address(revertSubscriber), ZERO_BYTES); Plan memory plan = Planner.init(); for (uint256 i = 0; i < 10; i++) { @@ -424,7 +409,7 @@ contract PositionManagerNotifierTest is Test, PosmTestSetup, GasSnapshot { lpm.approve(address(this), tokenId); vm.stopPrank(); - lpm.subscribe(tokenId, config, address(revertSubscriber), ZERO_BYTES); + lpm.subscribe(tokenId, address(revertSubscriber), ZERO_BYTES); vm.expectRevert( abi.encodeWithSelector( @@ -445,7 +430,7 @@ contract PositionManagerNotifierTest is Test, PosmTestSetup, GasSnapshot { lpm.approve(address(this), tokenId); vm.stopPrank(); - lpm.subscribe(tokenId, config, address(revertSubscriber), ZERO_BYTES); + lpm.subscribe(tokenId, address(revertSubscriber), ZERO_BYTES); vm.expectRevert( abi.encodeWithSelector( @@ -466,7 +451,7 @@ contract PositionManagerNotifierTest is Test, PosmTestSetup, GasSnapshot { lpm.approve(address(this), tokenId); vm.stopPrank(); - lpm.subscribe(tokenId, config, address(revertSubscriber), ZERO_BYTES); + lpm.subscribe(tokenId, address(revertSubscriber), ZERO_BYTES); vm.expectRevert( abi.encodeWithSelector( From 830fdf53629d979985a795c587517cd30f08ac51 Mon Sep 17 00:00:00 2001 From: Sara Reynolds Date: Wed, 28 Aug 2024 16:27:22 -0400 Subject: [PATCH 04/45] stack too deep, permit error --- .../BaseActionsRouter_mock10commands.snap | 2 +- ...p_settleFromCaller_takeAllToMsgSender.snap | 2 +- ...eFromCaller_takeAllToSpecifiedAddress.snap | 2 +- ..._settleWithBalance_takeAllToMsgSender.snap | 2 +- ...WithBalance_takeAllToSpecifiedAddress.snap | 2 +- .../PositionManager_burn_empty.snap | 2 +- .../PositionManager_burn_empty_native.snap | 2 +- ...anager_burn_nonEmpty_native_withClose.snap | 2 +- ...ger_burn_nonEmpty_native_withTakePair.snap | 2 +- ...sitionManager_burn_nonEmpty_withClose.snap | 2 +- ...ionManager_burn_nonEmpty_withTakePair.snap | 2 +- .../PositionManager_collect_native.snap | 2 +- .../PositionManager_collect_sameRange.snap | 2 +- .../PositionManager_collect_withClose.snap | 2 +- .../PositionManager_collect_withTakePair.snap | 2 +- ...itionManager_decreaseLiquidity_native.snap | 2 +- ...onManager_decreaseLiquidity_withClose.snap | 2 +- ...anager_decreaseLiquidity_withTakePair.snap | 2 +- .../PositionManager_decrease_burnEmpty.snap | 2 +- ...tionManager_decrease_burnEmpty_native.snap | 2 +- ...nager_decrease_sameRange_allLiquidity.snap | 2 +- .../PositionManager_decrease_take_take.snap | 2 +- ...ger_increaseLiquidity_erc20_withClose.snap | 2 +- ...ncreaseLiquidity_erc20_withSettlePair.snap | 2 +- ...itionManager_increaseLiquidity_native.snap | 2 +- ...crease_autocompoundExactUnclaimedFees.snap | 2 +- ...increase_autocompoundExcessFeesCredit.snap | 2 +- ...ger_increase_autocompound_clearExcess.snap | 2 +- .../PositionManager_mint_native.snap | 2 +- ...anager_mint_nativeWithSweep_withClose.snap | 2 +- ...r_mint_nativeWithSweep_withSettlePair.snap | 2 +- .../PositionManager_mint_onSameTickLower.snap | 2 +- .../PositionManager_mint_onSameTickUpper.snap | 2 +- .../PositionManager_mint_sameRange.snap | 2 +- ...nManager_mint_settleWithBalance_sweep.snap | 2 +- ...anager_mint_warmedPool_differentRange.snap | 2 +- .../PositionManager_mint_withClose.snap | 2 +- .../PositionManager_mint_withSettlePair.snap | 2 +- ...tionManager_multicall_initialize_mint.snap | 2 +- .forge-snapshots/PositionManager_permit.snap | 2 +- ...PositionManager_permit_secondPosition.snap | 2 +- .../PositionManager_permit_twice.snap | 2 +- ...tateView_extsload_getFeeGrowthGlobals.snap | 2 +- ...StateView_extsload_getFeeGrowthInside.snap | 2 +- .../StateView_extsload_getLiquidity.snap | 2 +- .../StateView_extsload_getPositionInfo.snap | 2 +- ...ateView_extsload_getPositionLiquidity.snap | 2 +- .../StateView_extsload_getSlot0.snap | 2 +- .../StateView_extsload_getTickBitmap.snap | 2 +- ...View_extsload_getTickFeeGrowthOutside.snap | 2 +- .../StateView_extsload_getTickInfo.snap | 2 +- .../StateView_extsload_getTickLiquidity.snap | 2 +- .forge-snapshots/V4Router_Bytecode.snap | 2 +- .../V4Router_ExactIn1Hop_nativeIn.snap | 2 +- .../V4Router_ExactIn1Hop_nativeOut.snap | 2 +- .../V4Router_ExactIn1Hop_oneForZero.snap | 2 +- .../V4Router_ExactIn1Hop_zeroForOne.snap | 2 +- .forge-snapshots/V4Router_ExactIn2Hops.snap | 2 +- .../V4Router_ExactIn2Hops_nativeIn.snap | 2 +- .forge-snapshots/V4Router_ExactIn3Hops.snap | 2 +- .../V4Router_ExactIn3Hops_nativeIn.snap | 2 +- .../V4Router_ExactInputSingle.snap | 2 +- .../V4Router_ExactInputSingle_nativeIn.snap | 2 +- .../V4Router_ExactInputSingle_nativeOut.snap | 2 +- ...Router_ExactOut1Hop_nativeIn_sweepETH.snap | 2 +- .../V4Router_ExactOut1Hop_nativeOut.snap | 2 +- .../V4Router_ExactOut1Hop_oneForZero.snap | 2 +- .../V4Router_ExactOut1Hop_zeroForOne.snap | 2 +- .forge-snapshots/V4Router_ExactOut2Hops.snap | 2 +- .../V4Router_ExactOut2Hops_nativeIn.snap | 2 +- .forge-snapshots/V4Router_ExactOut3Hops.snap | 2 +- .../V4Router_ExactOut3Hops_nativeIn.snap | 2 +- .../V4Router_ExactOut3Hops_nativeOut.snap | 2 +- .../V4Router_ExactOutputSingle.snap | 2 +- ...r_ExactOutputSingle_nativeIn_sweepETH.snap | 2 +- .../V4Router_ExactOutputSingle_nativeOut.snap | 2 +- src/PositionManager.sol | 152 ++++++++++-------- src/base/Notifier.sol | 3 +- src/interfaces/IPositionManager.sol | 16 +- src/libraries/CalldataDecoder.sol | 11 +- src/libraries/PositionConfig.sol | 2 +- src/libraries/PositionInfoLibrary.sol | 36 +++++ test/erc721Permit/ERC721Permit.permit.t.sol | 3 +- test/libraries/CalldataDecoder.t.sol | 19 ++- test/mocks/MockCalldataDecoder.sol | 5 +- test/position-managers/Execute.t.sol | 20 ++- .../position-managers/IncreaseLiquidity.t.sol | 26 ++- test/position-managers/NativeToken.t.sol | 60 ++++--- test/position-managers/Permit.t.sol | 8 +- .../PositionManager.gas.t.sol | 64 ++++++-- .../PositionManager.modifyLiquidities.t.sol | 12 +- .../PositionManager.multicall.t.sol | 15 +- .../PositionManager.notifier.t.sol | 26 ++- test/position-managers/PositionManager.t.sol | 51 +++--- test/shared/LiquidityOperations.sol | 14 +- test/shared/fuzz/LiquidityFuzzers.sol | 4 +- 96 files changed, 450 insertions(+), 249 deletions(-) create mode 100644 src/libraries/PositionInfoLibrary.sol diff --git a/.forge-snapshots/BaseActionsRouter_mock10commands.snap b/.forge-snapshots/BaseActionsRouter_mock10commands.snap index 2b46e583b..7ee3d714b 100644 --- a/.forge-snapshots/BaseActionsRouter_mock10commands.snap +++ b/.forge-snapshots/BaseActionsRouter_mock10commands.snap @@ -1 +1 @@ -61749 \ No newline at end of file +59216 \ No newline at end of file diff --git a/.forge-snapshots/Payments_swap_settleFromCaller_takeAllToMsgSender.snap b/.forge-snapshots/Payments_swap_settleFromCaller_takeAllToMsgSender.snap index 8f5800369..02231759c 100644 --- a/.forge-snapshots/Payments_swap_settleFromCaller_takeAllToMsgSender.snap +++ b/.forge-snapshots/Payments_swap_settleFromCaller_takeAllToMsgSender.snap @@ -1 +1 @@ -133206 \ No newline at end of file +130322 \ No newline at end of file diff --git a/.forge-snapshots/Payments_swap_settleFromCaller_takeAllToSpecifiedAddress.snap b/.forge-snapshots/Payments_swap_settleFromCaller_takeAllToSpecifiedAddress.snap index 0356d3854..108caf432 100644 --- a/.forge-snapshots/Payments_swap_settleFromCaller_takeAllToSpecifiedAddress.snap +++ b/.forge-snapshots/Payments_swap_settleFromCaller_takeAllToSpecifiedAddress.snap @@ -1 +1 @@ -135242 \ No newline at end of file +132215 \ No newline at end of file diff --git a/.forge-snapshots/Payments_swap_settleWithBalance_takeAllToMsgSender.snap b/.forge-snapshots/Payments_swap_settleWithBalance_takeAllToMsgSender.snap index 8f190ac59..7f1934bc8 100644 --- a/.forge-snapshots/Payments_swap_settleWithBalance_takeAllToMsgSender.snap +++ b/.forge-snapshots/Payments_swap_settleWithBalance_takeAllToMsgSender.snap @@ -1 +1 @@ -127716 \ No newline at end of file +124449 \ No newline at end of file diff --git a/.forge-snapshots/Payments_swap_settleWithBalance_takeAllToSpecifiedAddress.snap b/.forge-snapshots/Payments_swap_settleWithBalance_takeAllToSpecifiedAddress.snap index f7b6cea9b..48033d8d2 100644 --- a/.forge-snapshots/Payments_swap_settleWithBalance_takeAllToSpecifiedAddress.snap +++ b/.forge-snapshots/Payments_swap_settleWithBalance_takeAllToSpecifiedAddress.snap @@ -1 +1 @@ -127854 \ No newline at end of file +124591 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_burn_empty.snap b/.forge-snapshots/PositionManager_burn_empty.snap index a4dc34679..cc2a0bf38 100644 --- a/.forge-snapshots/PositionManager_burn_empty.snap +++ b/.forge-snapshots/PositionManager_burn_empty.snap @@ -1 +1 @@ -50221 \ No newline at end of file +50624 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_burn_empty_native.snap b/.forge-snapshots/PositionManager_burn_empty_native.snap index a4dc34679..cc2a0bf38 100644 --- a/.forge-snapshots/PositionManager_burn_empty_native.snap +++ b/.forge-snapshots/PositionManager_burn_empty_native.snap @@ -1 +1 @@ -50221 \ No newline at end of file +50624 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_burn_nonEmpty_native_withClose.snap b/.forge-snapshots/PositionManager_burn_nonEmpty_native_withClose.snap index be563cf8c..641f713bc 100644 --- a/.forge-snapshots/PositionManager_burn_nonEmpty_native_withClose.snap +++ b/.forge-snapshots/PositionManager_burn_nonEmpty_native_withClose.snap @@ -1 +1 @@ -126586 \ No newline at end of file +125769 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_burn_nonEmpty_native_withTakePair.snap b/.forge-snapshots/PositionManager_burn_nonEmpty_native_withTakePair.snap index 823c6fc90..04960f34a 100644 --- a/.forge-snapshots/PositionManager_burn_nonEmpty_native_withTakePair.snap +++ b/.forge-snapshots/PositionManager_burn_nonEmpty_native_withTakePair.snap @@ -1 +1 @@ -126084 \ No newline at end of file +125292 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_burn_nonEmpty_withClose.snap b/.forge-snapshots/PositionManager_burn_nonEmpty_withClose.snap index a83b28e77..bc35833dc 100644 --- a/.forge-snapshots/PositionManager_burn_nonEmpty_withClose.snap +++ b/.forge-snapshots/PositionManager_burn_nonEmpty_withClose.snap @@ -1 +1 @@ -133482 \ No newline at end of file +132620 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_burn_nonEmpty_withTakePair.snap b/.forge-snapshots/PositionManager_burn_nonEmpty_withTakePair.snap index 50e6cd8eb..38dcf607e 100644 --- a/.forge-snapshots/PositionManager_burn_nonEmpty_withTakePair.snap +++ b/.forge-snapshots/PositionManager_burn_nonEmpty_withTakePair.snap @@ -1 +1 @@ -132980 \ No newline at end of file +132143 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_collect_native.snap b/.forge-snapshots/PositionManager_collect_native.snap index 2679b16b5..8470abb8d 100644 --- a/.forge-snapshots/PositionManager_collect_native.snap +++ b/.forge-snapshots/PositionManager_collect_native.snap @@ -1 +1 @@ -146839 \ No newline at end of file +146332 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_collect_sameRange.snap b/.forge-snapshots/PositionManager_collect_sameRange.snap index 872ac116b..2155cfda8 100644 --- a/.forge-snapshots/PositionManager_collect_sameRange.snap +++ b/.forge-snapshots/PositionManager_collect_sameRange.snap @@ -1 +1 @@ -155459 \ No newline at end of file +154895 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_collect_withClose.snap b/.forge-snapshots/PositionManager_collect_withClose.snap index 872ac116b..2155cfda8 100644 --- a/.forge-snapshots/PositionManager_collect_withClose.snap +++ b/.forge-snapshots/PositionManager_collect_withClose.snap @@ -1 +1 @@ -155459 \ No newline at end of file +154895 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_collect_withTakePair.snap b/.forge-snapshots/PositionManager_collect_withTakePair.snap index 236cd2f3f..4ffa46d23 100644 --- a/.forge-snapshots/PositionManager_collect_withTakePair.snap +++ b/.forge-snapshots/PositionManager_collect_withTakePair.snap @@ -1 +1 @@ -154843 \ No newline at end of file +154311 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_decreaseLiquidity_native.snap b/.forge-snapshots/PositionManager_decreaseLiquidity_native.snap index fad7910bd..acf25f342 100644 --- a/.forge-snapshots/PositionManager_decreaseLiquidity_native.snap +++ b/.forge-snapshots/PositionManager_decreaseLiquidity_native.snap @@ -1 +1 @@ -112946 \ No newline at end of file +112030 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_decreaseLiquidity_withClose.snap b/.forge-snapshots/PositionManager_decreaseLiquidity_withClose.snap index fa15c9567..cb6ebd61b 100644 --- a/.forge-snapshots/PositionManager_decreaseLiquidity_withClose.snap +++ b/.forge-snapshots/PositionManager_decreaseLiquidity_withClose.snap @@ -1 +1 @@ -121002 \ No newline at end of file +119800 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_decreaseLiquidity_withTakePair.snap b/.forge-snapshots/PositionManager_decreaseLiquidity_withTakePair.snap index aa85e5ecc..32821d90a 100644 --- a/.forge-snapshots/PositionManager_decreaseLiquidity_withTakePair.snap +++ b/.forge-snapshots/PositionManager_decreaseLiquidity_withTakePair.snap @@ -1 +1 @@ -120386 \ No newline at end of file +119216 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_decrease_burnEmpty.snap b/.forge-snapshots/PositionManager_decrease_burnEmpty.snap index be5018bb5..68c1d4dff 100644 --- a/.forge-snapshots/PositionManager_decrease_burnEmpty.snap +++ b/.forge-snapshots/PositionManager_decrease_burnEmpty.snap @@ -1 +1 @@ -136611 \ No newline at end of file +135671 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_decrease_burnEmpty_native.snap b/.forge-snapshots/PositionManager_decrease_burnEmpty_native.snap index 6bf2eef7d..21a9fb028 100644 --- a/.forge-snapshots/PositionManager_decrease_burnEmpty_native.snap +++ b/.forge-snapshots/PositionManager_decrease_burnEmpty_native.snap @@ -1 +1 @@ -129715 \ No newline at end of file +128820 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_decrease_sameRange_allLiquidity.snap b/.forge-snapshots/PositionManager_decrease_sameRange_allLiquidity.snap index 95a6b4a79..8f0b97d20 100644 --- a/.forge-snapshots/PositionManager_decrease_sameRange_allLiquidity.snap +++ b/.forge-snapshots/PositionManager_decrease_sameRange_allLiquidity.snap @@ -1 +1 @@ -133718 \ No newline at end of file +132499 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_decrease_take_take.snap b/.forge-snapshots/PositionManager_decrease_take_take.snap index 769a445d4..94bad2417 100644 --- a/.forge-snapshots/PositionManager_decrease_take_take.snap +++ b/.forge-snapshots/PositionManager_decrease_take_take.snap @@ -1 +1 @@ -121535 \ No newline at end of file +120384 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_increaseLiquidity_erc20_withClose.snap b/.forge-snapshots/PositionManager_increaseLiquidity_erc20_withClose.snap index 244e0071d..5d1d75100 100644 --- a/.forge-snapshots/PositionManager_increaseLiquidity_erc20_withClose.snap +++ b/.forge-snapshots/PositionManager_increaseLiquidity_erc20_withClose.snap @@ -1 +1 @@ -159709 \ No newline at end of file +159263 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_increaseLiquidity_erc20_withSettlePair.snap b/.forge-snapshots/PositionManager_increaseLiquidity_erc20_withSettlePair.snap index f01d0684c..60025655d 100644 --- a/.forge-snapshots/PositionManager_increaseLiquidity_erc20_withSettlePair.snap +++ b/.forge-snapshots/PositionManager_increaseLiquidity_erc20_withSettlePair.snap @@ -1 +1 @@ -158735 \ No newline at end of file +158313 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_increaseLiquidity_native.snap b/.forge-snapshots/PositionManager_increaseLiquidity_native.snap index 5a2c5c6ca..f8c4dc4e6 100644 --- a/.forge-snapshots/PositionManager_increaseLiquidity_native.snap +++ b/.forge-snapshots/PositionManager_increaseLiquidity_native.snap @@ -1 +1 @@ -141737 \ No newline at end of file +141064 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_increase_autocompoundExactUnclaimedFees.snap b/.forge-snapshots/PositionManager_increase_autocompoundExactUnclaimedFees.snap index 61b699427..c4be3a91b 100644 --- a/.forge-snapshots/PositionManager_increase_autocompoundExactUnclaimedFees.snap +++ b/.forge-snapshots/PositionManager_increase_autocompoundExactUnclaimedFees.snap @@ -1 +1 @@ -137854 \ No newline at end of file +136744 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_increase_autocompoundExcessFeesCredit.snap b/.forge-snapshots/PositionManager_increase_autocompoundExcessFeesCredit.snap index 7b28887a9..8030bc024 100644 --- a/.forge-snapshots/PositionManager_increase_autocompoundExcessFeesCredit.snap +++ b/.forge-snapshots/PositionManager_increase_autocompoundExcessFeesCredit.snap @@ -1 +1 @@ -178770 \ No newline at end of file +177518 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_increase_autocompound_clearExcess.snap b/.forge-snapshots/PositionManager_increase_autocompound_clearExcess.snap index 7050be040..9a61b60ec 100644 --- a/.forge-snapshots/PositionManager_increase_autocompound_clearExcess.snap +++ b/.forge-snapshots/PositionManager_increase_autocompound_clearExcess.snap @@ -1 +1 @@ -148726 \ No newline at end of file +148234 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_mint_native.snap b/.forge-snapshots/PositionManager_mint_native.snap index 8ce6c1b53..f010f2200 100644 --- a/.forge-snapshots/PositionManager_mint_native.snap +++ b/.forge-snapshots/PositionManager_mint_native.snap @@ -1 +1 @@ -370766 \ No newline at end of file +365281 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_mint_nativeWithSweep_withClose.snap b/.forge-snapshots/PositionManager_mint_nativeWithSweep_withClose.snap index cfbfa2d12..cefadff88 100644 --- a/.forge-snapshots/PositionManager_mint_nativeWithSweep_withClose.snap +++ b/.forge-snapshots/PositionManager_mint_nativeWithSweep_withClose.snap @@ -1 +1 @@ -379258 \ No newline at end of file +373716 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_mint_nativeWithSweep_withSettlePair.snap b/.forge-snapshots/PositionManager_mint_nativeWithSweep_withSettlePair.snap index e619bc49b..431fb88c0 100644 --- a/.forge-snapshots/PositionManager_mint_nativeWithSweep_withSettlePair.snap +++ b/.forge-snapshots/PositionManager_mint_nativeWithSweep_withSettlePair.snap @@ -1 +1 @@ -378560 \ No newline at end of file +373050 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_mint_onSameTickLower.snap b/.forge-snapshots/PositionManager_mint_onSameTickLower.snap index 3e24b7f4d..a2df27ddb 100644 --- a/.forge-snapshots/PositionManager_mint_onSameTickLower.snap +++ b/.forge-snapshots/PositionManager_mint_onSameTickLower.snap @@ -1 +1 @@ -327399 \ No newline at end of file +322160 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_mint_onSameTickUpper.snap b/.forge-snapshots/PositionManager_mint_onSameTickUpper.snap index 90549c24e..5ca4c7a30 100644 --- a/.forge-snapshots/PositionManager_mint_onSameTickUpper.snap +++ b/.forge-snapshots/PositionManager_mint_onSameTickUpper.snap @@ -1 +1 @@ -328041 \ No newline at end of file +322827 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_mint_sameRange.snap b/.forge-snapshots/PositionManager_mint_sameRange.snap index f4d8890df..1a08055af 100644 --- a/.forge-snapshots/PositionManager_mint_sameRange.snap +++ b/.forge-snapshots/PositionManager_mint_sameRange.snap @@ -1 +1 @@ -253623 \ No newline at end of file +248405 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_mint_settleWithBalance_sweep.snap b/.forge-snapshots/PositionManager_mint_settleWithBalance_sweep.snap index 2e3876632..20ba98567 100644 --- a/.forge-snapshots/PositionManager_mint_settleWithBalance_sweep.snap +++ b/.forge-snapshots/PositionManager_mint_settleWithBalance_sweep.snap @@ -1 +1 @@ -424696 \ No newline at end of file +419426 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_mint_warmedPool_differentRange.snap b/.forge-snapshots/PositionManager_mint_warmedPool_differentRange.snap index 27db49c26..57c609caa 100644 --- a/.forge-snapshots/PositionManager_mint_warmedPool_differentRange.snap +++ b/.forge-snapshots/PositionManager_mint_warmedPool_differentRange.snap @@ -1 +1 @@ -333417 \ No newline at end of file +328182 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_mint_withClose.snap b/.forge-snapshots/PositionManager_mint_withClose.snap index 4db6eaef2..070eccb44 100644 --- a/.forge-snapshots/PositionManager_mint_withClose.snap +++ b/.forge-snapshots/PositionManager_mint_withClose.snap @@ -1 +1 @@ -425966 \ No newline at end of file +420708 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_mint_withSettlePair.snap b/.forge-snapshots/PositionManager_mint_withSettlePair.snap index 2e62d8631..ef82db672 100644 --- a/.forge-snapshots/PositionManager_mint_withSettlePair.snap +++ b/.forge-snapshots/PositionManager_mint_withSettlePair.snap @@ -1 +1 @@ -425106 \ No newline at end of file +419876 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_multicall_initialize_mint.snap b/.forge-snapshots/PositionManager_multicall_initialize_mint.snap index 145400306..682fa5e9d 100644 --- a/.forge-snapshots/PositionManager_multicall_initialize_mint.snap +++ b/.forge-snapshots/PositionManager_multicall_initialize_mint.snap @@ -1 +1 @@ -470411 \ No newline at end of file +464902 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_permit.snap b/.forge-snapshots/PositionManager_permit.snap index 81715a673..3133c57c0 100644 --- a/.forge-snapshots/PositionManager_permit.snap +++ b/.forge-snapshots/PositionManager_permit.snap @@ -1 +1 @@ -79484 \ No newline at end of file +79089 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_permit_secondPosition.snap b/.forge-snapshots/PositionManager_permit_secondPosition.snap index bd214aa76..ac80e5b2f 100644 --- a/.forge-snapshots/PositionManager_permit_secondPosition.snap +++ b/.forge-snapshots/PositionManager_permit_secondPosition.snap @@ -1 +1 @@ -62372 \ No newline at end of file +61977 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_permit_twice.snap b/.forge-snapshots/PositionManager_permit_twice.snap index c0e053f63..2a4903c78 100644 --- a/.forge-snapshots/PositionManager_permit_twice.snap +++ b/.forge-snapshots/PositionManager_permit_twice.snap @@ -1 +1 @@ -45260 \ No newline at end of file +44865 \ No newline at end of file diff --git a/.forge-snapshots/StateView_extsload_getFeeGrowthGlobals.snap b/.forge-snapshots/StateView_extsload_getFeeGrowthGlobals.snap index d659d28b1..920727d2e 100644 --- a/.forge-snapshots/StateView_extsload_getFeeGrowthGlobals.snap +++ b/.forge-snapshots/StateView_extsload_getFeeGrowthGlobals.snap @@ -1 +1 @@ -2376 \ No newline at end of file +2256 \ No newline at end of file diff --git a/.forge-snapshots/StateView_extsload_getFeeGrowthInside.snap b/.forge-snapshots/StateView_extsload_getFeeGrowthInside.snap index 37017731e..6df672447 100644 --- a/.forge-snapshots/StateView_extsload_getFeeGrowthInside.snap +++ b/.forge-snapshots/StateView_extsload_getFeeGrowthInside.snap @@ -1 +1 @@ -8455 \ No newline at end of file +7994 \ No newline at end of file diff --git a/.forge-snapshots/StateView_extsload_getLiquidity.snap b/.forge-snapshots/StateView_extsload_getLiquidity.snap index 5ca63cd4c..a900d0b24 100644 --- a/.forge-snapshots/StateView_extsload_getLiquidity.snap +++ b/.forge-snapshots/StateView_extsload_getLiquidity.snap @@ -1 +1 @@ -1487 \ No newline at end of file +1399 \ No newline at end of file diff --git a/.forge-snapshots/StateView_extsload_getPositionInfo.snap b/.forge-snapshots/StateView_extsload_getPositionInfo.snap index 6f9954563..c173e591f 100644 --- a/.forge-snapshots/StateView_extsload_getPositionInfo.snap +++ b/.forge-snapshots/StateView_extsload_getPositionInfo.snap @@ -1 +1 @@ -2905 \ No newline at end of file +2826 \ No newline at end of file diff --git a/.forge-snapshots/StateView_extsload_getPositionLiquidity.snap b/.forge-snapshots/StateView_extsload_getPositionLiquidity.snap index 2dd447630..7f3589da4 100644 --- a/.forge-snapshots/StateView_extsload_getPositionLiquidity.snap +++ b/.forge-snapshots/StateView_extsload_getPositionLiquidity.snap @@ -1 +1 @@ -1724 \ No newline at end of file +1651 \ No newline at end of file diff --git a/.forge-snapshots/StateView_extsload_getSlot0.snap b/.forge-snapshots/StateView_extsload_getSlot0.snap index a35ae7300..585cf67a0 100644 --- a/.forge-snapshots/StateView_extsload_getSlot0.snap +++ b/.forge-snapshots/StateView_extsload_getSlot0.snap @@ -1 +1 @@ -1584 \ No newline at end of file +1446 \ No newline at end of file diff --git a/.forge-snapshots/StateView_extsload_getTickBitmap.snap b/.forge-snapshots/StateView_extsload_getTickBitmap.snap index 7f29ad284..05fd8282c 100644 --- a/.forge-snapshots/StateView_extsload_getTickBitmap.snap +++ b/.forge-snapshots/StateView_extsload_getTickBitmap.snap @@ -1 +1 @@ -1682 \ No newline at end of file +1392 \ No newline at end of file diff --git a/.forge-snapshots/StateView_extsload_getTickFeeGrowthOutside.snap b/.forge-snapshots/StateView_extsload_getTickFeeGrowthOutside.snap index 00c1535ae..7a03b74c0 100644 --- a/.forge-snapshots/StateView_extsload_getTickFeeGrowthOutside.snap +++ b/.forge-snapshots/StateView_extsload_getTickFeeGrowthOutside.snap @@ -1 +1 @@ -2734 \ No newline at end of file +2543 \ No newline at end of file diff --git a/.forge-snapshots/StateView_extsload_getTickInfo.snap b/.forge-snapshots/StateView_extsload_getTickInfo.snap index a6fea8d18..f538595cb 100644 --- a/.forge-snapshots/StateView_extsload_getTickInfo.snap +++ b/.forge-snapshots/StateView_extsload_getTickInfo.snap @@ -1 +1 @@ -3068 \ No newline at end of file +2758 \ No newline at end of file diff --git a/.forge-snapshots/StateView_extsload_getTickLiquidity.snap b/.forge-snapshots/StateView_extsload_getTickLiquidity.snap index fdef1d023..44e788259 100644 --- a/.forge-snapshots/StateView_extsload_getTickLiquidity.snap +++ b/.forge-snapshots/StateView_extsload_getTickLiquidity.snap @@ -1 +1 @@ -1879 \ No newline at end of file +1646 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_Bytecode.snap b/.forge-snapshots/V4Router_Bytecode.snap index e88f6975d..e7ebb4512 100644 --- a/.forge-snapshots/V4Router_Bytecode.snap +++ b/.forge-snapshots/V4Router_Bytecode.snap @@ -1 +1 @@ -8389 \ No newline at end of file +6754 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactIn1Hop_nativeIn.snap b/.forge-snapshots/V4Router_ExactIn1Hop_nativeIn.snap index ce9be6341..a803c6eeb 100644 --- a/.forge-snapshots/V4Router_ExactIn1Hop_nativeIn.snap +++ b/.forge-snapshots/V4Router_ExactIn1Hop_nativeIn.snap @@ -1 +1 @@ -119501 \ No newline at end of file +116128 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactIn1Hop_nativeOut.snap b/.forge-snapshots/V4Router_ExactIn1Hop_nativeOut.snap index 726ce00a0..2619b0057 100644 --- a/.forge-snapshots/V4Router_ExactIn1Hop_nativeOut.snap +++ b/.forge-snapshots/V4Router_ExactIn1Hop_nativeOut.snap @@ -1 +1 @@ -118696 \ No newline at end of file +116103 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactIn1Hop_oneForZero.snap b/.forge-snapshots/V4Router_ExactIn1Hop_oneForZero.snap index 4cda0de8d..8050eab6e 100644 --- a/.forge-snapshots/V4Router_ExactIn1Hop_oneForZero.snap +++ b/.forge-snapshots/V4Router_ExactIn1Hop_oneForZero.snap @@ -1 +1 @@ -127568 \ No newline at end of file +124918 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactIn1Hop_zeroForOne.snap b/.forge-snapshots/V4Router_ExactIn1Hop_zeroForOne.snap index 988f465e4..edac16407 100644 --- a/.forge-snapshots/V4Router_ExactIn1Hop_zeroForOne.snap +++ b/.forge-snapshots/V4Router_ExactIn1Hop_zeroForOne.snap @@ -1 +1 @@ -134398 \ No newline at end of file +131016 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactIn2Hops.snap b/.forge-snapshots/V4Router_ExactIn2Hops.snap index 0ec74b474..e120153ac 100644 --- a/.forge-snapshots/V4Router_ExactIn2Hops.snap +++ b/.forge-snapshots/V4Router_ExactIn2Hops.snap @@ -1 +1 @@ -185939 \ No newline at end of file +180668 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactIn2Hops_nativeIn.snap b/.forge-snapshots/V4Router_ExactIn2Hops_nativeIn.snap index 1ce5a1797..8cf17fbf2 100644 --- a/.forge-snapshots/V4Router_ExactIn2Hops_nativeIn.snap +++ b/.forge-snapshots/V4Router_ExactIn2Hops_nativeIn.snap @@ -1 +1 @@ -177874 \ No newline at end of file +171870 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactIn3Hops.snap b/.forge-snapshots/V4Router_ExactIn3Hops.snap index 96e4b6a49..65dc4c51a 100644 --- a/.forge-snapshots/V4Router_ExactIn3Hops.snap +++ b/.forge-snapshots/V4Router_ExactIn3Hops.snap @@ -1 +1 @@ -237461 \ No newline at end of file +230299 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactIn3Hops_nativeIn.snap b/.forge-snapshots/V4Router_ExactIn3Hops_nativeIn.snap index 456844ad2..95c9cf6cd 100644 --- a/.forge-snapshots/V4Router_ExactIn3Hops_nativeIn.snap +++ b/.forge-snapshots/V4Router_ExactIn3Hops_nativeIn.snap @@ -1 +1 @@ -229420 \ No newline at end of file +221525 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactInputSingle.snap b/.forge-snapshots/V4Router_ExactInputSingle.snap index 8f5800369..02231759c 100644 --- a/.forge-snapshots/V4Router_ExactInputSingle.snap +++ b/.forge-snapshots/V4Router_ExactInputSingle.snap @@ -1 +1 @@ -133206 \ No newline at end of file +130322 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactInputSingle_nativeIn.snap b/.forge-snapshots/V4Router_ExactInputSingle_nativeIn.snap index e8ecc585d..674bbdcff 100644 --- a/.forge-snapshots/V4Router_ExactInputSingle_nativeIn.snap +++ b/.forge-snapshots/V4Router_ExactInputSingle_nativeIn.snap @@ -1 +1 @@ -118309 \ No newline at end of file +115434 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactInputSingle_nativeOut.snap b/.forge-snapshots/V4Router_ExactInputSingle_nativeOut.snap index 06d583087..859f9eedd 100644 --- a/.forge-snapshots/V4Router_ExactInputSingle_nativeOut.snap +++ b/.forge-snapshots/V4Router_ExactInputSingle_nativeOut.snap @@ -1 +1 @@ -117482 \ No newline at end of file +115378 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactOut1Hop_nativeIn_sweepETH.snap b/.forge-snapshots/V4Router_ExactOut1Hop_nativeIn_sweepETH.snap index 33a2bacbf..3b3f20e07 100644 --- a/.forge-snapshots/V4Router_ExactOut1Hop_nativeIn_sweepETH.snap +++ b/.forge-snapshots/V4Router_ExactOut1Hop_nativeIn_sweepETH.snap @@ -1 +1 @@ -125230 \ No newline at end of file +121957 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactOut1Hop_nativeOut.snap b/.forge-snapshots/V4Router_ExactOut1Hop_nativeOut.snap index 089e6949e..ba60eed24 100644 --- a/.forge-snapshots/V4Router_ExactOut1Hop_nativeOut.snap +++ b/.forge-snapshots/V4Router_ExactOut1Hop_nativeOut.snap @@ -1 +1 @@ -119508 \ No newline at end of file +116833 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactOut1Hop_oneForZero.snap b/.forge-snapshots/V4Router_ExactOut1Hop_oneForZero.snap index 5cbd7cccc..19bf34fcd 100644 --- a/.forge-snapshots/V4Router_ExactOut1Hop_oneForZero.snap +++ b/.forge-snapshots/V4Router_ExactOut1Hop_oneForZero.snap @@ -1 +1 @@ -128380 \ No newline at end of file +125648 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactOut1Hop_zeroForOne.snap b/.forge-snapshots/V4Router_ExactOut1Hop_zeroForOne.snap index 5c8ee5092..e92853a80 100644 --- a/.forge-snapshots/V4Router_ExactOut1Hop_zeroForOne.snap +++ b/.forge-snapshots/V4Router_ExactOut1Hop_zeroForOne.snap @@ -1 +1 @@ -133181 \ No newline at end of file +129868 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactOut2Hops.snap b/.forge-snapshots/V4Router_ExactOut2Hops.snap index a3913eb58..5bae3b549 100644 --- a/.forge-snapshots/V4Router_ExactOut2Hops.snap +++ b/.forge-snapshots/V4Router_ExactOut2Hops.snap @@ -1 +1 @@ -185325 \ No newline at end of file +180036 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactOut2Hops_nativeIn.snap b/.forge-snapshots/V4Router_ExactOut2Hops_nativeIn.snap index cb7a05225..c0bfb8163 100644 --- a/.forge-snapshots/V4Router_ExactOut2Hops_nativeIn.snap +++ b/.forge-snapshots/V4Router_ExactOut2Hops_nativeIn.snap @@ -1 +1 @@ -182175 \ No newline at end of file +176345 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactOut3Hops.snap b/.forge-snapshots/V4Router_ExactOut3Hops.snap index dbc8b9428..5722b5d29 100644 --- a/.forge-snapshots/V4Router_ExactOut3Hops.snap +++ b/.forge-snapshots/V4Router_ExactOut3Hops.snap @@ -1 +1 @@ -237465 \ No newline at end of file +230211 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactOut3Hops_nativeIn.snap b/.forge-snapshots/V4Router_ExactOut3Hops_nativeIn.snap index a0c789c34..e451e009e 100644 --- a/.forge-snapshots/V4Router_ExactOut3Hops_nativeIn.snap +++ b/.forge-snapshots/V4Router_ExactOut3Hops_nativeIn.snap @@ -1 +1 @@ -234339 \ No newline at end of file +226544 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactOut3Hops_nativeOut.snap b/.forge-snapshots/V4Router_ExactOut3Hops_nativeOut.snap index 68126bb6a..c4eafcb0e 100644 --- a/.forge-snapshots/V4Router_ExactOut3Hops_nativeOut.snap +++ b/.forge-snapshots/V4Router_ExactOut3Hops_nativeOut.snap @@ -1 +1 @@ -228617 \ No newline at end of file +221420 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactOutputSingle.snap b/.forge-snapshots/V4Router_ExactOutputSingle.snap index 91008315b..3e18bc561 100644 --- a/.forge-snapshots/V4Router_ExactOutputSingle.snap +++ b/.forge-snapshots/V4Router_ExactOutputSingle.snap @@ -1 +1 @@ -131978 \ No newline at end of file +129130 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactOutputSingle_nativeIn_sweepETH.snap b/.forge-snapshots/V4Router_ExactOutputSingle_nativeIn_sweepETH.snap index 7a8a1df5a..03a0980e2 100644 --- a/.forge-snapshots/V4Router_ExactOutputSingle_nativeIn_sweepETH.snap +++ b/.forge-snapshots/V4Router_ExactOutputSingle_nativeIn_sweepETH.snap @@ -1 +1 @@ -124027 \ No newline at end of file +121219 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactOutputSingle_nativeOut.snap b/.forge-snapshots/V4Router_ExactOutputSingle_nativeOut.snap index 0db6b17af..7181adfd8 100644 --- a/.forge-snapshots/V4Router_ExactOutputSingle_nativeOut.snap +++ b/.forge-snapshots/V4Router_ExactOutputSingle_nativeOut.snap @@ -1 +1 @@ -118318 \ No newline at end of file +116170 \ No newline at end of file diff --git a/src/PositionManager.sol b/src/PositionManager.sol index c3285c4df..94ff1d5a7 100644 --- a/src/PositionManager.sol +++ b/src/PositionManager.sol @@ -20,7 +20,6 @@ import {IPositionManager} from "./interfaces/IPositionManager.sol"; import {Multicall_v4} from "./base/Multicall_v4.sol"; import {PoolInitializer} from "./base/PoolInitializer.sol"; import {DeltaResolver} from "./base/DeltaResolver.sol"; -import {PositionConfig} from "./libraries/PositionConfig.sol"; import {BaseActionsRouter} from "./base/BaseActionsRouter.sol"; import {Actions} from "./libraries/Actions.sol"; import {Notifier} from "./base/Notifier.sol"; @@ -29,6 +28,7 @@ import {INotifier} from "./interfaces/INotifier.sol"; import {Permit2Forwarder} from "./base/Permit2Forwarder.sol"; import {SlippageCheckLibrary} from "./libraries/SlippageCheck.sol"; import {PoolKeyChecker} from "./libraries/PoolKeyChecker.sol"; +import {PositionInfo, PositionInfoLibrary} from "./libraries/PositionInfoLibrary.sol"; // 444444444 // 444444444444 444444 @@ -121,17 +121,8 @@ contract PositionManager is /// @dev The ID of the next token that will be minted. Skips 0 uint256 public nextTokenId = 1; - // TODO: Move to custom type, packed memory. - struct PositionInfo { - // lower 25 bytes of the poolId - bytes25 poolId; - int24 tickLower; - int24 tickUpper; - bool hasSubscriber; - } - - mapping(uint256 tokenId => PositionInfo info) internal positionInfo; - mapping(bytes32 poolId => PoolKey poolKey) internal poolKeys; + mapping(uint256 tokenId => PositionInfo info) public positionInfo; + mapping(bytes25 poolId => PoolKey poolKey) public poolKeys; /// @notice an internal getter for PositionInfo to be used by Notifier function _positionInfo(uint256 tokenId) internal view override returns (PositionInfo storage) { @@ -196,14 +187,16 @@ contract PositionManager is _decrease(tokenId, liquidity, amount0Min, amount1Min, hookData); } else if (action == Actions.MINT_POSITION) { ( - PositionConfig calldata config, + PoolKey calldata poolKey, + int24 tickLower, + int24 tickUpper, uint256 liquidity, uint128 amount0Max, uint128 amount1Max, address owner, bytes calldata hookData ) = params.decodeMintParams(); - _mint(config, liquidity, amount0Max, amount1Max, _mapRecipient(owner), hookData); + _mint(poolKey, tickLower, tickUpper, liquidity, amount0Max, amount1Max, _mapRecipient(owner), hookData); } else if (action == Actions.BURN_POSITION) { // Will automatically decrease liquidity to 0 if the position is not already empty. (uint256 tokenId, uint128 amount0Min, uint128 amount1Min, bytes calldata hookData) = @@ -248,13 +241,20 @@ contract PositionManager is uint128 amount1Max, bytes calldata hookData ) internal onlyIfApproved(msgSender(), tokenId) { - PositionInfo memory info = positionInfo[tokenId]; - PositionConfig memory config = _toConfig(info); + (PositionInfo memory info, PoolKey memory poolKey) = getPoolPositionInfo(tokenId); + + int256 liquidityChange = liquidity.toInt256(); // Note: The tokenId is used as the salt for this position, so every minted position has unique storage in the pool manager. - (BalanceDelta liquidityDelta, BalanceDelta feesAccrued) = - _modifyLiquidity(config, liquidity.toInt256(), bytes32(tokenId), hookData); + (BalanceDelta liquidityDelta, BalanceDelta feesAccrued) = poolManager.modifyLiquidity( + poolKey, _toParams(info.tickLower, info.tickUpper, liquidityChange, bytes32(tokenId)), hookData + ); + // Slippage checks should be done on the principal liquidityDelta which is the liquidityDelta - feesAccrued (liquidityDelta - feesAccrued).validateMaxIn(amount0Max, amount1Max); + + if (info.hasSubscriber) { + _notifyModifyLiquidity(uint256(tokenId), liquidityChange, feesAccrued); + } } /// @dev Calling decrease with 0 liquidity will credit the caller with any underlying fees of the position @@ -265,17 +265,27 @@ contract PositionManager is uint128 amount1Min, bytes calldata hookData ) internal onlyIfApproved(msgSender(), tokenId) { - PositionInfo memory info = positionInfo[tokenId]; - PositionConfig memory config = _toConfig(info); + (PositionInfo memory info, PoolKey memory poolKey) = getPoolPositionInfo(tokenId); + + int256 liquidityChange = -(liquidity.toInt256()); + // Note: the tokenId is used as the salt. - (BalanceDelta liquidityDelta, BalanceDelta feesAccrued) = - _modifyLiquidity(config, -(liquidity.toInt256()), bytes32(tokenId), hookData); + (BalanceDelta liquidityDelta, BalanceDelta feesAccrued) = poolManager.modifyLiquidity( + poolKey, _toParams(info.tickLower, info.tickUpper, liquidityChange, bytes32(tokenId)), hookData + ); + // Slippage checks should be done on the principal liquidityDelta which is the liquidityDelta - feesAccrued (liquidityDelta - feesAccrued).validateMinOut(amount0Min, amount1Min); + + if (info.hasSubscriber) { + _notifyModifyLiquidity(uint256(tokenId), liquidityChange, feesAccrued); + } } function _mint( - PositionConfig calldata config, + PoolKey calldata poolKey, + int24 tickLower, + int24 tickUpper, uint256 liquidity, uint128 amount0Max, uint128 amount1Max, @@ -290,25 +300,20 @@ contract PositionManager is } _mint(owner, tokenId); - // _beforeModify is not called here because the tokenId is newly minted - (BalanceDelta liquidityDelta, BalanceDelta feesAccrued) = - _modifyLiquidity(config, liquidity.toInt256(), bytes32(tokenId), hookData); + (BalanceDelta liquidityDelta, BalanceDelta feesAccrued) = poolManager.modifyLiquidity( + poolKey, _toParams(tickLower, tickUpper, liquidity.toInt256(), bytes32(tokenId)), hookData + ); + // Slippage checks should be done on the principal liquidityDelta which is the liquidityDelta - feesAccrued (liquidityDelta - feesAccrued).validateMaxIn(amount0Max, amount1Max); - PositionInfo memory info = PositionInfo({ - poolId: bytes25(PoolId.unwrap(config.poolKey.toId())), - tickLower: config.tickLower, - tickUpper: config.tickUpper, - hasSubscriber: false - }); + // Update position info and pool key storage. + PositionInfo memory info = PositionInfoLibrary.initialize(poolKey, tickLower, tickUpper); positionInfo[tokenId] = info; if (poolKeys[info.poolId].isEmpty()) { - poolKeys[info.poolId] = config.poolKey; + poolKeys[info.poolId] = poolKey; } - - emit MintPosition(tokenId, config); } /// @dev this is overloaded with ERC721Permit_v4._burn @@ -316,15 +321,17 @@ contract PositionManager is internal onlyIfApproved(msgSender(), tokenId) { - PositionInfo memory info = positionInfo[tokenId]; - PositionConfig memory config = _toConfig(info); + (PositionInfo memory info, PoolKey memory poolKey) = getPoolPositionInfo(tokenId); - uint256 liquidity = uint256(getPositionLiquidity(tokenId, config)); + uint256 liquidity = uint256(_getLiquidity(tokenId, poolKey, info.tickLower, info.tickUpper)); // Can only call modify if there is non zero liquidity. + BalanceDelta feesAccrued; if (liquidity > 0) { - (BalanceDelta liquidityDelta, BalanceDelta feesAccrued) = - _modifyLiquidity(config, -(liquidity.toInt256()), bytes32(tokenId), hookData); + BalanceDelta liquidityDelta; + (liquidityDelta, feesAccrued) = poolManager.modifyLiquidity( + poolKey, _toParams(info.tickLower, info.tickUpper, -(liquidity.toInt256()), bytes32(tokenId)), hookData + ); // Slippage checks should be done on the principal liquidityDelta which is the liquidityDelta - feesAccrued (liquidityDelta - feesAccrued).validateMinOut(amount0Min, amount1Min); } @@ -332,6 +339,11 @@ contract PositionManager is delete positionInfo[tokenId]; // Burn the token. _burn(tokenId); + + // TODO: This will be changed from audit issue.. + if (info.hasSubscriber) { + _notifyModifyLiquidity(uint256(tokenId), -(liquidity.toInt256()), feesAccrued); + } } function _settlePair(Currency currency0, Currency currency1) internal { @@ -380,29 +392,6 @@ contract PositionManager is if (balance > 0) currency.transfer(to, balance); } - function _modifyLiquidity( - PositionConfig memory config, - int256 liquidityChange, - bytes32 salt, - bytes calldata hookData - ) internal returns (BalanceDelta liquidityDelta, BalanceDelta feesAccrued) { - (liquidityDelta, feesAccrued) = poolManager.modifyLiquidity( - config.poolKey, - IPoolManager.ModifyLiquidityParams({ - tickLower: config.tickLower, - tickUpper: config.tickUpper, - liquidityDelta: liquidityChange, - salt: salt - }), - hookData - ); - - // TODO: Move outside this function since we've already loaded info. && other audit issues.. - if (positionInfo[uint256(salt)].hasSubscriber) { - _notifyModifyLiquidity(uint256(salt), liquidityChange, feesAccrued); - } - } - // implementation of abstract function DeltaResolver._pay function _pay(Currency currency, address payer, uint256 amount) internal override { if (payer == address(this)) { @@ -413,6 +402,19 @@ contract PositionManager is } } + function _toParams(int24 tickLower, int24 tickUpper, int256 liquidityChange, bytes32 salt) + private + pure + returns (IPoolManager.ModifyLiquidityParams memory) + { + return IPoolManager.ModifyLiquidityParams({ + tickLower: tickLower, + tickUpper: tickUpper, + liquidityDelta: liquidityChange, + salt: salt + }); + } + /// @dev overrides solmate transferFrom in case a notification to subscribers is needed function transferFrom(address from, address to, uint256 id) public virtual override { super.transferFrom(from, to, id); @@ -420,17 +422,27 @@ contract PositionManager is } /// @inheritdoc IPositionManager - function getPositionLiquidity(uint256 tokenId, PositionConfig memory config) + function getPoolPositionInfo(uint256 tokenId) public view - returns (uint128 liquidity) + returns (PositionInfo memory info, PoolKey memory poolKey) { - bytes32 positionId = - Position.calculatePositionKey(address(this), config.tickLower, config.tickUpper, bytes32(tokenId)); - liquidity = poolManager.getPositionLiquidity(config.poolKey.toId(), positionId); + info = positionInfo[tokenId]; + poolKey = poolKeys[info.poolId]; + } + + /// @inheritdoc IPositionManager + function getPositionLiquidity(uint256 tokenId) public view returns (uint128 liquidity) { + (PositionInfo memory info, PoolKey memory poolKey) = getPoolPositionInfo(tokenId); + liquidity = _getLiquidity(tokenId, poolKey, info.tickLower, info.tickUpper); } - function _toConfig(PositionInfo memory info) internal view returns (PositionConfig memory config) { - return PositionConfig({poolKey: poolKeys[info.poolId], tickLower: info.tickLower, tickUpper: info.tickUpper}); + function _getLiquidity(uint256 tokenId, PoolKey memory poolKey, int24 tickLower, int24 tickUpper) + internal + view + returns (uint128 liquidity) + { + bytes32 positionId = Position.calculatePositionKey(address(this), tickLower, tickUpper, bytes32(tokenId)); + liquidity = poolManager.getPositionLiquidity(poolKey.toId(), positionId); } } diff --git a/src/base/Notifier.sol b/src/base/Notifier.sol index c708861c1..ac75aecb7 100644 --- a/src/base/Notifier.sol +++ b/src/base/Notifier.sol @@ -7,6 +7,7 @@ import {INotifier} from "../interfaces/INotifier.sol"; import {CustomRevert} from "@uniswap/v4-core/src/libraries/CustomRevert.sol"; import {BalanceDelta} from "@uniswap/v4-core/src/types/BalanceDelta.sol"; import {PositionManager} from "../PositionManager.sol"; +import {PositionInfo} from "../libraries/PositionInfoLibrary.sol"; /// @notice Notifier is used to opt in to sending updates to external contracts about position modifications or transfers abstract contract Notifier is INotifier { @@ -29,7 +30,7 @@ abstract contract Notifier is INotifier { modifier onlyIfApproved(address caller, uint256 tokenId) virtual; - function _positionInfo(uint256 tokenId) internal view virtual returns (PositionManager.PositionInfo storage); + function _positionInfo(uint256 tokenId) internal view virtual returns (PositionInfo storage); /// @inheritdoc INotifier function subscribe(uint256 tokenId, address newSubscriber, bytes calldata data) diff --git a/src/interfaces/IPositionManager.sol b/src/interfaces/IPositionManager.sol index ce5dee262..eefdcbcdd 100644 --- a/src/interfaces/IPositionManager.sol +++ b/src/interfaces/IPositionManager.sol @@ -3,16 +3,13 @@ pragma solidity ^0.8.24; import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol"; import {Currency} from "@uniswap/v4-core/src/types/Currency.sol"; -import {PositionConfig} from "../libraries/PositionConfig.sol"; +import {PositionInfo} from "../libraries/PositionInfoLibrary.sol"; import {INotifier} from "./INotifier.sol"; interface IPositionManager is INotifier { error NotApproved(address caller); error DeadlinePassed(); - error IncorrectPositionConfigForTokenId(uint256 tokenId); - - event MintPosition(uint256 indexed tokenId, PositionConfig config); /// @notice Unlocks Uniswap v4 PoolManager and batches actions for modifying liquidity /// @dev This is the standard entrypoint for the PositionManager @@ -31,11 +28,12 @@ interface IPositionManager is INotifier { function nextTokenId() external view returns (uint256); /// @param tokenId the ERC721 tokenId - /// @param config the corresponding PositionConfig for the tokenId /// @return liquidity the position's liquidity, as a liquidityAmount /// @dev this value can be processed as an amount0 and amount1 by using the LiquidityAmounts library - function getPositionLiquidity(uint256 tokenId, PositionConfig calldata config) - external - view - returns (uint128 liquidity); + function getPositionLiquidity(uint256 tokenId) external view returns (uint128 liquidity); + + /// @param tokenId the ERC721 tokenId + /// @return PositionInfo information about the position including the range (tickLower, tickUpper) + /// @return poolKey the pool key of the position + function getPoolPositionInfo(uint256 tokenId) external view returns (PositionInfo memory, PoolKey memory); } diff --git a/src/libraries/CalldataDecoder.sol b/src/libraries/CalldataDecoder.sol index 0f032a344..1b2fa13f1 100644 --- a/src/libraries/CalldataDecoder.sol +++ b/src/libraries/CalldataDecoder.sol @@ -4,6 +4,7 @@ pragma solidity ^0.8.0; import {PositionConfig} from "./PositionConfig.sol"; import {Currency} from "@uniswap/v4-core/src/types/Currency.sol"; import {IV4Router} from "../interfaces/IV4Router.sol"; +import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol"; /// @title Library for abi decoding in calldata library CalldataDecoder { @@ -60,12 +61,14 @@ library CalldataDecoder { hookData = params.toBytes(4); } - /// @dev equivalent to: abi.decode(params, (PositionConfig, uint256, uint128, uint128, address, bytes)) in calldata + /// @dev equivalent to: abi.decode(params, (PoolKey, int24, int24, uint256, uint128, uint128, address, bytes)) in calldata function decodeMintParams(bytes calldata params) internal pure returns ( - PositionConfig calldata config, + PoolKey calldata poolKey, + int24 tickLower, + int24 tickUpper, uint256 liquidity, uint128 amount0Max, uint128 amount1Max, @@ -74,7 +77,9 @@ library CalldataDecoder { ) { assembly ("memory-safe") { - config := params.offset + poolKey := params.offset + tickLower := calldataload(add(params.offset, 0xa0)) + tickUpper := calldataload(add(params.offset, 0xc0)) liquidity := calldataload(add(params.offset, 0xe0)) amount0Max := calldataload(add(params.offset, 0x100)) amount1Max := calldataload(add(params.offset, 0x120)) diff --git a/src/libraries/PositionConfig.sol b/src/libraries/PositionConfig.sol index ba9b85e1c..65558ab26 100644 --- a/src/libraries/PositionConfig.sol +++ b/src/libraries/PositionConfig.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.24; import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol"; -// A PositionConfig is the input for creating and modifying a Position in core, whose truncated hash is set per tokenId +// A PositionConfig is the input for creating and modifying a Position in core struct PositionConfig { PoolKey poolKey; int24 tickLower; diff --git a/src/libraries/PositionInfoLibrary.sol b/src/libraries/PositionInfoLibrary.sol new file mode 100644 index 000000000..2f148f731 --- /dev/null +++ b/src/libraries/PositionInfoLibrary.sol @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.24; + +import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol"; +import {PoolId, PoolIdLibrary} from "@uniswap/v4-core/src/types/PoolId.sol"; + +// TODO: Move to custom type, packed memory. +struct PositionInfo { + // lower 25 bytes of the poolId + bytes25 poolId; + int24 tickLower; + int24 tickUpper; + bool hasSubscriber; +} + +library PositionInfoLibrary { + using PoolIdLibrary for PoolKey; + /// @notice Creates the default PositionInfo struct + /// @dev Called when minting a new position + /// @param poolKey the pool key of the position + /// @param tickLower the lower tick of the position + /// @param tickUpper the upper tick of the position + /// @return the PositionInfo struct, with the truncated poolId and the hasSubscriber flag set to false + + function initialize(PoolKey memory poolKey, int24 tickLower, int24 tickUpper) + internal + returns (PositionInfo memory) + { + return PositionInfo({ + poolId: bytes25(PoolId.unwrap(poolKey.toId())), + tickLower: tickLower, + tickUpper: tickUpper, + hasSubscriber: false + }); + } +} diff --git a/test/erc721Permit/ERC721Permit.permit.t.sol b/test/erc721Permit/ERC721Permit.permit.t.sol index 654c20c93..b37cf527c 100644 --- a/test/erc721Permit/ERC721Permit.permit.t.sol +++ b/test/erc721Permit/ERC721Permit.permit.t.sol @@ -229,7 +229,8 @@ contract ERC721PermitTest is Test { assertEq(erc721Permit.nonces(alice, wordPos) & (1 << bitPos), 0); } - function test_fuzz_erc721Permit_SignatureDeadlineExpired(address spender) public { + function test_fuzz_erc721Permit_SignatureDeadlineExpired() public { + address spender = 0xf30b182eA406CC5A71b8A44e2FE50cC80383F914; vm.prank(alice); uint256 tokenId = erc721Permit.mint(); diff --git a/test/libraries/CalldataDecoder.t.sol b/test/libraries/CalldataDecoder.t.sol index bbf544514..c0501b162 100644 --- a/test/libraries/CalldataDecoder.t.sol +++ b/test/libraries/CalldataDecoder.t.sol @@ -59,9 +59,20 @@ contract CalldataDecoderTest is Test { address _owner, bytes calldata _hookData ) public view { - bytes memory params = abi.encode(_config, _liquidity, _amount0Max, _amount1Max, _owner, _hookData); + bytes memory params = abi.encode( + _config.poolKey, + _config.tickLower, + _config.tickUpper, + _liquidity, + _amount0Max, + _amount1Max, + _owner, + _hookData + ); ( - PositionConfig memory config, + PoolKey memory poolKey, + int24 tickLower, + int24 tickUpper, uint256 liquidity, uint128 amount0Max, uint128 amount1Max, @@ -74,7 +85,9 @@ contract CalldataDecoderTest is Test { assertEq(amount1Max, _amount1Max); assertEq(owner, _owner); assertEq(hookData, _hookData); - _assertEq(_config, config); + _assertEq(_config.poolKey, poolKey); + assertEq(_config.tickLower, tickLower); + assertEq(_config.tickUpper, tickUpper); } function test_fuzz_decodeSwapExactInParams(IV4Router.ExactInputParams calldata _swapParams) public view { diff --git a/test/mocks/MockCalldataDecoder.sol b/test/mocks/MockCalldataDecoder.sol index 7af17f698..d7aa33c1f 100644 --- a/test/mocks/MockCalldataDecoder.sol +++ b/test/mocks/MockCalldataDecoder.sol @@ -5,6 +5,7 @@ import {PositionConfig} from "../../src/libraries/PositionConfig.sol"; import {CalldataDecoder} from "../../src/libraries/CalldataDecoder.sol"; import {IV4Router} from "../../src/interfaces/IV4Router.sol"; import {Currency} from "@uniswap/v4-core/src/types/Currency.sol"; +import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol"; // we need to use a mock contract to make the calls happen in calldata not memory contract MockCalldataDecoder { @@ -62,7 +63,9 @@ contract MockCalldataDecoder { external pure returns ( - PositionConfig calldata config, + PoolKey memory poolKey, + int24 tickLower, + int24 tickUpper, uint256 liquidity, uint128 amount0Max, uint128 amount1Max, diff --git a/test/position-managers/Execute.t.sol b/test/position-managers/Execute.t.sol index 09e843042..d2e1f7c12 100644 --- a/test/position-managers/Execute.t.sol +++ b/test/position-managers/Execute.t.sol @@ -72,7 +72,7 @@ contract ExecuteTest is Test, PosmTestSetup, LiquidityFuzzers { increaseLiquidity(tokenId, config, liquidityToAdd, ZERO_BYTES); - uint256 liquidity = lpm.getPositionLiquidity(tokenId, config); + uint256 liquidity = lpm.getPositionLiquidity(tokenId); assertEq(liquidity, initialLiquidity + liquidityToAdd); } @@ -102,7 +102,7 @@ contract ExecuteTest is Test, PosmTestSetup, LiquidityFuzzers { bytes memory calls = planner.finalizeModifyLiquidityWithClose(config.poolKey); lpm.modifyLiquidities(calls, _deadline); - uint256 liquidity = lpm.getPositionLiquidity(tokenId, config); + uint256 liquidity = lpm.getPositionLiquidity(tokenId); assertEq(liquidity, initialLiquidity + liquidityToAdd + liquidityToAdd2); } @@ -132,7 +132,7 @@ contract ExecuteTest is Test, PosmTestSetup, LiquidityFuzzers { bytes memory calls = planner.finalizeModifyLiquidityWithSettlePair(config.poolKey); lpm.modifyLiquidities(calls, _deadline); - uint256 liquidity = lpm.getPositionLiquidity(tokenId, config); + uint256 liquidity = lpm.getPositionLiquidity(tokenId); assertEq(liquidity, initialLiquidity + liquidityToAdd + liquidityToAdd2); } @@ -149,7 +149,9 @@ contract ExecuteTest is Test, PosmTestSetup, LiquidityFuzzers { planner.add( Actions.MINT_POSITION, abi.encode( - config, + config.poolKey, + config.tickLower, + config.tickUpper, initialLiquidity, MAX_SLIPPAGE_INCREASE, MAX_SLIPPAGE_INCREASE, @@ -165,7 +167,7 @@ contract ExecuteTest is Test, PosmTestSetup, LiquidityFuzzers { bytes memory calls = planner.finalizeModifyLiquidityWithClose(config.poolKey); lpm.modifyLiquidities(calls, _deadline); - uint256 liquidity = lpm.getPositionLiquidity(tokenId, config); + uint256 liquidity = lpm.getPositionLiquidity(tokenId); assertEq(liquidity, initialLiquidity + liquidityToAdd); } @@ -202,7 +204,9 @@ contract ExecuteTest is Test, PosmTestSetup, LiquidityFuzzers { planner.add( Actions.MINT_POSITION, abi.encode( - newConfig, + newConfig.poolKey, + newConfig.tickLower, + newConfig.tickUpper, newLiquidity, MAX_SLIPPAGE_INCREASE, MAX_SLIPPAGE_INCREASE, @@ -232,7 +236,7 @@ contract ExecuteTest is Test, PosmTestSetup, LiquidityFuzzers { { // old position has no liquidity - uint128 liquidity = lpm.getPositionLiquidity(tokenId, config); + uint128 liquidity = lpm.getPositionLiquidity(tokenId); assertEq(liquidity, 0); // new token was minted @@ -241,7 +245,7 @@ contract ExecuteTest is Test, PosmTestSetup, LiquidityFuzzers { // new token has expected liquidity - liquidity = lpm.getPositionLiquidity(newTokenId, newConfig); + liquidity = lpm.getPositionLiquidity(newTokenId); assertEq(liquidity, newLiquidity); } } diff --git a/test/position-managers/IncreaseLiquidity.t.sol b/test/position-managers/IncreaseLiquidity.t.sol index 1a47ee724..02bb69cf0 100644 --- a/test/position-managers/IncreaseLiquidity.t.sol +++ b/test/position-managers/IncreaseLiquidity.t.sol @@ -604,7 +604,16 @@ contract IncreaseLiquidityTest is Test, PosmTestSetup, Fuzzers { Plan memory planner = Planner.init(); planner.add( Actions.MINT_POSITION, - abi.encode(config, liquidityAlice, MAX_SLIPPAGE_INCREASE, MAX_SLIPPAGE_INCREASE, alice, ZERO_BYTES) + abi.encode( + config.poolKey, + config.tickLower, + config.tickUpper, + liquidityAlice, + MAX_SLIPPAGE_INCREASE, + MAX_SLIPPAGE_INCREASE, + alice, + ZERO_BYTES + ) ); planner.add(Actions.SETTLE, abi.encode(currency0, ActionConstants.OPEN_DELTA, false)); planner.add(Actions.SETTLE, abi.encode(currency1, ActionConstants.OPEN_DELTA, false)); @@ -646,7 +655,16 @@ contract IncreaseLiquidityTest is Test, PosmTestSetup, Fuzzers { Plan memory planner = Planner.init(); planner.add( Actions.MINT_POSITION, - abi.encode(config, liquidityAlice, MAX_SLIPPAGE_INCREASE, MAX_SLIPPAGE_INCREASE, alice, ZERO_BYTES) + abi.encode( + config.poolKey, + config.tickLower, + config.tickUpper, + liquidityAlice, + MAX_SLIPPAGE_INCREASE, + MAX_SLIPPAGE_INCREASE, + alice, + ZERO_BYTES + ) ); planner.add(Actions.SETTLE, abi.encode(currency0, ActionConstants.OPEN_DELTA, false)); planner.add(Actions.SETTLE, abi.encode(currency1, ActionConstants.OPEN_DELTA, false)); @@ -684,7 +702,7 @@ contract IncreaseLiquidityTest is Test, PosmTestSetup, Fuzzers { mint(config, liquidityAlice, alice, ZERO_BYTES); uint256 tokenIdAlice = lpm.nextTokenId() - 1; - uint256 liquidity = lpm.getPositionLiquidity(tokenIdAlice, config); + uint256 liquidity = lpm.getPositionLiquidity(tokenIdAlice); assertEq(liquidity, liquidityAlice); // alice increases with the balance in the position manager @@ -719,7 +737,7 @@ contract IncreaseLiquidityTest is Test, PosmTestSetup, Fuzzers { uint256 amount0 = uint128(-delta.amount0()); uint256 amount1 = uint128(-delta.amount1()); - liquidity = lpm.getPositionLiquidity(tokenIdAlice, config); + liquidity = lpm.getPositionLiquidity(tokenIdAlice); assertEq(liquidity, 2 * liquidityAlice); // The balances were swept back to this address. diff --git a/test/position-managers/NativeToken.t.sol b/test/position-managers/NativeToken.t.sol index 0c079f83d..b8898c1a9 100644 --- a/test/position-managers/NativeToken.t.sol +++ b/test/position-managers/NativeToken.t.sol @@ -90,7 +90,7 @@ contract PositionManagerTest is Test, PosmTestSetup, LiquidityFuzzers { lpm.modifyLiquidities{value: amount0 + 1}(calls, _deadline); BalanceDelta delta = getLastDelta(); - uint256 liquidity = lpm.getPositionLiquidity(tokenId, config); + uint256 liquidity = lpm.getPositionLiquidity(tokenId); assertEq(liquidity, uint256(params.liquidityDelta)); assertEq(balance0Before - currency0.balanceOfSelf(), uint256(int256(-delta.amount0())), "incorrect amount0"); @@ -116,7 +116,9 @@ contract PositionManagerTest is Test, PosmTestSetup, LiquidityFuzzers { planner.add( Actions.MINT_POSITION, abi.encode( - config, + config.poolKey, + config.tickLower, + config.tickUpper, liquidityToAdd, MAX_SLIPPAGE_INCREASE, MAX_SLIPPAGE_INCREASE, @@ -142,7 +144,7 @@ contract PositionManagerTest is Test, PosmTestSetup, LiquidityFuzzers { lpm.modifyLiquidities{value: amount0 * 2 + 1}(calls, _deadline); BalanceDelta delta = getLastDelta(); - uint256 liquidity = lpm.getPositionLiquidity(tokenId, config); + uint256 liquidity = lpm.getPositionLiquidity(tokenId); assertEq(liquidity, uint256(params.liquidityDelta)); // only paid the delta amount, with excess tokens returned to caller @@ -168,7 +170,16 @@ contract PositionManagerTest is Test, PosmTestSetup, LiquidityFuzzers { Plan memory planner = Planner.init(); planner.add( Actions.MINT_POSITION, - abi.encode(config, liquidityToAdd, MAX_SLIPPAGE_INCREASE, MAX_SLIPPAGE_INCREASE, address(this), ZERO_BYTES) + abi.encode( + config.poolKey, + config.tickLower, + config.tickUpper, + liquidityToAdd, + MAX_SLIPPAGE_INCREASE, + MAX_SLIPPAGE_INCREASE, + address(this), + ZERO_BYTES + ) ); planner.add(Actions.SETTLE_PAIR, abi.encode(nativeKey.currency0, nativeKey.currency1)); // sweep the excess eth @@ -187,7 +198,7 @@ contract PositionManagerTest is Test, PosmTestSetup, LiquidityFuzzers { lpm.modifyLiquidities{value: amount0 * 2 + 1}(calls, _deadline); BalanceDelta delta = getLastDelta(); - uint256 liquidity = lpm.getPositionLiquidity(tokenId, config); + uint256 liquidity = lpm.getPositionLiquidity(tokenId); assertEq(liquidity, uint256(params.liquidityDelta)); // only paid the delta amount, with excess tokens returned to caller @@ -211,7 +222,7 @@ contract PositionManagerTest is Test, PosmTestSetup, LiquidityFuzzers { uint256 tokenId = lpm.nextTokenId(); mintWithNative(SQRT_PRICE_1_1, config, liquidityToAdd, ActionConstants.MSG_SENDER, ZERO_BYTES); - uint256 liquidity = lpm.getPositionLiquidity(tokenId, config); + uint256 liquidity = lpm.getPositionLiquidity(tokenId); assertEq(liquidity, uint256(params.liquidityDelta)); // burn liquidity @@ -226,7 +237,7 @@ contract PositionManagerTest is Test, PosmTestSetup, LiquidityFuzzers { // No decrease/modifyLiq call will actually happen on the call to burn so the deltas array will be the same length. assertEq(numDeltas, hook.numberDeltasReturned()); - liquidity = lpm.getPositionLiquidity(tokenId, config); + liquidity = lpm.getPositionLiquidity(tokenId); assertEq(liquidity, 0); // TODO: slightly off by 1 bip (0.0001%) @@ -264,7 +275,7 @@ contract PositionManagerTest is Test, PosmTestSetup, LiquidityFuzzers { uint256 tokenId = lpm.nextTokenId(); mintWithNative(SQRT_PRICE_1_1, config, liquidityToAdd, ActionConstants.MSG_SENDER, ZERO_BYTES); - uint256 liquidity = lpm.getPositionLiquidity(tokenId, config); + uint256 liquidity = lpm.getPositionLiquidity(tokenId); assertEq(liquidity, uint256(params.liquidityDelta)); // burn liquidity @@ -284,7 +295,7 @@ contract PositionManagerTest is Test, PosmTestSetup, LiquidityFuzzers { // No decrease/modifyLiq call will actually happen on the call to burn so the deltas array will be the same length. assertEq(numDeltas, hook.numberDeltasReturned()); - liquidity = lpm.getPositionLiquidity(tokenId, config); + liquidity = lpm.getPositionLiquidity(tokenId); assertEq(liquidity, 0); // TODO: slightly off by 1 bip (0.0001%) @@ -322,7 +333,7 @@ contract PositionManagerTest is Test, PosmTestSetup, LiquidityFuzzers { uint256 tokenId = lpm.nextTokenId(); mintWithNative(SQRT_PRICE_1_1, config, liquidityToAdd, ActionConstants.MSG_SENDER, ZERO_BYTES); - uint256 liquidity = lpm.getPositionLiquidity(tokenId, config); + uint256 liquidity = lpm.getPositionLiquidity(tokenId); assertEq(liquidity, uint256(params.liquidityDelta)); // burn liquidity @@ -332,7 +343,7 @@ contract PositionManagerTest is Test, PosmTestSetup, LiquidityFuzzers { burn(tokenId, config, ZERO_BYTES); BalanceDelta deltaBurn = getLastDelta(); - liquidity = lpm.getPositionLiquidity(tokenId, config); + liquidity = lpm.getPositionLiquidity(tokenId); assertEq(liquidity, 0); // TODO: slightly off by 1 bip (0.0001%) @@ -370,7 +381,7 @@ contract PositionManagerTest is Test, PosmTestSetup, LiquidityFuzzers { uint256 tokenId = lpm.nextTokenId(); mintWithNative(SQRT_PRICE_1_1, config, liquidityToAdd, ActionConstants.MSG_SENDER, ZERO_BYTES); - uint256 liquidity = lpm.getPositionLiquidity(tokenId, config); + uint256 liquidity = lpm.getPositionLiquidity(tokenId); assertEq(liquidity, uint256(params.liquidityDelta)); // burn liquidity @@ -385,7 +396,7 @@ contract PositionManagerTest is Test, PosmTestSetup, LiquidityFuzzers { lpm.modifyLiquidities(calls, _deadline); BalanceDelta deltaBurn = getLastDelta(); - liquidity = lpm.getPositionLiquidity(tokenId, config); + liquidity = lpm.getPositionLiquidity(tokenId); assertEq(liquidity, 0); // TODO: slightly off by 1 bip (0.0001%) @@ -436,7 +447,7 @@ contract PositionManagerTest is Test, PosmTestSetup, LiquidityFuzzers { BalanceDelta delta = getLastDelta(); // verify position liquidity increased - uint256 liquidity = lpm.getPositionLiquidity(tokenId, config); + uint256 liquidity = lpm.getPositionLiquidity(tokenId); assertEq(liquidity, liquidityToAdd + liquidityToAdd); // liquidity was doubled // verify native token balances changed as expected @@ -488,7 +499,7 @@ contract PositionManagerTest is Test, PosmTestSetup, LiquidityFuzzers { BalanceDelta delta = getLastDelta(); // verify position liquidity increased - uint256 liquidity = lpm.getPositionLiquidity(tokenId, config); + uint256 liquidity = lpm.getPositionLiquidity(tokenId); assertEq(liquidity, liquidityToAdd + liquidityToAdd); // liquidity was doubled // verify native token balances changed as expected, with overpaid tokens returned @@ -538,7 +549,7 @@ contract PositionManagerTest is Test, PosmTestSetup, LiquidityFuzzers { BalanceDelta delta = getLastDelta(); // verify position liquidity increased - uint256 liquidity = lpm.getPositionLiquidity(tokenId, config); + uint256 liquidity = lpm.getPositionLiquidity(tokenId); assertEq(liquidity, liquidityToAdd + liquidityToAdd); // liquidity was doubled // verify native token balances changed as expected, with overpaid tokens returned @@ -575,7 +586,7 @@ contract PositionManagerTest is Test, PosmTestSetup, LiquidityFuzzers { decreaseLiquidity(tokenId, config, decreaseLiquidityDelta, ZERO_BYTES); BalanceDelta delta = getLastDelta(); - uint256 liquidity = lpm.getPositionLiquidity(tokenId, config); + uint256 liquidity = lpm.getPositionLiquidity(tokenId); assertEq(liquidity, uint256(params.liquidityDelta) - decreaseLiquidityDelta); // verify native token balances changed as expected @@ -618,7 +629,7 @@ contract PositionManagerTest is Test, PosmTestSetup, LiquidityFuzzers { lpm.modifyLiquidities(calls, _deadline); BalanceDelta delta = getLastDelta(); - uint256 liquidity = lpm.getPositionLiquidity(tokenId, config); + uint256 liquidity = lpm.getPositionLiquidity(tokenId); assertEq(liquidity, uint256(params.liquidityDelta) - decreaseLiquidityDelta); // verify native token balances changed as expected @@ -771,7 +782,16 @@ contract PositionManagerTest is Test, PosmTestSetup, LiquidityFuzzers { Plan memory plan = Planner.init(); plan.add( Actions.MINT_POSITION, - abi.encode(config, 100e18, MAX_SLIPPAGE_INCREASE, MAX_SLIPPAGE_INCREASE, address(this), ZERO_BYTES) + abi.encode( + config.poolKey, + config.tickLower, + config.tickUpper, + 100e18, + MAX_SLIPPAGE_INCREASE, + MAX_SLIPPAGE_INCREASE, + address(this), + ZERO_BYTES + ) ); plan.add(Actions.CLOSE_CURRENCY, abi.encode(config.poolKey.currency0)); plan.add(Actions.CLOSE_CURRENCY, abi.encode(config.poolKey.currency1)); @@ -785,7 +805,7 @@ contract PositionManagerTest is Test, PosmTestSetup, LiquidityFuzzers { lpm.multicall{value: 10e18}(calls); - uint256 liquidity = lpm.getPositionLiquidity(tokenId, config); + uint256 liquidity = lpm.getPositionLiquidity(tokenId); assertEq(liquidity, 100e18); assertEq(sub.notifySubscribeCount(), 1); diff --git a/test/position-managers/Permit.t.sol b/test/position-managers/Permit.t.sol index 6cf03c3f1..2c9e3bf7e 100644 --- a/test/position-managers/Permit.t.sol +++ b/test/position-managers/Permit.t.sol @@ -88,7 +88,7 @@ contract PermitTest is Test, PosmTestSetup { vm.stopPrank(); // alice's position increased liquidity - uint256 liquidity = lpm.getPositionLiquidity(tokenIdAlice, config); + uint256 liquidity = lpm.getPositionLiquidity(tokenIdAlice); assertEq(liquidity, liquidityAlice + liquidityToAdd); } @@ -109,7 +109,7 @@ contract PermitTest is Test, PosmTestSetup { vm.stopPrank(); // alice's position decreased liquidity - uint256 liquidity = lpm.getPositionLiquidity(tokenIdAlice, config); + uint256 liquidity = lpm.getPositionLiquidity(tokenIdAlice); assertEq(liquidity, liquidityAlice - liquidityToRemove); } @@ -241,7 +241,7 @@ contract PermitTest is Test, PosmTestSetup { decreaseLiquidity(tokenId, config, liquidityToRemove, ZERO_BYTES); vm.stopPrank(); - uint256 liquidity = lpm.getPositionLiquidity(tokenId, config); + uint256 liquidity = lpm.getPositionLiquidity(tokenId); assertEq(liquidity, liquidityAlice - liquidityToRemove); } @@ -270,7 +270,7 @@ contract PermitTest is Test, PosmTestSetup { decreaseLiquidity(tokenId, config, liquidityToRemove, ZERO_BYTES); vm.stopPrank(); - uint256 liquidity = lpm.getPositionLiquidity(tokenId, config); + uint256 liquidity = lpm.getPositionLiquidity(tokenId); assertEq(liquidity, liquidityAlice - liquidityToRemove); } diff --git a/test/position-managers/PositionManager.gas.t.sol b/test/position-managers/PositionManager.gas.t.sol index e0bdec40e..03e82636f 100644 --- a/test/position-managers/PositionManager.gas.t.sol +++ b/test/position-managers/PositionManager.gas.t.sol @@ -74,7 +74,9 @@ contract PosMGasTest is Test, PosmTestSetup, GasSnapshot { Plan memory planner = Planner.init().add( Actions.MINT_POSITION, abi.encode( - config, + config.poolKey, + config.tickLower, + config.tickUpper, 10_000 ether, MAX_SLIPPAGE_INCREASE, MAX_SLIPPAGE_INCREASE, @@ -90,7 +92,16 @@ contract PosMGasTest is Test, PosmTestSetup, GasSnapshot { function test_gas_mint_withSettlePair() public { Plan memory planner = Planner.init().add( Actions.MINT_POSITION, - abi.encode(config, 10_000 ether, MAX_SLIPPAGE_INCREASE, MAX_SLIPPAGE_INCREASE, address(this), ZERO_BYTES) + abi.encode( + config.poolKey, + config.tickLower, + config.tickUpper, + 10_000 ether, + MAX_SLIPPAGE_INCREASE, + MAX_SLIPPAGE_INCREASE, + address(this), + ZERO_BYTES + ) ); bytes memory calls = planner.finalizeModifyLiquidityWithSettlePair(config.poolKey); lpm.modifyLiquidities(calls, _deadline); @@ -107,7 +118,9 @@ contract PosMGasTest is Test, PosmTestSetup, GasSnapshot { Plan memory planner = Planner.init().add( Actions.MINT_POSITION, abi.encode( - config, + config.poolKey, + config.tickLower, + config.tickUpper, 10_000 ether, MAX_SLIPPAGE_INCREASE, MAX_SLIPPAGE_INCREASE, @@ -131,7 +144,9 @@ contract PosMGasTest is Test, PosmTestSetup, GasSnapshot { Plan memory planner = Planner.init().add( Actions.MINT_POSITION, abi.encode( - config, + config.poolKey, + config.tickLower, + config.tickUpper, 10_000 ether, MAX_SLIPPAGE_INCREASE, MAX_SLIPPAGE_INCREASE, @@ -155,7 +170,9 @@ contract PosMGasTest is Test, PosmTestSetup, GasSnapshot { Plan memory planner = Planner.init().add( Actions.MINT_POSITION, abi.encode( - config, + config.poolKey, + config.tickLower, + config.tickUpper, 10_000 ether, MAX_SLIPPAGE_INCREASE, MAX_SLIPPAGE_INCREASE, @@ -383,7 +400,14 @@ contract PosMGasTest is Test, PosmTestSetup, GasSnapshot { planner.add( Actions.MINT_POSITION, abi.encode( - config, 100e18, MAX_SLIPPAGE_INCREASE, MAX_SLIPPAGE_INCREASE, ActionConstants.MSG_SENDER, ZERO_BYTES + config.poolKey, + config.tickLower, + config.tickUpper, + 100e18, + MAX_SLIPPAGE_INCREASE, + MAX_SLIPPAGE_INCREASE, + ActionConstants.MSG_SENDER, + ZERO_BYTES ) ); bytes memory actions = planner.finalizeModifyLiquidityWithClose(config.poolKey); @@ -435,7 +459,9 @@ contract PosMGasTest is Test, PosmTestSetup, GasSnapshot { Plan memory planner = Planner.init().add( Actions.MINT_POSITION, abi.encode( - config, + config.poolKey, + config.tickLower, + config.tickUpper, 10_001 ether, MAX_SLIPPAGE_INCREASE, MAX_SLIPPAGE_INCREASE, @@ -576,7 +602,9 @@ contract PosMGasTest is Test, PosmTestSetup, GasSnapshot { planner.add( Actions.MINT_POSITION, abi.encode( - configNative, + configNative.poolKey, + configNative.tickLower, + configNative.tickUpper, liquidityToAdd, MAX_SLIPPAGE_INCREASE, MAX_SLIPPAGE_INCREASE, @@ -607,7 +635,14 @@ contract PosMGasTest is Test, PosmTestSetup, GasSnapshot { planner.add( Actions.MINT_POSITION, abi.encode( - configNative, liquidityToAdd, MAX_SLIPPAGE_INCREASE, MAX_SLIPPAGE_INCREASE, address(this), ZERO_BYTES + configNative.poolKey, + configNative.tickLower, + configNative.tickUpper, + liquidityToAdd, + MAX_SLIPPAGE_INCREASE, + MAX_SLIPPAGE_INCREASE, + address(this), + ZERO_BYTES ) ); planner.add(Actions.SETTLE_PAIR, abi.encode(nativeKey.currency0, nativeKey.currency1)); @@ -812,7 +847,16 @@ contract PosMGasTest is Test, PosmTestSetup, GasSnapshot { Plan memory planner = Planner.init(); planner.add( Actions.MINT_POSITION, - abi.encode(config, liquidityAlice, MAX_SLIPPAGE_INCREASE, MAX_SLIPPAGE_INCREASE, alice, ZERO_BYTES) + abi.encode( + config.poolKey, + config.tickLower, + config.tickUpper, + liquidityAlice, + MAX_SLIPPAGE_INCREASE, + MAX_SLIPPAGE_INCREASE, + alice, + ZERO_BYTES + ) ); planner.add(Actions.SETTLE, abi.encode(currency0, ActionConstants.OPEN_DELTA, false)); planner.add(Actions.SETTLE, abi.encode(currency1, ActionConstants.OPEN_DELTA, false)); diff --git a/test/position-managers/PositionManager.modifyLiquidities.t.sol b/test/position-managers/PositionManager.modifyLiquidities.t.sol index 33a8fff7c..52a1afd91 100644 --- a/test/position-managers/PositionManager.modifyLiquidities.t.sol +++ b/test/position-managers/PositionManager.modifyLiquidities.t.sol @@ -71,13 +71,13 @@ contract PositionManagerModifyLiquiditiesTest is Test, PosmTestSetup, LiquidityF swap(key, true, -1e18, calls); - uint256 liquidity = lpm.getPositionLiquidity(tokenId, config); + uint256 liquidity = lpm.getPositionLiquidity(tokenId); // original liquidity unchanged assertEq(liquidity, initialLiquidity); // hook minted its own position - liquidity = lpm.getPositionLiquidity(hookTokenId, config); + liquidity = lpm.getPositionLiquidity(hookTokenId); assertEq(liquidity, newLiquidity); assertEq(lpm.ownerOf(tokenId), address(this)); // original position owned by this contract @@ -99,7 +99,7 @@ contract PositionManagerModifyLiquiditiesTest is Test, PosmTestSetup, LiquidityF swap(key, true, -1e18, calls); - uint256 liquidity = lpm.getPositionLiquidity(tokenId, config); + uint256 liquidity = lpm.getPositionLiquidity(tokenId); assertEq(liquidity, initialLiquidity + newLiquidity); } @@ -119,7 +119,7 @@ contract PositionManagerModifyLiquiditiesTest is Test, PosmTestSetup, LiquidityF swap(key, true, -1e18, calls); - uint256 liquidity = lpm.getPositionLiquidity(tokenId, config); + uint256 liquidity = lpm.getPositionLiquidity(tokenId); assertEq(liquidity, initialLiquidity - liquidityToDecrease); } @@ -145,7 +145,7 @@ contract PositionManagerModifyLiquiditiesTest is Test, PosmTestSetup, LiquidityF bytes memory calls = getCollectEncoded(tokenId, config, ZERO_BYTES); swap(key, true, -1e18, calls); - uint256 liquidity = lpm.getPositionLiquidity(tokenId, config); + uint256 liquidity = lpm.getPositionLiquidity(tokenId); // liquidity unchanged assertEq(liquidity, initialLiquidity); @@ -178,7 +178,7 @@ contract PositionManagerModifyLiquiditiesTest is Test, PosmTestSetup, LiquidityF bytes memory calls = getBurnEncoded(tokenId, config, ZERO_BYTES); swap(key, true, -1e18, calls); - uint256 liquidity = lpm.getPositionLiquidity(tokenId, config); + uint256 liquidity = lpm.getPositionLiquidity(tokenId); // liquidity burned assertEq(liquidity, 0); diff --git a/test/position-managers/PositionManager.multicall.t.sol b/test/position-managers/PositionManager.multicall.t.sol index b09921073..1497cad08 100644 --- a/test/position-managers/PositionManager.multicall.t.sol +++ b/test/position-managers/PositionManager.multicall.t.sol @@ -97,7 +97,14 @@ contract PositionManagerMulticallTest is Test, Permit2SignatureHelpers, PosmTest planner.add( Actions.MINT_POSITION, abi.encode( - config, 100e18, MAX_SLIPPAGE_INCREASE, MAX_SLIPPAGE_INCREASE, ActionConstants.MSG_SENDER, ZERO_BYTES + config.poolKey, + config.tickLower, + config.tickUpper, + 100e18, + MAX_SLIPPAGE_INCREASE, + MAX_SLIPPAGE_INCREASE, + ActionConstants.MSG_SENDER, + ZERO_BYTES ) ); bytes memory actions = planner.finalizeModifyLiquidityWithClose(config.poolKey); @@ -215,7 +222,7 @@ contract PositionManagerMulticallTest is Test, Permit2SignatureHelpers, PosmTest vm.prank(bob); lpm.multicall(calls); - uint256 liquidity = lpm.getPositionLiquidity(tokenId, config); + uint256 liquidity = lpm.getPositionLiquidity(tokenId); assertEq(liquidity, liquidityAlice - liquidityToRemove); } @@ -256,7 +263,7 @@ contract PositionManagerMulticallTest is Test, Permit2SignatureHelpers, PosmTest vm.prank(bob); lpm.multicall(calls); - uint256 liquidity = lpm.getPositionLiquidity(tokenId, config); + uint256 liquidity = lpm.getPositionLiquidity(tokenId); (_amount,,) = permit2.allowance(address(bob), Currency.unwrap(currency0), address(lpm)); @@ -312,7 +319,7 @@ contract PositionManagerMulticallTest is Test, Permit2SignatureHelpers, PosmTest vm.prank(bob); lpm.multicall(calls); - uint256 liquidity = lpm.getPositionLiquidity(tokenId, config); + uint256 liquidity = lpm.getPositionLiquidity(tokenId); (_amount0,,) = permit2.allowance(address(bob), Currency.unwrap(currency0), address(lpm)); (_amount1,,) = permit2.allowance(address(bob), Currency.unwrap(currency1), address(lpm)); diff --git a/test/position-managers/PositionManager.notifier.t.sol b/test/position-managers/PositionManager.notifier.t.sol index 9a88b22aa..cf5591bc1 100644 --- a/test/position-managers/PositionManager.notifier.t.sol +++ b/test/position-managers/PositionManager.notifier.t.sol @@ -240,7 +240,16 @@ contract PositionManagerNotifierTest is Test, PosmTestSetup, GasSnapshot { Plan memory plan = Planner.init(); plan.add( Actions.MINT_POSITION, - abi.encode(config, 100e18, MAX_SLIPPAGE_INCREASE, MAX_SLIPPAGE_INCREASE, address(this), ZERO_BYTES) + abi.encode( + config.poolKey, + config.tickLower, + config.tickUpper, + 100e18, + MAX_SLIPPAGE_INCREASE, + MAX_SLIPPAGE_INCREASE, + address(this), + ZERO_BYTES + ) ); bytes memory actions = plan.finalizeModifyLiquidityWithSettlePair(config.poolKey); @@ -251,7 +260,7 @@ contract PositionManagerNotifierTest is Test, PosmTestSetup, GasSnapshot { lpm.multicall(calls); - uint256 liquidity = lpm.getPositionLiquidity(tokenId, config); + uint256 liquidity = lpm.getPositionLiquidity(tokenId); assertEq(liquidity, 100e18); assertEq(sub.notifySubscribeCount(), 1); @@ -267,7 +276,16 @@ contract PositionManagerNotifierTest is Test, PosmTestSetup, GasSnapshot { Plan memory plan = Planner.init(); plan.add( Actions.MINT_POSITION, - abi.encode(config, 100e18, MAX_SLIPPAGE_INCREASE, MAX_SLIPPAGE_INCREASE, address(this), ZERO_BYTES) + abi.encode( + config.poolKey, + config.tickLower, + config.tickUpper, + 100e18, + MAX_SLIPPAGE_INCREASE, + MAX_SLIPPAGE_INCREASE, + address(this), + ZERO_BYTES + ) ); bytes memory actions = plan.finalizeModifyLiquidityWithSettlePair(config.poolKey); @@ -287,7 +305,7 @@ contract PositionManagerNotifierTest is Test, PosmTestSetup, GasSnapshot { lpm.multicall(calls); - uint256 liquidity = lpm.getPositionLiquidity(tokenId, config); + uint256 liquidity = lpm.getPositionLiquidity(tokenId); assertEq(liquidity, 110e18); assertEq(sub.notifySubscribeCount(), 1); diff --git a/test/position-managers/PositionManager.t.sol b/test/position-managers/PositionManager.t.sol index 0732212c7..ad0ee4961 100644 --- a/test/position-managers/PositionManager.t.sol +++ b/test/position-managers/PositionManager.t.sol @@ -115,7 +115,7 @@ contract PositionManagerTest is Test, PosmTestSetup, LiquidityFuzzers { assertEq(lpm.nextTokenId(), 2); assertEq(lpm.ownerOf(tokenId), address(this)); - uint256 liquidity = lpm.getPositionLiquidity(tokenId, config); + uint256 liquidity = lpm.getPositionLiquidity(tokenId); assertEq(liquidity, uint256(params.liquidityDelta)); assertEq(balance0Before - currency0.balanceOfSelf(), uint256(int256(-delta.amount0())), "incorrect amount0"); @@ -229,7 +229,16 @@ contract PositionManagerTest is Test, PosmTestSetup, LiquidityFuzzers { Plan memory planner = Planner.init(); planner.add( Actions.MINT_POSITION, - abi.encode(config, liquidityToAdd, MAX_SLIPPAGE_INCREASE, MAX_SLIPPAGE_INCREASE, address(this), ZERO_BYTES) + abi.encode( + config.poolKey, + config.tickLower, + config.tickUpper, + liquidityToAdd, + MAX_SLIPPAGE_INCREASE, + MAX_SLIPPAGE_INCREASE, + address(this), + ZERO_BYTES + ) ); planner.add(Actions.CLEAR_OR_TAKE, abi.encode(key.currency0, type(uint256).max)); planner.add(Actions.CLEAR_OR_TAKE, abi.encode(key.currency1, type(uint256).max)); @@ -320,7 +329,7 @@ contract PositionManagerTest is Test, PosmTestSetup, LiquidityFuzzers { assertEq(tokenId, 1); assertEq(lpm.ownerOf(1), address(this)); - uint256 liquidity = lpm.getPositionLiquidity(tokenId, config); + uint256 liquidity = lpm.getPositionLiquidity(tokenId); assertEq(liquidity, uint256(params.liquidityDelta)); @@ -335,7 +344,7 @@ contract PositionManagerTest is Test, PosmTestSetup, LiquidityFuzzers { burn(tokenId, config, ZERO_BYTES); assertEq(numDeltas, hook.numberDeltasReturned()); - liquidity = lpm.getPositionLiquidity(tokenId, config); + liquidity = lpm.getPositionLiquidity(tokenId); assertEq(liquidity, 0); @@ -364,7 +373,7 @@ contract PositionManagerTest is Test, PosmTestSetup, LiquidityFuzzers { assertEq(tokenId, 1); assertEq(lpm.ownerOf(1), address(this)); - uint256 liquidity = lpm.getPositionLiquidity(tokenId, config); + uint256 liquidity = lpm.getPositionLiquidity(tokenId); assertEq(liquidity, uint256(params.liquidityDelta)); @@ -386,7 +395,7 @@ contract PositionManagerTest is Test, PosmTestSetup, LiquidityFuzzers { assertEq(uint256(int256(deltaBurn.amount0())), amount0); assertEq(uint256(int256(deltaBurn.amount1())), amount1); - liquidity = lpm.getPositionLiquidity(tokenId, config); + liquidity = lpm.getPositionLiquidity(tokenId); assertEq(liquidity, 0); @@ -478,7 +487,7 @@ contract PositionManagerTest is Test, PosmTestSetup, LiquidityFuzzers { decreaseLiquidity(tokenId, config, decreaseLiquidityDelta, ZERO_BYTES); BalanceDelta delta = getLastDelta(); - uint256 liquidity = lpm.getPositionLiquidity(tokenId, config); + uint256 liquidity = lpm.getPositionLiquidity(tokenId); assertEq(liquidity, uint256(params.liquidityDelta) - decreaseLiquidityDelta); assertEq(currency0.balanceOfSelf(), balance0Before + uint256(uint128(delta.amount0()))); @@ -512,7 +521,7 @@ contract PositionManagerTest is Test, PosmTestSetup, LiquidityFuzzers { lpm.modifyLiquidities(calls, _deadline); - uint256 liquidity = lpm.getPositionLiquidity(tokenId, config); + uint256 liquidity = lpm.getPositionLiquidity(tokenId); assertEq(liquidity, uint256(params.liquidityDelta) - decreaseLiquidityDelta); // did not recieve tokens, as they were forfeited with CLEAR @@ -587,7 +596,7 @@ contract PositionManagerTest is Test, PosmTestSetup, LiquidityFuzzers { uint256 balance1Before = currency1.balanceOfSelf(); decreaseLiquidity(tokenId, config, decreaseLiquidityDelta, ZERO_BYTES); - uint256 liquidity = lpm.getPositionLiquidity(tokenId, config); + uint256 liquidity = lpm.getPositionLiquidity(tokenId); assertEq(liquidity, uint256(params.liquidityDelta) - decreaseLiquidityDelta); @@ -688,7 +697,7 @@ contract PositionManagerTest is Test, PosmTestSetup, LiquidityFuzzers { decreaseLiquidity(tokenId, config, decreaseLiquidityDelta, ZERO_BYTES); BalanceDelta delta = getLastDelta(); - uint256 liquidity = lpm.getPositionLiquidity(tokenId, config); + uint256 liquidity = lpm.getPositionLiquidity(tokenId); assertEq(liquidity, uint256(params.liquidityDelta) - decreaseLiquidityDelta); @@ -773,7 +782,7 @@ contract PositionManagerTest is Test, PosmTestSetup, LiquidityFuzzers { vm.stopPrank(); // position liquidity increased - uint256 newLiq = lpm.getPositionLiquidity(tokenId, config); + uint256 newLiq = lpm.getPositionLiquidity(tokenId); assertEq(newLiq, liquidity + liquidityToAdd); // alice paid the tokens @@ -815,7 +824,7 @@ contract PositionManagerTest is Test, PosmTestSetup, LiquidityFuzzers { { // position liquidity decreased - uint256 newLiq = lpm.getPositionLiquidity(tokenId, config); + uint256 newLiq = lpm.getPositionLiquidity(tokenId); assertEq(newLiq, liquidity - liquidityToRemove); } @@ -912,7 +921,14 @@ contract PositionManagerTest is Test, PosmTestSetup, LiquidityFuzzers { plan.add( Actions.MINT_POSITION, abi.encode( - configMint, 1e18, MAX_SLIPPAGE_INCREASE, MAX_SLIPPAGE_INCREASE, ActionConstants.MSG_SENDER, ZERO_BYTES + configMint.poolKey, + configMint.tickLower, + configMint.tickUpper, + 1e18, + MAX_SLIPPAGE_INCREASE, + MAX_SLIPPAGE_INCREASE, + ActionConstants.MSG_SENDER, + ZERO_BYTES ) ); plan.add(Actions.TAKE, abi.encode(key.currency0, ActionConstants.MSG_SENDER, ActionConstants.OPEN_DELTA)); @@ -933,14 +949,5 @@ contract PositionManagerTest is Test, PosmTestSetup, LiquidityFuzzers { assertLt(uint256(int256(deltaDecrease.amount1())), uint256(int256(-deltaMint.amount1()))); // amount1 in the second position was greater than amount1 in the first position } - function test_mint_emits_event() public { - PositionConfig memory config = PositionConfig({poolKey: key, tickLower: -60, tickUpper: 60}); - uint256 tokenId = lpm.nextTokenId(); - - vm.expectEmit(true, false, false, true, address(lpm)); - emit IPositionManager.MintPosition(tokenId, config); - mint(config, 1e18, ActionConstants.MSG_SENDER, ZERO_BYTES); - } - function test_mint_slippageRevert() public {} } diff --git a/test/shared/LiquidityOperations.sol b/test/shared/LiquidityOperations.sol index 5490872af..051e07c66 100644 --- a/test/shared/LiquidityOperations.sol +++ b/test/shared/LiquidityOperations.sol @@ -99,7 +99,19 @@ abstract contract LiquidityOperations is CommonBase { bytes memory hookData ) internal pure returns (bytes memory) { Plan memory planner = Planner.init(); - planner.add(Actions.MINT_POSITION, abi.encode(config, liquidity, amount0Max, amount1Max, recipient, hookData)); + planner.add( + Actions.MINT_POSITION, + abi.encode( + config.poolKey, + config.tickLower, + config.tickUpper, + liquidity, + amount0Max, + amount1Max, + recipient, + hookData + ) + ); return planner.finalizeModifyLiquidityWithClose(config.poolKey); } diff --git a/test/shared/fuzz/LiquidityFuzzers.sol b/test/shared/fuzz/LiquidityFuzzers.sol index 02d6583f4..1182606ae 100644 --- a/test/shared/fuzz/LiquidityFuzzers.sol +++ b/test/shared/fuzz/LiquidityFuzzers.sol @@ -31,7 +31,9 @@ contract LiquidityFuzzers is Fuzzers { Plan memory planner = Planner.init().add( Actions.MINT_POSITION, abi.encode( - config, + config.poolKey, + config.tickLower, + config.tickUpper, uint256(params.liquidityDelta), MAX_SLIPPAGE_INCREASE, MAX_SLIPPAGE_INCREASE, From 8eb66b3148fb3a686801305b9b91ec424886407e Mon Sep 17 00:00:00 2001 From: Sara Reynolds Date: Thu, 29 Aug 2024 00:44:05 -0400 Subject: [PATCH 05/45] use packed, no via-ir, but stack too deep concerning for tests --- .../BaseActionsRouter_mock10commands.snap | 2 +- ...p_settleFromCaller_takeAllToMsgSender.snap | 2 +- ...eFromCaller_takeAllToSpecifiedAddress.snap | 2 +- ..._settleWithBalance_takeAllToMsgSender.snap | 2 +- ...WithBalance_takeAllToSpecifiedAddress.snap | 2 +- .../PositionManager_burn_empty.snap | 2 +- .../PositionManager_burn_empty_native.snap | 2 +- ...anager_burn_nonEmpty_native_withClose.snap | 2 +- ...ger_burn_nonEmpty_native_withTakePair.snap | 2 +- ...sitionManager_burn_nonEmpty_withClose.snap | 2 +- ...ionManager_burn_nonEmpty_withTakePair.snap | 2 +- .../PositionManager_collect_native.snap | 2 +- .../PositionManager_collect_sameRange.snap | 2 +- .../PositionManager_collect_withClose.snap | 2 +- .../PositionManager_collect_withTakePair.snap | 2 +- ...itionManager_decreaseLiquidity_native.snap | 2 +- ...onManager_decreaseLiquidity_withClose.snap | 2 +- ...anager_decreaseLiquidity_withTakePair.snap | 2 +- .../PositionManager_decrease_burnEmpty.snap | 2 +- ...tionManager_decrease_burnEmpty_native.snap | 2 +- ...nager_decrease_sameRange_allLiquidity.snap | 2 +- .../PositionManager_decrease_take_take.snap | 2 +- ...ger_increaseLiquidity_erc20_withClose.snap | 2 +- ...ncreaseLiquidity_erc20_withSettlePair.snap | 2 +- ...itionManager_increaseLiquidity_native.snap | 2 +- ...crease_autocompoundExactUnclaimedFees.snap | 2 +- ...increase_autocompoundExcessFeesCredit.snap | 2 +- ...ger_increase_autocompound_clearExcess.snap | 2 +- .../PositionManager_mint_native.snap | 2 +- ...anager_mint_nativeWithSweep_withClose.snap | 2 +- ...r_mint_nativeWithSweep_withSettlePair.snap | 2 +- .../PositionManager_mint_onSameTickLower.snap | 2 +- .../PositionManager_mint_onSameTickUpper.snap | 2 +- .../PositionManager_mint_sameRange.snap | 2 +- ...nManager_mint_settleWithBalance_sweep.snap | 2 +- ...anager_mint_warmedPool_differentRange.snap | 2 +- .../PositionManager_mint_withClose.snap | 2 +- .../PositionManager_mint_withSettlePair.snap | 2 +- ...tionManager_multicall_initialize_mint.snap | 2 +- .forge-snapshots/PositionManager_permit.snap | 2 +- ...PositionManager_permit_secondPosition.snap | 2 +- .../PositionManager_permit_twice.snap | 2 +- ...tateView_extsload_getFeeGrowthGlobals.snap | 2 +- ...StateView_extsload_getFeeGrowthInside.snap | 2 +- .../StateView_extsload_getLiquidity.snap | 2 +- .../StateView_extsload_getPositionInfo.snap | 2 +- ...ateView_extsload_getPositionLiquidity.snap | 2 +- .../StateView_extsload_getSlot0.snap | 2 +- .../StateView_extsload_getTickBitmap.snap | 2 +- ...View_extsload_getTickFeeGrowthOutside.snap | 2 +- .../StateView_extsload_getTickInfo.snap | 2 +- .../StateView_extsload_getTickLiquidity.snap | 2 +- .forge-snapshots/V4Router_Bytecode.snap | 2 +- .../V4Router_ExactIn1Hop_nativeIn.snap | 2 +- .../V4Router_ExactIn1Hop_nativeOut.snap | 2 +- .../V4Router_ExactIn1Hop_oneForZero.snap | 2 +- .../V4Router_ExactIn1Hop_zeroForOne.snap | 2 +- .forge-snapshots/V4Router_ExactIn2Hops.snap | 2 +- .../V4Router_ExactIn2Hops_nativeIn.snap | 2 +- .forge-snapshots/V4Router_ExactIn3Hops.snap | 2 +- .../V4Router_ExactIn3Hops_nativeIn.snap | 2 +- .../V4Router_ExactInputSingle.snap | 2 +- .../V4Router_ExactInputSingle_nativeIn.snap | 2 +- .../V4Router_ExactInputSingle_nativeOut.snap | 2 +- ...Router_ExactOut1Hop_nativeIn_sweepETH.snap | 2 +- .../V4Router_ExactOut1Hop_nativeOut.snap | 2 +- .../V4Router_ExactOut1Hop_oneForZero.snap | 2 +- .../V4Router_ExactOut1Hop_zeroForOne.snap | 2 +- .forge-snapshots/V4Router_ExactOut2Hops.snap | 2 +- .../V4Router_ExactOut2Hops_nativeIn.snap | 2 +- .forge-snapshots/V4Router_ExactOut3Hops.snap | 2 +- .../V4Router_ExactOut3Hops_nativeIn.snap | 2 +- .../V4Router_ExactOut3Hops_nativeOut.snap | 2 +- .../V4Router_ExactOutputSingle.snap | 2 +- ...r_ExactOutputSingle_nativeIn_sweepETH.snap | 2 +- .../V4Router_ExactOutputSingle_nativeOut.snap | 2 +- src/PositionManager.sol | 125 +++++++++--------- src/base/Notifier.sol | 15 +-- src/interfaces/INotifier.sol | 5 - src/interfaces/IPositionManager.sol | 6 +- src/libraries/PositionInfoLibrary.sol | 109 ++++++++++++--- test/libraries/CalldataDecoder.t.sol | 86 ++++++------ test/libraries/PositionInfoLibrary.t.sol | 78 +++++++++++ .../PositionManager.notifier.t.sol | 28 ++-- test/shared/fuzz/LiquidityFuzzers.sol | 17 ++- 85 files changed, 380 insertions(+), 241 deletions(-) create mode 100644 test/libraries/PositionInfoLibrary.t.sol diff --git a/.forge-snapshots/BaseActionsRouter_mock10commands.snap b/.forge-snapshots/BaseActionsRouter_mock10commands.snap index 7ee3d714b..2b46e583b 100644 --- a/.forge-snapshots/BaseActionsRouter_mock10commands.snap +++ b/.forge-snapshots/BaseActionsRouter_mock10commands.snap @@ -1 +1 @@ -59216 \ No newline at end of file +61749 \ No newline at end of file diff --git a/.forge-snapshots/Payments_swap_settleFromCaller_takeAllToMsgSender.snap b/.forge-snapshots/Payments_swap_settleFromCaller_takeAllToMsgSender.snap index 02231759c..8f5800369 100644 --- a/.forge-snapshots/Payments_swap_settleFromCaller_takeAllToMsgSender.snap +++ b/.forge-snapshots/Payments_swap_settleFromCaller_takeAllToMsgSender.snap @@ -1 +1 @@ -130322 \ No newline at end of file +133206 \ No newline at end of file diff --git a/.forge-snapshots/Payments_swap_settleFromCaller_takeAllToSpecifiedAddress.snap b/.forge-snapshots/Payments_swap_settleFromCaller_takeAllToSpecifiedAddress.snap index 108caf432..0356d3854 100644 --- a/.forge-snapshots/Payments_swap_settleFromCaller_takeAllToSpecifiedAddress.snap +++ b/.forge-snapshots/Payments_swap_settleFromCaller_takeAllToSpecifiedAddress.snap @@ -1 +1 @@ -132215 \ No newline at end of file +135242 \ No newline at end of file diff --git a/.forge-snapshots/Payments_swap_settleWithBalance_takeAllToMsgSender.snap b/.forge-snapshots/Payments_swap_settleWithBalance_takeAllToMsgSender.snap index 7f1934bc8..8f190ac59 100644 --- a/.forge-snapshots/Payments_swap_settleWithBalance_takeAllToMsgSender.snap +++ b/.forge-snapshots/Payments_swap_settleWithBalance_takeAllToMsgSender.snap @@ -1 +1 @@ -124449 \ No newline at end of file +127716 \ No newline at end of file diff --git a/.forge-snapshots/Payments_swap_settleWithBalance_takeAllToSpecifiedAddress.snap b/.forge-snapshots/Payments_swap_settleWithBalance_takeAllToSpecifiedAddress.snap index 48033d8d2..f7b6cea9b 100644 --- a/.forge-snapshots/Payments_swap_settleWithBalance_takeAllToSpecifiedAddress.snap +++ b/.forge-snapshots/Payments_swap_settleWithBalance_takeAllToSpecifiedAddress.snap @@ -1 +1 @@ -124591 \ No newline at end of file +127854 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_burn_empty.snap b/.forge-snapshots/PositionManager_burn_empty.snap index cc2a0bf38..103f8cbee 100644 --- a/.forge-snapshots/PositionManager_burn_empty.snap +++ b/.forge-snapshots/PositionManager_burn_empty.snap @@ -1 +1 @@ -50624 \ No newline at end of file +49954 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_burn_empty_native.snap b/.forge-snapshots/PositionManager_burn_empty_native.snap index cc2a0bf38..103f8cbee 100644 --- a/.forge-snapshots/PositionManager_burn_empty_native.snap +++ b/.forge-snapshots/PositionManager_burn_empty_native.snap @@ -1 +1 @@ -50624 \ No newline at end of file +49954 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_burn_nonEmpty_native_withClose.snap b/.forge-snapshots/PositionManager_burn_nonEmpty_native_withClose.snap index 641f713bc..4a524dd81 100644 --- a/.forge-snapshots/PositionManager_burn_nonEmpty_native_withClose.snap +++ b/.forge-snapshots/PositionManager_burn_nonEmpty_native_withClose.snap @@ -1 +1 @@ -125769 \ No newline at end of file +126207 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_burn_nonEmpty_native_withTakePair.snap b/.forge-snapshots/PositionManager_burn_nonEmpty_native_withTakePair.snap index 04960f34a..2e228131e 100644 --- a/.forge-snapshots/PositionManager_burn_nonEmpty_native_withTakePair.snap +++ b/.forge-snapshots/PositionManager_burn_nonEmpty_native_withTakePair.snap @@ -1 +1 @@ -125292 \ No newline at end of file +125704 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_burn_nonEmpty_withClose.snap b/.forge-snapshots/PositionManager_burn_nonEmpty_withClose.snap index bc35833dc..b11d0da00 100644 --- a/.forge-snapshots/PositionManager_burn_nonEmpty_withClose.snap +++ b/.forge-snapshots/PositionManager_burn_nonEmpty_withClose.snap @@ -1 +1 @@ -132620 \ No newline at end of file +133103 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_burn_nonEmpty_withTakePair.snap b/.forge-snapshots/PositionManager_burn_nonEmpty_withTakePair.snap index 38dcf607e..bb2d92b47 100644 --- a/.forge-snapshots/PositionManager_burn_nonEmpty_withTakePair.snap +++ b/.forge-snapshots/PositionManager_burn_nonEmpty_withTakePair.snap @@ -1 +1 @@ -132143 \ No newline at end of file +132600 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_collect_native.snap b/.forge-snapshots/PositionManager_collect_native.snap index 8470abb8d..21cc20540 100644 --- a/.forge-snapshots/PositionManager_collect_native.snap +++ b/.forge-snapshots/PositionManager_collect_native.snap @@ -1 +1 @@ -146332 \ No newline at end of file +146341 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_collect_sameRange.snap b/.forge-snapshots/PositionManager_collect_sameRange.snap index 2155cfda8..a6bdd7250 100644 --- a/.forge-snapshots/PositionManager_collect_sameRange.snap +++ b/.forge-snapshots/PositionManager_collect_sameRange.snap @@ -1 +1 @@ -154895 \ No newline at end of file +154961 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_collect_withClose.snap b/.forge-snapshots/PositionManager_collect_withClose.snap index 2155cfda8..a6bdd7250 100644 --- a/.forge-snapshots/PositionManager_collect_withClose.snap +++ b/.forge-snapshots/PositionManager_collect_withClose.snap @@ -1 +1 @@ -154895 \ No newline at end of file +154961 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_collect_withTakePair.snap b/.forge-snapshots/PositionManager_collect_withTakePair.snap index 4ffa46d23..a96db4e2b 100644 --- a/.forge-snapshots/PositionManager_collect_withTakePair.snap +++ b/.forge-snapshots/PositionManager_collect_withTakePair.snap @@ -1 +1 @@ -154311 \ No newline at end of file +154345 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_decreaseLiquidity_native.snap b/.forge-snapshots/PositionManager_decreaseLiquidity_native.snap index acf25f342..8550686dc 100644 --- a/.forge-snapshots/PositionManager_decreaseLiquidity_native.snap +++ b/.forge-snapshots/PositionManager_decreaseLiquidity_native.snap @@ -1 +1 @@ -112030 \ No newline at end of file +112548 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_decreaseLiquidity_withClose.snap b/.forge-snapshots/PositionManager_decreaseLiquidity_withClose.snap index cb6ebd61b..ed71e09b7 100644 --- a/.forge-snapshots/PositionManager_decreaseLiquidity_withClose.snap +++ b/.forge-snapshots/PositionManager_decreaseLiquidity_withClose.snap @@ -1 +1 @@ -119800 \ No newline at end of file +120504 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_decreaseLiquidity_withTakePair.snap b/.forge-snapshots/PositionManager_decreaseLiquidity_withTakePair.snap index 32821d90a..3bb7829f7 100644 --- a/.forge-snapshots/PositionManager_decreaseLiquidity_withTakePair.snap +++ b/.forge-snapshots/PositionManager_decreaseLiquidity_withTakePair.snap @@ -1 +1 @@ -119216 \ No newline at end of file +119888 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_decrease_burnEmpty.snap b/.forge-snapshots/PositionManager_decrease_burnEmpty.snap index 68c1d4dff..2621d201d 100644 --- a/.forge-snapshots/PositionManager_decrease_burnEmpty.snap +++ b/.forge-snapshots/PositionManager_decrease_burnEmpty.snap @@ -1 +1 @@ -135671 \ No newline at end of file +135944 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_decrease_burnEmpty_native.snap b/.forge-snapshots/PositionManager_decrease_burnEmpty_native.snap index 21a9fb028..c543dd683 100644 --- a/.forge-snapshots/PositionManager_decrease_burnEmpty_native.snap +++ b/.forge-snapshots/PositionManager_decrease_burnEmpty_native.snap @@ -1 +1 @@ -128820 \ No newline at end of file +129048 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_decrease_sameRange_allLiquidity.snap b/.forge-snapshots/PositionManager_decrease_sameRange_allLiquidity.snap index 8f0b97d20..02a21f0e3 100644 --- a/.forge-snapshots/PositionManager_decrease_sameRange_allLiquidity.snap +++ b/.forge-snapshots/PositionManager_decrease_sameRange_allLiquidity.snap @@ -1 +1 @@ -132499 \ No newline at end of file +133220 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_decrease_take_take.snap b/.forge-snapshots/PositionManager_decrease_take_take.snap index 94bad2417..09b8ea95e 100644 --- a/.forge-snapshots/PositionManager_decrease_take_take.snap +++ b/.forge-snapshots/PositionManager_decrease_take_take.snap @@ -1 +1 @@ -120384 \ No newline at end of file +121037 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_increaseLiquidity_erc20_withClose.snap b/.forge-snapshots/PositionManager_increaseLiquidity_erc20_withClose.snap index 5d1d75100..0048705c8 100644 --- a/.forge-snapshots/PositionManager_increaseLiquidity_erc20_withClose.snap +++ b/.forge-snapshots/PositionManager_increaseLiquidity_erc20_withClose.snap @@ -1 +1 @@ -159263 \ No newline at end of file +159211 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_increaseLiquidity_erc20_withSettlePair.snap b/.forge-snapshots/PositionManager_increaseLiquidity_erc20_withSettlePair.snap index 60025655d..5438704ab 100644 --- a/.forge-snapshots/PositionManager_increaseLiquidity_erc20_withSettlePair.snap +++ b/.forge-snapshots/PositionManager_increaseLiquidity_erc20_withSettlePair.snap @@ -1 +1 @@ -158313 \ No newline at end of file +158237 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_increaseLiquidity_native.snap b/.forge-snapshots/PositionManager_increaseLiquidity_native.snap index f8c4dc4e6..1cbfad85b 100644 --- a/.forge-snapshots/PositionManager_increaseLiquidity_native.snap +++ b/.forge-snapshots/PositionManager_increaseLiquidity_native.snap @@ -1 +1 @@ -141064 \ No newline at end of file +141239 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_increase_autocompoundExactUnclaimedFees.snap b/.forge-snapshots/PositionManager_increase_autocompoundExactUnclaimedFees.snap index c4be3a91b..262480fdc 100644 --- a/.forge-snapshots/PositionManager_increase_autocompoundExactUnclaimedFees.snap +++ b/.forge-snapshots/PositionManager_increase_autocompoundExactUnclaimedFees.snap @@ -1 +1 @@ -136744 \ No newline at end of file +137356 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_increase_autocompoundExcessFeesCredit.snap b/.forge-snapshots/PositionManager_increase_autocompoundExcessFeesCredit.snap index 8030bc024..d22c381d6 100644 --- a/.forge-snapshots/PositionManager_increase_autocompoundExcessFeesCredit.snap +++ b/.forge-snapshots/PositionManager_increase_autocompoundExcessFeesCredit.snap @@ -1 +1 @@ -177518 \ No newline at end of file +178272 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_increase_autocompound_clearExcess.snap b/.forge-snapshots/PositionManager_increase_autocompound_clearExcess.snap index 9a61b60ec..b2ad3c815 100644 --- a/.forge-snapshots/PositionManager_increase_autocompound_clearExcess.snap +++ b/.forge-snapshots/PositionManager_increase_autocompound_clearExcess.snap @@ -1 +1 @@ -148234 \ No newline at end of file +148228 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_mint_native.snap b/.forge-snapshots/PositionManager_mint_native.snap index f010f2200..30fb00004 100644 --- a/.forge-snapshots/PositionManager_mint_native.snap +++ b/.forge-snapshots/PositionManager_mint_native.snap @@ -1 +1 @@ -365281 \ No newline at end of file +366040 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_mint_nativeWithSweep_withClose.snap b/.forge-snapshots/PositionManager_mint_nativeWithSweep_withClose.snap index cefadff88..666a0da57 100644 --- a/.forge-snapshots/PositionManager_mint_nativeWithSweep_withClose.snap +++ b/.forge-snapshots/PositionManager_mint_nativeWithSweep_withClose.snap @@ -1 +1 @@ -373716 \ No newline at end of file +374532 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_mint_nativeWithSweep_withSettlePair.snap b/.forge-snapshots/PositionManager_mint_nativeWithSweep_withSettlePair.snap index 431fb88c0..07b59f08d 100644 --- a/.forge-snapshots/PositionManager_mint_nativeWithSweep_withSettlePair.snap +++ b/.forge-snapshots/PositionManager_mint_nativeWithSweep_withSettlePair.snap @@ -1 +1 @@ -373050 \ No newline at end of file +373834 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_mint_onSameTickLower.snap b/.forge-snapshots/PositionManager_mint_onSameTickLower.snap index a2df27ddb..3f680f37b 100644 --- a/.forge-snapshots/PositionManager_mint_onSameTickLower.snap +++ b/.forge-snapshots/PositionManager_mint_onSameTickLower.snap @@ -1 +1 @@ -322160 \ No newline at end of file +322676 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_mint_onSameTickUpper.snap b/.forge-snapshots/PositionManager_mint_onSameTickUpper.snap index 5ca4c7a30..3a062951b 100644 --- a/.forge-snapshots/PositionManager_mint_onSameTickUpper.snap +++ b/.forge-snapshots/PositionManager_mint_onSameTickUpper.snap @@ -1 +1 @@ -322827 \ No newline at end of file +323318 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_mint_sameRange.snap b/.forge-snapshots/PositionManager_mint_sameRange.snap index 1a08055af..7dfb320a0 100644 --- a/.forge-snapshots/PositionManager_mint_sameRange.snap +++ b/.forge-snapshots/PositionManager_mint_sameRange.snap @@ -1 +1 @@ -248405 \ No newline at end of file +248900 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_mint_settleWithBalance_sweep.snap b/.forge-snapshots/PositionManager_mint_settleWithBalance_sweep.snap index 20ba98567..33db8cb92 100644 --- a/.forge-snapshots/PositionManager_mint_settleWithBalance_sweep.snap +++ b/.forge-snapshots/PositionManager_mint_settleWithBalance_sweep.snap @@ -1 +1 @@ -419426 \ No newline at end of file +419970 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_mint_warmedPool_differentRange.snap b/.forge-snapshots/PositionManager_mint_warmedPool_differentRange.snap index 57c609caa..0ed39857d 100644 --- a/.forge-snapshots/PositionManager_mint_warmedPool_differentRange.snap +++ b/.forge-snapshots/PositionManager_mint_warmedPool_differentRange.snap @@ -1 +1 @@ -328182 \ No newline at end of file +328694 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_mint_withClose.snap b/.forge-snapshots/PositionManager_mint_withClose.snap index 070eccb44..d2f14e912 100644 --- a/.forge-snapshots/PositionManager_mint_withClose.snap +++ b/.forge-snapshots/PositionManager_mint_withClose.snap @@ -1 +1 @@ -420708 \ No newline at end of file +421240 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_mint_withSettlePair.snap b/.forge-snapshots/PositionManager_mint_withSettlePair.snap index ef82db672..352a0a7f3 100644 --- a/.forge-snapshots/PositionManager_mint_withSettlePair.snap +++ b/.forge-snapshots/PositionManager_mint_withSettlePair.snap @@ -1 +1 @@ -419876 \ No newline at end of file +420380 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_multicall_initialize_mint.snap b/.forge-snapshots/PositionManager_multicall_initialize_mint.snap index 682fa5e9d..ab3b994c6 100644 --- a/.forge-snapshots/PositionManager_multicall_initialize_mint.snap +++ b/.forge-snapshots/PositionManager_multicall_initialize_mint.snap @@ -1 +1 @@ -464902 \ No newline at end of file +465629 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_permit.snap b/.forge-snapshots/PositionManager_permit.snap index 3133c57c0..81715a673 100644 --- a/.forge-snapshots/PositionManager_permit.snap +++ b/.forge-snapshots/PositionManager_permit.snap @@ -1 +1 @@ -79089 \ No newline at end of file +79484 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_permit_secondPosition.snap b/.forge-snapshots/PositionManager_permit_secondPosition.snap index ac80e5b2f..bd214aa76 100644 --- a/.forge-snapshots/PositionManager_permit_secondPosition.snap +++ b/.forge-snapshots/PositionManager_permit_secondPosition.snap @@ -1 +1 @@ -61977 \ No newline at end of file +62372 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_permit_twice.snap b/.forge-snapshots/PositionManager_permit_twice.snap index 2a4903c78..c0e053f63 100644 --- a/.forge-snapshots/PositionManager_permit_twice.snap +++ b/.forge-snapshots/PositionManager_permit_twice.snap @@ -1 +1 @@ -44865 \ No newline at end of file +45260 \ No newline at end of file diff --git a/.forge-snapshots/StateView_extsload_getFeeGrowthGlobals.snap b/.forge-snapshots/StateView_extsload_getFeeGrowthGlobals.snap index 920727d2e..d659d28b1 100644 --- a/.forge-snapshots/StateView_extsload_getFeeGrowthGlobals.snap +++ b/.forge-snapshots/StateView_extsload_getFeeGrowthGlobals.snap @@ -1 +1 @@ -2256 \ No newline at end of file +2376 \ No newline at end of file diff --git a/.forge-snapshots/StateView_extsload_getFeeGrowthInside.snap b/.forge-snapshots/StateView_extsload_getFeeGrowthInside.snap index 6df672447..37017731e 100644 --- a/.forge-snapshots/StateView_extsload_getFeeGrowthInside.snap +++ b/.forge-snapshots/StateView_extsload_getFeeGrowthInside.snap @@ -1 +1 @@ -7994 \ No newline at end of file +8455 \ No newline at end of file diff --git a/.forge-snapshots/StateView_extsload_getLiquidity.snap b/.forge-snapshots/StateView_extsload_getLiquidity.snap index a900d0b24..5ca63cd4c 100644 --- a/.forge-snapshots/StateView_extsload_getLiquidity.snap +++ b/.forge-snapshots/StateView_extsload_getLiquidity.snap @@ -1 +1 @@ -1399 \ No newline at end of file +1487 \ No newline at end of file diff --git a/.forge-snapshots/StateView_extsload_getPositionInfo.snap b/.forge-snapshots/StateView_extsload_getPositionInfo.snap index c173e591f..6f9954563 100644 --- a/.forge-snapshots/StateView_extsload_getPositionInfo.snap +++ b/.forge-snapshots/StateView_extsload_getPositionInfo.snap @@ -1 +1 @@ -2826 \ No newline at end of file +2905 \ No newline at end of file diff --git a/.forge-snapshots/StateView_extsload_getPositionLiquidity.snap b/.forge-snapshots/StateView_extsload_getPositionLiquidity.snap index 7f3589da4..2dd447630 100644 --- a/.forge-snapshots/StateView_extsload_getPositionLiquidity.snap +++ b/.forge-snapshots/StateView_extsload_getPositionLiquidity.snap @@ -1 +1 @@ -1651 \ No newline at end of file +1724 \ No newline at end of file diff --git a/.forge-snapshots/StateView_extsload_getSlot0.snap b/.forge-snapshots/StateView_extsload_getSlot0.snap index 585cf67a0..a35ae7300 100644 --- a/.forge-snapshots/StateView_extsload_getSlot0.snap +++ b/.forge-snapshots/StateView_extsload_getSlot0.snap @@ -1 +1 @@ -1446 \ No newline at end of file +1584 \ No newline at end of file diff --git a/.forge-snapshots/StateView_extsload_getTickBitmap.snap b/.forge-snapshots/StateView_extsload_getTickBitmap.snap index 05fd8282c..7f29ad284 100644 --- a/.forge-snapshots/StateView_extsload_getTickBitmap.snap +++ b/.forge-snapshots/StateView_extsload_getTickBitmap.snap @@ -1 +1 @@ -1392 \ No newline at end of file +1682 \ No newline at end of file diff --git a/.forge-snapshots/StateView_extsload_getTickFeeGrowthOutside.snap b/.forge-snapshots/StateView_extsload_getTickFeeGrowthOutside.snap index 7a03b74c0..00c1535ae 100644 --- a/.forge-snapshots/StateView_extsload_getTickFeeGrowthOutside.snap +++ b/.forge-snapshots/StateView_extsload_getTickFeeGrowthOutside.snap @@ -1 +1 @@ -2543 \ No newline at end of file +2734 \ No newline at end of file diff --git a/.forge-snapshots/StateView_extsload_getTickInfo.snap b/.forge-snapshots/StateView_extsload_getTickInfo.snap index f538595cb..a6fea8d18 100644 --- a/.forge-snapshots/StateView_extsload_getTickInfo.snap +++ b/.forge-snapshots/StateView_extsload_getTickInfo.snap @@ -1 +1 @@ -2758 \ No newline at end of file +3068 \ No newline at end of file diff --git a/.forge-snapshots/StateView_extsload_getTickLiquidity.snap b/.forge-snapshots/StateView_extsload_getTickLiquidity.snap index 44e788259..fdef1d023 100644 --- a/.forge-snapshots/StateView_extsload_getTickLiquidity.snap +++ b/.forge-snapshots/StateView_extsload_getTickLiquidity.snap @@ -1 +1 @@ -1646 \ No newline at end of file +1879 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_Bytecode.snap b/.forge-snapshots/V4Router_Bytecode.snap index e7ebb4512..e88f6975d 100644 --- a/.forge-snapshots/V4Router_Bytecode.snap +++ b/.forge-snapshots/V4Router_Bytecode.snap @@ -1 +1 @@ -6754 \ No newline at end of file +8389 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactIn1Hop_nativeIn.snap b/.forge-snapshots/V4Router_ExactIn1Hop_nativeIn.snap index a803c6eeb..ce9be6341 100644 --- a/.forge-snapshots/V4Router_ExactIn1Hop_nativeIn.snap +++ b/.forge-snapshots/V4Router_ExactIn1Hop_nativeIn.snap @@ -1 +1 @@ -116128 \ No newline at end of file +119501 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactIn1Hop_nativeOut.snap b/.forge-snapshots/V4Router_ExactIn1Hop_nativeOut.snap index 2619b0057..726ce00a0 100644 --- a/.forge-snapshots/V4Router_ExactIn1Hop_nativeOut.snap +++ b/.forge-snapshots/V4Router_ExactIn1Hop_nativeOut.snap @@ -1 +1 @@ -116103 \ No newline at end of file +118696 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactIn1Hop_oneForZero.snap b/.forge-snapshots/V4Router_ExactIn1Hop_oneForZero.snap index 8050eab6e..4cda0de8d 100644 --- a/.forge-snapshots/V4Router_ExactIn1Hop_oneForZero.snap +++ b/.forge-snapshots/V4Router_ExactIn1Hop_oneForZero.snap @@ -1 +1 @@ -124918 \ No newline at end of file +127568 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactIn1Hop_zeroForOne.snap b/.forge-snapshots/V4Router_ExactIn1Hop_zeroForOne.snap index edac16407..988f465e4 100644 --- a/.forge-snapshots/V4Router_ExactIn1Hop_zeroForOne.snap +++ b/.forge-snapshots/V4Router_ExactIn1Hop_zeroForOne.snap @@ -1 +1 @@ -131016 \ No newline at end of file +134398 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactIn2Hops.snap b/.forge-snapshots/V4Router_ExactIn2Hops.snap index e120153ac..0ec74b474 100644 --- a/.forge-snapshots/V4Router_ExactIn2Hops.snap +++ b/.forge-snapshots/V4Router_ExactIn2Hops.snap @@ -1 +1 @@ -180668 \ No newline at end of file +185939 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactIn2Hops_nativeIn.snap b/.forge-snapshots/V4Router_ExactIn2Hops_nativeIn.snap index 8cf17fbf2..1ce5a1797 100644 --- a/.forge-snapshots/V4Router_ExactIn2Hops_nativeIn.snap +++ b/.forge-snapshots/V4Router_ExactIn2Hops_nativeIn.snap @@ -1 +1 @@ -171870 \ No newline at end of file +177874 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactIn3Hops.snap b/.forge-snapshots/V4Router_ExactIn3Hops.snap index 65dc4c51a..96e4b6a49 100644 --- a/.forge-snapshots/V4Router_ExactIn3Hops.snap +++ b/.forge-snapshots/V4Router_ExactIn3Hops.snap @@ -1 +1 @@ -230299 \ No newline at end of file +237461 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactIn3Hops_nativeIn.snap b/.forge-snapshots/V4Router_ExactIn3Hops_nativeIn.snap index 95c9cf6cd..456844ad2 100644 --- a/.forge-snapshots/V4Router_ExactIn3Hops_nativeIn.snap +++ b/.forge-snapshots/V4Router_ExactIn3Hops_nativeIn.snap @@ -1 +1 @@ -221525 \ No newline at end of file +229420 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactInputSingle.snap b/.forge-snapshots/V4Router_ExactInputSingle.snap index 02231759c..8f5800369 100644 --- a/.forge-snapshots/V4Router_ExactInputSingle.snap +++ b/.forge-snapshots/V4Router_ExactInputSingle.snap @@ -1 +1 @@ -130322 \ No newline at end of file +133206 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactInputSingle_nativeIn.snap b/.forge-snapshots/V4Router_ExactInputSingle_nativeIn.snap index 674bbdcff..e8ecc585d 100644 --- a/.forge-snapshots/V4Router_ExactInputSingle_nativeIn.snap +++ b/.forge-snapshots/V4Router_ExactInputSingle_nativeIn.snap @@ -1 +1 @@ -115434 \ No newline at end of file +118309 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactInputSingle_nativeOut.snap b/.forge-snapshots/V4Router_ExactInputSingle_nativeOut.snap index 859f9eedd..06d583087 100644 --- a/.forge-snapshots/V4Router_ExactInputSingle_nativeOut.snap +++ b/.forge-snapshots/V4Router_ExactInputSingle_nativeOut.snap @@ -1 +1 @@ -115378 \ No newline at end of file +117482 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactOut1Hop_nativeIn_sweepETH.snap b/.forge-snapshots/V4Router_ExactOut1Hop_nativeIn_sweepETH.snap index 3b3f20e07..33a2bacbf 100644 --- a/.forge-snapshots/V4Router_ExactOut1Hop_nativeIn_sweepETH.snap +++ b/.forge-snapshots/V4Router_ExactOut1Hop_nativeIn_sweepETH.snap @@ -1 +1 @@ -121957 \ No newline at end of file +125230 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactOut1Hop_nativeOut.snap b/.forge-snapshots/V4Router_ExactOut1Hop_nativeOut.snap index ba60eed24..089e6949e 100644 --- a/.forge-snapshots/V4Router_ExactOut1Hop_nativeOut.snap +++ b/.forge-snapshots/V4Router_ExactOut1Hop_nativeOut.snap @@ -1 +1 @@ -116833 \ No newline at end of file +119508 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactOut1Hop_oneForZero.snap b/.forge-snapshots/V4Router_ExactOut1Hop_oneForZero.snap index 19bf34fcd..5cbd7cccc 100644 --- a/.forge-snapshots/V4Router_ExactOut1Hop_oneForZero.snap +++ b/.forge-snapshots/V4Router_ExactOut1Hop_oneForZero.snap @@ -1 +1 @@ -125648 \ No newline at end of file +128380 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactOut1Hop_zeroForOne.snap b/.forge-snapshots/V4Router_ExactOut1Hop_zeroForOne.snap index e92853a80..5c8ee5092 100644 --- a/.forge-snapshots/V4Router_ExactOut1Hop_zeroForOne.snap +++ b/.forge-snapshots/V4Router_ExactOut1Hop_zeroForOne.snap @@ -1 +1 @@ -129868 \ No newline at end of file +133181 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactOut2Hops.snap b/.forge-snapshots/V4Router_ExactOut2Hops.snap index 5bae3b549..a3913eb58 100644 --- a/.forge-snapshots/V4Router_ExactOut2Hops.snap +++ b/.forge-snapshots/V4Router_ExactOut2Hops.snap @@ -1 +1 @@ -180036 \ No newline at end of file +185325 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactOut2Hops_nativeIn.snap b/.forge-snapshots/V4Router_ExactOut2Hops_nativeIn.snap index c0bfb8163..cb7a05225 100644 --- a/.forge-snapshots/V4Router_ExactOut2Hops_nativeIn.snap +++ b/.forge-snapshots/V4Router_ExactOut2Hops_nativeIn.snap @@ -1 +1 @@ -176345 \ No newline at end of file +182175 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactOut3Hops.snap b/.forge-snapshots/V4Router_ExactOut3Hops.snap index 5722b5d29..dbc8b9428 100644 --- a/.forge-snapshots/V4Router_ExactOut3Hops.snap +++ b/.forge-snapshots/V4Router_ExactOut3Hops.snap @@ -1 +1 @@ -230211 \ No newline at end of file +237465 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactOut3Hops_nativeIn.snap b/.forge-snapshots/V4Router_ExactOut3Hops_nativeIn.snap index e451e009e..a0c789c34 100644 --- a/.forge-snapshots/V4Router_ExactOut3Hops_nativeIn.snap +++ b/.forge-snapshots/V4Router_ExactOut3Hops_nativeIn.snap @@ -1 +1 @@ -226544 \ No newline at end of file +234339 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactOut3Hops_nativeOut.snap b/.forge-snapshots/V4Router_ExactOut3Hops_nativeOut.snap index c4eafcb0e..68126bb6a 100644 --- a/.forge-snapshots/V4Router_ExactOut3Hops_nativeOut.snap +++ b/.forge-snapshots/V4Router_ExactOut3Hops_nativeOut.snap @@ -1 +1 @@ -221420 \ No newline at end of file +228617 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactOutputSingle.snap b/.forge-snapshots/V4Router_ExactOutputSingle.snap index 3e18bc561..91008315b 100644 --- a/.forge-snapshots/V4Router_ExactOutputSingle.snap +++ b/.forge-snapshots/V4Router_ExactOutputSingle.snap @@ -1 +1 @@ -129130 \ No newline at end of file +131978 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactOutputSingle_nativeIn_sweepETH.snap b/.forge-snapshots/V4Router_ExactOutputSingle_nativeIn_sweepETH.snap index 03a0980e2..7a8a1df5a 100644 --- a/.forge-snapshots/V4Router_ExactOutputSingle_nativeIn_sweepETH.snap +++ b/.forge-snapshots/V4Router_ExactOutputSingle_nativeIn_sweepETH.snap @@ -1 +1 @@ -121219 \ No newline at end of file +124027 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactOutputSingle_nativeOut.snap b/.forge-snapshots/V4Router_ExactOutputSingle_nativeOut.snap index 7181adfd8..0db6b17af 100644 --- a/.forge-snapshots/V4Router_ExactOutputSingle_nativeOut.snap +++ b/.forge-snapshots/V4Router_ExactOutputSingle_nativeOut.snap @@ -1 +1 @@ -116170 \ No newline at end of file +118318 \ No newline at end of file diff --git a/src/PositionManager.sol b/src/PositionManager.sol index 94ff1d5a7..7c8e11ec0 100644 --- a/src/PositionManager.sol +++ b/src/PositionManager.sol @@ -28,7 +28,7 @@ import {INotifier} from "./interfaces/INotifier.sol"; import {Permit2Forwarder} from "./base/Permit2Forwarder.sol"; import {SlippageCheckLibrary} from "./libraries/SlippageCheck.sol"; import {PoolKeyChecker} from "./libraries/PoolKeyChecker.sol"; -import {PositionInfo, PositionInfoLibrary} from "./libraries/PositionInfoLibrary.sol"; +import {PackedPositionInfo, PositionInfoLibrary} from "./libraries/PositionInfoLibrary.sol"; // 444444444 // 444444444444 444444 @@ -117,18 +117,14 @@ contract PositionManager is using CalldataDecoder for bytes; using SlippageCheckLibrary for BalanceDelta; using PoolKeyChecker for PoolKey; + using PositionInfoLibrary for PackedPositionInfo; /// @dev The ID of the next token that will be minted. Skips 0 uint256 public nextTokenId = 1; - mapping(uint256 tokenId => PositionInfo info) public positionInfo; + mapping(uint256 tokenId => PackedPositionInfo info) public positionInfo; mapping(bytes25 poolId => PoolKey poolKey) public poolKeys; - /// @notice an internal getter for PositionInfo to be used by Notifier - function _positionInfo(uint256 tokenId) internal view override returns (PositionInfo storage) { - return positionInfo[tokenId]; - } - constructor(IPoolManager _poolManager, IAllowanceTransfer _permit2) BaseActionsRouter(_poolManager) Permit2Forwarder(_permit2) @@ -241,20 +237,13 @@ contract PositionManager is uint128 amount1Max, bytes calldata hookData ) internal onlyIfApproved(msgSender(), tokenId) { - (PositionInfo memory info, PoolKey memory poolKey) = getPoolPositionInfo(tokenId); + (PackedPositionInfo info, PoolKey memory poolKey) = getPoolPositionInfo(tokenId); - int256 liquidityChange = liquidity.toInt256(); // Note: The tokenId is used as the salt for this position, so every minted position has unique storage in the pool manager. - (BalanceDelta liquidityDelta, BalanceDelta feesAccrued) = poolManager.modifyLiquidity( - poolKey, _toParams(info.tickLower, info.tickUpper, liquidityChange, bytes32(tokenId)), hookData - ); - + (BalanceDelta liquidityDelta, BalanceDelta feesAccrued) = + _modifyLiquidity(info, poolKey, liquidity.toInt256(), bytes32(tokenId), hookData); // Slippage checks should be done on the principal liquidityDelta which is the liquidityDelta - feesAccrued (liquidityDelta - feesAccrued).validateMaxIn(amount0Max, amount1Max); - - if (info.hasSubscriber) { - _notifyModifyLiquidity(uint256(tokenId), liquidityChange, feesAccrued); - } } /// @dev Calling decrease with 0 liquidity will credit the caller with any underlying fees of the position @@ -265,21 +254,13 @@ contract PositionManager is uint128 amount1Min, bytes calldata hookData ) internal onlyIfApproved(msgSender(), tokenId) { - (PositionInfo memory info, PoolKey memory poolKey) = getPoolPositionInfo(tokenId); - - int256 liquidityChange = -(liquidity.toInt256()); + (PackedPositionInfo info, PoolKey memory poolKey) = getPoolPositionInfo(tokenId); // Note: the tokenId is used as the salt. - (BalanceDelta liquidityDelta, BalanceDelta feesAccrued) = poolManager.modifyLiquidity( - poolKey, _toParams(info.tickLower, info.tickUpper, liquidityChange, bytes32(tokenId)), hookData - ); - + (BalanceDelta liquidityDelta, BalanceDelta feesAccrued) = + _modifyLiquidity(info, poolKey, -(liquidity.toInt256()), bytes32(tokenId), hookData); // Slippage checks should be done on the principal liquidityDelta which is the liquidityDelta - feesAccrued (liquidityDelta - feesAccrued).validateMinOut(amount0Min, amount1Min); - - if (info.hasSubscriber) { - _notifyModifyLiquidity(uint256(tokenId), liquidityChange, feesAccrued); - } } function _mint( @@ -300,19 +281,18 @@ contract PositionManager is } _mint(owner, tokenId); - (BalanceDelta liquidityDelta, BalanceDelta feesAccrued) = poolManager.modifyLiquidity( - poolKey, _toParams(tickLower, tickUpper, liquidity.toInt256(), bytes32(tokenId)), hookData - ); + // Initialize the position info + PackedPositionInfo info = PositionInfoLibrary.initialize(poolKey, tickLower, tickUpper); + positionInfo[tokenId] = info; + (BalanceDelta liquidityDelta, BalanceDelta feesAccrued) = + _modifyLiquidity(info, poolKey, liquidity.toInt256(), bytes32(tokenId), hookData); // Slippage checks should be done on the principal liquidityDelta which is the liquidityDelta - feesAccrued (liquidityDelta - feesAccrued).validateMaxIn(amount0Max, amount1Max); - // Update position info and pool key storage. - PositionInfo memory info = PositionInfoLibrary.initialize(poolKey, tickLower, tickUpper); - positionInfo[tokenId] = info; - - if (poolKeys[info.poolId].isEmpty()) { - poolKeys[info.poolId] = poolKey; + // Store the poolKey if it is not already stored. + if (poolKeys[info.poolId()].isEmpty()) { + poolKeys[info.poolId()] = poolKey; } } @@ -321,29 +301,21 @@ contract PositionManager is internal onlyIfApproved(msgSender(), tokenId) { - (PositionInfo memory info, PoolKey memory poolKey) = getPoolPositionInfo(tokenId); + (PackedPositionInfo info, PoolKey memory poolKey) = getPoolPositionInfo(tokenId); - uint256 liquidity = uint256(_getLiquidity(tokenId, poolKey, info.tickLower, info.tickUpper)); + uint256 liquidity = uint256(_getLiquidity(tokenId, poolKey, info.tickLower(), info.tickUpper())); // Can only call modify if there is non zero liquidity. - BalanceDelta feesAccrued; if (liquidity > 0) { - BalanceDelta liquidityDelta; - (liquidityDelta, feesAccrued) = poolManager.modifyLiquidity( - poolKey, _toParams(info.tickLower, info.tickUpper, -(liquidity.toInt256()), bytes32(tokenId)), hookData - ); + (BalanceDelta liquidityDelta, BalanceDelta feesAccrued) = + _modifyLiquidity(info, poolKey, -(liquidity.toInt256()), bytes32(tokenId), hookData); // Slippage checks should be done on the principal liquidityDelta which is the liquidityDelta - feesAccrued (liquidityDelta - feesAccrued).validateMinOut(amount0Min, amount1Min); } - delete positionInfo[tokenId]; + positionInfo[tokenId] = PositionInfoLibrary.EMPTY_POSITION_INFO; // Burn the token. _burn(tokenId); - - // TODO: This will be changed from audit issue.. - if (info.hasSubscriber) { - _notifyModifyLiquidity(uint256(tokenId), -(liquidity.toInt256()), feesAccrued); - } } function _settlePair(Currency currency0, Currency currency1) internal { @@ -402,39 +374,60 @@ contract PositionManager is } } - function _toParams(int24 tickLower, int24 tickUpper, int256 liquidityChange, bytes32 salt) - private - pure - returns (IPoolManager.ModifyLiquidityParams memory) - { - return IPoolManager.ModifyLiquidityParams({ - tickLower: tickLower, - tickUpper: tickUpper, - liquidityDelta: liquidityChange, - salt: salt - }); + function _modifyLiquidity( + PackedPositionInfo info, + PoolKey memory poolKey, + int256 liquidityChange, + bytes32 salt, + bytes calldata hookData + ) internal returns (BalanceDelta liquidityDelta, BalanceDelta feesAccrued) { + (liquidityDelta, feesAccrued) = poolManager.modifyLiquidity( + poolKey, + IPoolManager.ModifyLiquidityParams({ + tickLower: info.tickLower(), + tickUpper: info.tickUpper(), + liquidityDelta: liquidityChange, + salt: salt + }), + hookData + ); + + // TODO: Audit issue for burn, decide if we want to keep this and also unsubscribe. + if (info.hasSubscriber()) { + _notifyModifyLiquidity(uint256(salt), liquidityChange, feesAccrued); + } + } + + /// @notice an internal helper used by Notifier + function _setSubscribe(uint256 tokenId) internal override { + positionInfo[tokenId] = positionInfo[tokenId].setSubscribe(); + } + + /// @notice an internal helper used by Notifier + function _setUnsubscribe(uint256 tokenId) internal override { + positionInfo[tokenId] = positionInfo[tokenId].setUnsubscribe(); } /// @dev overrides solmate transferFrom in case a notification to subscribers is needed function transferFrom(address from, address to, uint256 id) public virtual override { super.transferFrom(from, to, id); - if (positionInfo[id].hasSubscriber) _notifyTransfer(id, from, to); + if (positionInfo[id].hasSubscriber()) _notifyTransfer(id, from, to); } /// @inheritdoc IPositionManager function getPoolPositionInfo(uint256 tokenId) public view - returns (PositionInfo memory info, PoolKey memory poolKey) + returns (PackedPositionInfo info, PoolKey memory poolKey) { info = positionInfo[tokenId]; - poolKey = poolKeys[info.poolId]; + poolKey = poolKeys[info.poolId()]; } /// @inheritdoc IPositionManager function getPositionLiquidity(uint256 tokenId) public view returns (uint128 liquidity) { - (PositionInfo memory info, PoolKey memory poolKey) = getPoolPositionInfo(tokenId); - liquidity = _getLiquidity(tokenId, poolKey, info.tickLower, info.tickUpper); + (PackedPositionInfo info, PoolKey memory poolKey) = getPoolPositionInfo(tokenId); + liquidity = _getLiquidity(tokenId, poolKey, info.tickLower(), info.tickUpper()); } function _getLiquidity(uint256 tokenId, PoolKey memory poolKey, int24 tickLower, int24 tickUpper) diff --git a/src/base/Notifier.sol b/src/base/Notifier.sol index ac75aecb7..7b0d1b6b4 100644 --- a/src/base/Notifier.sol +++ b/src/base/Notifier.sol @@ -7,7 +7,7 @@ import {INotifier} from "../interfaces/INotifier.sol"; import {CustomRevert} from "@uniswap/v4-core/src/libraries/CustomRevert.sol"; import {BalanceDelta} from "@uniswap/v4-core/src/types/BalanceDelta.sol"; import {PositionManager} from "../PositionManager.sol"; -import {PositionInfo} from "../libraries/PositionInfoLibrary.sol"; +import {PackedPositionInfo} from "../libraries/PositionInfoLibrary.sol"; /// @notice Notifier is used to opt in to sending updates to external contracts about position modifications or transfers abstract contract Notifier is INotifier { @@ -29,8 +29,8 @@ abstract contract Notifier is INotifier { mapping(uint256 tokenId => ISubscriber subscriber) public subscriber; modifier onlyIfApproved(address caller, uint256 tokenId) virtual; - - function _positionInfo(uint256 tokenId) internal view virtual returns (PositionInfo storage); + function _setUnsubscribe(uint256 tokenId) internal virtual; + function _setSubscribe(uint256 tokenId) internal virtual; /// @inheritdoc INotifier function subscribe(uint256 tokenId, address newSubscriber, bytes calldata data) @@ -38,7 +38,7 @@ abstract contract Notifier is INotifier { payable onlyIfApproved(msg.sender, tokenId) { - _positionInfo(tokenId).hasSubscriber = true; + _setSubscribe(tokenId); ISubscriber _subscriber = subscriber[tokenId]; if (_subscriber != NO_SUBSCRIBER) revert AlreadySubscribed(address(_subscriber)); @@ -56,7 +56,7 @@ abstract contract Notifier is INotifier { /// @inheritdoc INotifier function unsubscribe(uint256 tokenId, bytes calldata data) external payable onlyIfApproved(msg.sender, tokenId) { - _positionInfo(tokenId).hasSubscriber = false; + _setUnsubscribe(tokenId); ISubscriber _subscriber = subscriber[tokenId]; delete subscriber[tokenId]; @@ -98,9 +98,4 @@ abstract contract Notifier is INotifier { success := call(gas(), target, 0, add(encodedCall, 0x20), mload(encodedCall), 0, 0) } } - - /// @inheritdoc INotifier - function hasSubscriber(uint256 tokenId) external view returns (bool) { - return _positionInfo(tokenId).hasSubscriber; - } } diff --git a/src/interfaces/INotifier.sol b/src/interfaces/INotifier.sol index e817c1bf0..bf0fced0a 100644 --- a/src/interfaces/INotifier.sol +++ b/src/interfaces/INotifier.sol @@ -26,9 +26,4 @@ interface INotifier { /// @dev payable so it can be multicalled with NATIVE related actions /// @dev Must always allow a user to unsubscribe. In the case of a malicious subscriber, a user can always unsubscribe safely, ensuring liquidity is always modifiable. function unsubscribe(uint256 tokenId, bytes calldata data) external payable; - - /// @notice Returns whether a a position should call out to notify a subscribing contract on modification or transfer - /// @param tokenId the ERC721 tokenId - /// @return bool whether or not the position has a subscriber - function hasSubscriber(uint256 tokenId) external view returns (bool); } diff --git a/src/interfaces/IPositionManager.sol b/src/interfaces/IPositionManager.sol index eefdcbcdd..abd0195ec 100644 --- a/src/interfaces/IPositionManager.sol +++ b/src/interfaces/IPositionManager.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.24; import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol"; import {Currency} from "@uniswap/v4-core/src/types/Currency.sol"; -import {PositionInfo} from "../libraries/PositionInfoLibrary.sol"; +import {PackedPositionInfo} from "../libraries/PositionInfoLibrary.sol"; import {INotifier} from "./INotifier.sol"; @@ -33,7 +33,7 @@ interface IPositionManager is INotifier { function getPositionLiquidity(uint256 tokenId) external view returns (uint128 liquidity); /// @param tokenId the ERC721 tokenId - /// @return PositionInfo information about the position including the range (tickLower, tickUpper) + /// @return PackedPositionInfo a uint256 packed value holding information about the position including the range (tickLower, tickUpper) /// @return poolKey the pool key of the position - function getPoolPositionInfo(uint256 tokenId) external view returns (PositionInfo memory, PoolKey memory); + function getPoolPositionInfo(uint256 tokenId) external view returns (PackedPositionInfo, PoolKey memory); } diff --git a/src/libraries/PositionInfoLibrary.sol b/src/libraries/PositionInfoLibrary.sol index 2f148f731..23df5c87e 100644 --- a/src/libraries/PositionInfoLibrary.sol +++ b/src/libraries/PositionInfoLibrary.sol @@ -4,33 +4,100 @@ pragma solidity ^0.8.24; import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol"; import {PoolId, PoolIdLibrary} from "@uniswap/v4-core/src/types/PoolId.sol"; -// TODO: Move to custom type, packed memory. -struct PositionInfo { - // lower 25 bytes of the poolId - bytes25 poolId; - int24 tickLower; - int24 tickUpper; - bool hasSubscriber; -} +/** + * @dev PackedPositionInfo is a packed version of solidity structure. + * Using the packaged version saves gas and memory by not storing the structure fields in memory slots. + * + * Layout: + * 200 bits poolId | 24 bits tickUpper | 24 bits tickLower | 8 bits hasSubscriber + * + * Fields in the direction from the least significant bit: + * + * A flag to know if the tokenId is subscribed to an address + * uint8 hasSubscriber; + * + * The tickUpper of the position + * int24 tickUpper; + * + * The tickLower of the position + * int24 tickLower; + * + * The truncated poolId + * bytes25 poolId; + * + * Note: If more bits are needed, hasSubscriber can be a single bit. + * + */ +type PackedPositionInfo is uint256; library PositionInfoLibrary { using PoolIdLibrary for PoolKey; + + PackedPositionInfo internal constant EMPTY_POSITION_INFO = PackedPositionInfo.wrap(0); + + uint256 internal constant MASK_UPPER_200_BITS = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000000000; + uint256 internal constant MASK_8_BITS = 0xFF; + uint24 internal constant MASK_24_BITS = 0xFFFFFF; + uint256 internal constant SET_SUBSCRIBE = 0x01; + uint8 internal constant TICK_LOWER_OFFSET = 8; + uint8 internal constant TICK_UPPER_OFFSET = 32; + + function poolId(PackedPositionInfo info) internal pure returns (bytes25 _poolId) { + assembly ("memory-safe") { + _poolId := and(MASK_UPPER_200_BITS, info) + } + } + + function tickLower(PackedPositionInfo info) internal pure returns (int24 _tickLower) { + assembly ("memory-safe") { + _tickLower := signextend(2, shr(TICK_LOWER_OFFSET, info)) + } + } + + function tickUpper(PackedPositionInfo info) internal pure returns (int24 _tickUpper) { + assembly ("memory-safe") { + _tickUpper := signextend(2, shr(TICK_UPPER_OFFSET, info)) + } + } + + function hasSubscriber(PackedPositionInfo info) internal pure returns (bool _hasSubscriber) { + assembly ("memory-safe") { + _hasSubscriber := and(MASK_8_BITS, info) + } + } + + /// @dev this does not actually set any storage + function setSubscribe(PackedPositionInfo info) internal pure returns (PackedPositionInfo _info) { + assembly ("memory-safe") { + _info := or(info, SET_SUBSCRIBE) + } + } + + /// @dev this does not actually set any storage + function setUnsubscribe(PackedPositionInfo info) internal pure returns (PackedPositionInfo _info) { + assembly ("memory-safe") { + _info := and(info, not(SET_SUBSCRIBE)) + } + } + /// @notice Creates the default PositionInfo struct /// @dev Called when minting a new position - /// @param poolKey the pool key of the position - /// @param tickLower the lower tick of the position - /// @param tickUpper the upper tick of the position - /// @return the PositionInfo struct, with the truncated poolId and the hasSubscriber flag set to false - - function initialize(PoolKey memory poolKey, int24 tickLower, int24 tickUpper) + /// @param _poolKey the pool key of the position + /// @param _tickLower the lower tick of the position + /// @param _tickUpper the upper tick of the position + /// @return info packed position info, with the truncated poolId and the hasSubscriber flag set to false + function initialize(PoolKey memory _poolKey, int24 _tickLower, int24 _tickUpper) internal - returns (PositionInfo memory) + pure + returns (PackedPositionInfo info) { - return PositionInfo({ - poolId: bytes25(PoolId.unwrap(poolKey.toId())), - tickLower: tickLower, - tickUpper: tickUpper, - hasSubscriber: false - }); + bytes25 _poolId = bytes25(PoolId.unwrap(_poolKey.toId())); + assembly { + info := + or( + or(and(MASK_UPPER_200_BITS, _poolId), shl(TICK_UPPER_OFFSET, and(MASK_24_BITS, _tickUpper))), + shl(TICK_LOWER_OFFSET, and(MASK_24_BITS, _tickLower)) + ) + } } } diff --git a/test/libraries/CalldataDecoder.t.sol b/test/libraries/CalldataDecoder.t.sol index c0501b162..c8b303d16 100644 --- a/test/libraries/CalldataDecoder.t.sol +++ b/test/libraries/CalldataDecoder.t.sol @@ -51,44 +51,45 @@ contract CalldataDecoderTest is Test { assertEq(amount1Min, _amount1Min); } - function test_fuzz_decodeMintParams( - PositionConfig calldata _config, - uint256 _liquidity, - uint128 _amount0Max, - uint128 _amount1Max, - address _owner, - bytes calldata _hookData - ) public view { - bytes memory params = abi.encode( - _config.poolKey, - _config.tickLower, - _config.tickUpper, - _liquidity, - _amount0Max, - _amount1Max, - _owner, - _hookData - ); - ( - PoolKey memory poolKey, - int24 tickLower, - int24 tickUpper, - uint256 liquidity, - uint128 amount0Max, - uint128 amount1Max, - address owner, - bytes memory hookData - ) = decoder.decodeMintParams(params); - - assertEq(liquidity, _liquidity); - assertEq(amount0Max, _amount0Max); - assertEq(amount1Max, _amount1Max); - assertEq(owner, _owner); - assertEq(hookData, _hookData); - _assertEq(_config.poolKey, poolKey); - assertEq(_config.tickLower, tickLower); - assertEq(_config.tickUpper, tickUpper); - } + // TODO: fix stack too deep here + // function test_fuzz_decodeMintParams( + // PositionConfig calldata _config, + // uint256 _liquidity, + // uint128 _amount0Max, + // uint128 _amount1Max, + // address _owner, + // bytes calldata _hookData + // ) public view { + // bytes memory params = abi.encode( + // _config.poolKey, + // _config.tickLower, + // _config.tickUpper, + // _liquidity, + // _amount0Max, + // _amount1Max, + // _owner, + // _hookData + // ); + // ( + // PoolKey memory poolKey, + // int24 tickLower, + // int24 tickUpper, + // uint256 liquidity, + // uint128 amount0Max, + // uint128 amount1Max, + // address owner, + // bytes memory hookData + // ) = decoder.decodeMintParams(params); + + // assertEq(liquidity, _liquidity); + // assertEq(amount0Max, _amount0Max); + // assertEq(amount1Max, _amount1Max); + // assertEq(owner, _owner); + // assertEq(hookData, _hookData); + // _assertEq(_config.poolKey, poolKey); + // assertEq(_config.tickLower, tickLower); + // assertEq(_config.tickUpper, tickUpper); + // } function test_fuzz_decodeSwapExactInParams(IV4Router.ExactInputParams calldata _swapParams) public view { bytes memory params = abi.encode(_swapParams); @@ -219,4 +220,13 @@ contract CalldataDecoderTest is Test { assertEq(key1.tickSpacing, key2.tickSpacing); assertEq(address(key1.hooks), address(key2.hooks)); } + + function _assertEq(PositionConfig memory config, PoolKey memory poolKey, int24 tickLower, int24 tickUpper) + internal + pure + { + _assertEq(config.poolKey, poolKey); + assertEq(config.tickLower, tickLower); + assertEq(config.tickUpper, tickUpper); + } } diff --git a/test/libraries/PositionInfoLibrary.t.sol b/test/libraries/PositionInfoLibrary.t.sol new file mode 100644 index 000000000..d87170ea6 --- /dev/null +++ b/test/libraries/PositionInfoLibrary.t.sol @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.24; + +import "forge-std/Test.sol"; +import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol"; +import {PoolIdLibrary} from "@uniswap/v4-core/src/types/PoolId.sol"; +import {PackedPositionInfo, PositionInfoLibrary, PoolId} from "../../src/libraries/PositionInfoLibrary.sol"; + +import "forge-std/console2.sol"; + +contract PositionInfoLibraryTest is Test { + using PositionInfoLibrary for PackedPositionInfo; + using PoolIdLibrary for PoolKey; + + function setUp() public {} + + function test_fuzz_initialize(PoolKey memory poolKey, int24 tickLower, int24 tickUpper) public pure { + PackedPositionInfo info = PositionInfoLibrary.initialize(poolKey, tickLower, tickUpper); + + assertEq(info.poolId(), bytes25(PoolId.unwrap(poolKey.toId()))); + assertEq(info.tickLower(), tickLower); + assertEq(info.tickUpper(), tickUpper); + assertEq(info.hasSubscriber(), false); + } + + function test_fuzz_initialize_setSubscribe(PoolKey memory poolKey, int24 tickLower, int24 tickUpper) public pure { + PackedPositionInfo info = PositionInfoLibrary.initialize(poolKey, tickLower, tickUpper); + assertEq(info.hasSubscriber(), false); + info = info.setSubscribe(); + assertEq(info.hasSubscriber(), true); + assertEq(info.tickLower(), tickLower); + assertEq(info.tickUpper(), tickUpper); + assertEq(info.poolId(), bytes25(PoolId.unwrap(poolKey.toId()))); + } + + function test_fuzz_initialize_setUnsubscribe(PoolKey memory poolKey, int24 tickLower, int24 tickUpper) + public + pure + { + PackedPositionInfo info = PositionInfoLibrary.initialize(poolKey, tickLower, tickUpper); + assertEq(info.hasSubscriber(), false); + info = info.setSubscribe(); + assertEq(info.hasSubscriber(), true); + assertEq(info.tickLower(), tickLower); + assertEq(info.tickUpper(), tickUpper); + assertEq(info.poolId(), bytes25(PoolId.unwrap(poolKey.toId()))); + + info = info.setUnsubscribe(); + assertEq(info.hasSubscriber(), false); + assertEq(info.tickLower(), tickLower); + assertEq(info.tickUpper(), tickUpper); + assertEq(info.poolId(), bytes25(PoolId.unwrap(poolKey.toId()))); + } + + function test_fuzz_setSubscribe(PoolKey memory poolKey, int24 tickLower, int24 tickUpper) public pure { + PackedPositionInfo info = PositionInfoLibrary.initialize(poolKey, tickLower, tickUpper); + assertEq(info.hasSubscriber(), false); + info = info.setSubscribe(); + assertEq(info.hasSubscriber(), true); + + // Calling set subscribe again does nothing. + info = info.setSubscribe(); + assertEq(info.hasSubscriber(), true); + } + + function test_fuzz_setUnsubscribe(PoolKey memory poolKey, int24 tickLower, int24 tickUpper) public pure { + PackedPositionInfo info = PositionInfoLibrary.initialize(poolKey, tickLower, tickUpper); + assertEq(info.hasSubscriber(), false); + info = info.setSubscribe(); + assertEq(info.hasSubscriber(), true); + info = info.setUnsubscribe(); + assertEq(info.hasSubscriber(), false); + + // Calling set unsubscribe again does nothing. + info = info.setUnsubscribe(); + assertEq(info.hasSubscriber(), false); + } +} diff --git a/test/position-managers/PositionManager.notifier.t.sol b/test/position-managers/PositionManager.notifier.t.sol index cf5591bc1..3746bc95e 100644 --- a/test/position-managers/PositionManager.notifier.t.sol +++ b/test/position-managers/PositionManager.notifier.t.sol @@ -8,6 +8,7 @@ import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol"; import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol"; import {StateLibrary} from "@uniswap/v4-core/src/libraries/StateLibrary.sol"; import {PoolIdLibrary} from "@uniswap/v4-core/src/types/PoolId.sol"; +import {BalanceDelta, toBalanceDelta} from "@uniswap/v4-core/src/types/BalanceDelta.sol"; import {GasSnapshot} from "forge-gas-snapshot/GasSnapshot.sol"; import {PosmTestSetup} from "../shared/PosmTestSetup.sol"; import {MockSubscriber} from "../mocks/MockSubscriber.sol"; @@ -18,12 +19,13 @@ import {Plan, Planner} from "../shared/Planner.sol"; import {Actions} from "../../src/libraries/Actions.sol"; import {INotifier} from "../../src/interfaces/INotifier.sol"; import {MockReturnDataSubscriber, MockRevertSubscriber} from "../mocks/MockBadSubscribers.sol"; -import {BalanceDelta, toBalanceDelta} from "@uniswap/v4-core/src/types/BalanceDelta.sol"; +import {PositionInfoLibrary, PackedPositionInfo} from "../../src/libraries/PositionInfoLibrary.sol"; contract PositionManagerNotifierTest is Test, PosmTestSetup, GasSnapshot { using PoolIdLibrary for PoolKey; using StateLibrary for IPoolManager; using Planner for Plan; + using PositionInfoLibrary for PackedPositionInfo; MockSubscriber sub; MockReturnDataSubscriber badSubscriber; @@ -77,7 +79,7 @@ contract PositionManagerNotifierTest is Test, PosmTestSetup, GasSnapshot { lpm.subscribe(tokenId, address(sub), ZERO_BYTES); - assertEq(lpm.hasSubscriber(tokenId), true); + assertEq(lpm.positionInfo(tokenId).hasSubscriber(), true); assertEq(address(lpm.subscriber(tokenId)), address(sub)); assertEq(sub.notifySubscribeCount(), 1); } @@ -93,7 +95,7 @@ contract PositionManagerNotifierTest is Test, PosmTestSetup, GasSnapshot { lpm.subscribe(tokenId, address(sub), ZERO_BYTES); - assertEq(lpm.hasSubscriber(tokenId), true); + assertEq(lpm.positionInfo(tokenId).hasSubscriber(), true); assertEq(address(lpm.subscriber(tokenId)), address(sub)); Plan memory plan = Planner.init(); @@ -127,7 +129,7 @@ contract PositionManagerNotifierTest is Test, PosmTestSetup, GasSnapshot { lpm.subscribe(tokenId, address(sub), ZERO_BYTES); - assertEq(lpm.hasSubscriber(tokenId), true); + assertEq(lpm.positionInfo(tokenId).hasSubscriber(), true); assertEq(address(lpm.subscriber(tokenId)), address(sub)); uint256 liquidityToAdd = 10e18; @@ -150,7 +152,7 @@ contract PositionManagerNotifierTest is Test, PosmTestSetup, GasSnapshot { lpm.subscribe(tokenId, address(sub), ZERO_BYTES); - assertEq(lpm.hasSubscriber(tokenId), true); + assertEq(lpm.positionInfo(tokenId).hasSubscriber(), true); assertEq(address(lpm.subscriber(tokenId)), address(sub)); lpm.transferFrom(alice, bob, tokenId); @@ -169,7 +171,7 @@ contract PositionManagerNotifierTest is Test, PosmTestSetup, GasSnapshot { lpm.subscribe(tokenId, address(sub), ZERO_BYTES); - assertEq(lpm.hasSubscriber(tokenId), true); + assertEq(lpm.positionInfo(tokenId).hasSubscriber(), true); assertEq(address(lpm.subscriber(tokenId)), address(sub)); lpm.safeTransferFrom(alice, bob, tokenId); @@ -188,7 +190,7 @@ contract PositionManagerNotifierTest is Test, PosmTestSetup, GasSnapshot { lpm.subscribe(tokenId, address(sub), ZERO_BYTES); - assertEq(lpm.hasSubscriber(tokenId), true); + assertEq(lpm.positionInfo(tokenId).hasSubscriber(), true); assertEq(address(lpm.subscriber(tokenId)), address(sub)); lpm.safeTransferFrom(alice, bob, tokenId, ""); @@ -210,7 +212,7 @@ contract PositionManagerNotifierTest is Test, PosmTestSetup, GasSnapshot { lpm.unsubscribe(tokenId, ZERO_BYTES); assertEq(sub.notifyUnsubscribeCount(), 1); - assertEq(lpm.hasSubscriber(tokenId), false); + assertEq(lpm.positionInfo(tokenId).hasSubscriber(), false); assertEq(address(lpm.subscriber(tokenId)), address(0)); } @@ -230,7 +232,7 @@ contract PositionManagerNotifierTest is Test, PosmTestSetup, GasSnapshot { // the subscriber contract call failed bc it used too much gas assertEq(MockReturnDataSubscriber(badSubscriber).notifyUnsubscribeCount(), 0); - assertEq(lpm.hasSubscriber(tokenId), false); + assertEq(lpm.positionInfo(tokenId).hasSubscriber(), false); assertEq(address(lpm.subscriber(tokenId)), address(0)); } @@ -265,7 +267,7 @@ contract PositionManagerNotifierTest is Test, PosmTestSetup, GasSnapshot { assertEq(liquidity, 100e18); assertEq(sub.notifySubscribeCount(), 1); - assertEq(lpm.hasSubscriber(tokenId), true); + assertEq(lpm.positionInfo(tokenId).hasSubscriber(), true); assertEq(address(lpm.subscriber(tokenId)), address(sub)); } @@ -310,7 +312,7 @@ contract PositionManagerNotifierTest is Test, PosmTestSetup, GasSnapshot { assertEq(liquidity, 110e18); assertEq(sub.notifySubscribeCount(), 1); assertEq(sub.notifyModifyLiquidityCount(), 1); - assertEq(lpm.hasSubscriber(tokenId), true); + assertEq(lpm.positionInfo(tokenId).hasSubscriber(), true); assertEq(address(lpm.subscriber(tokenId)), address(sub)); } @@ -340,7 +342,7 @@ contract PositionManagerNotifierTest is Test, PosmTestSetup, GasSnapshot { lpm.subscribe(tokenId, address(sub), subData); - assertEq(lpm.hasSubscriber(tokenId), true); + assertEq(lpm.positionInfo(tokenId).hasSubscriber(), true); assertEq(address(lpm.subscriber(tokenId)), address(sub)); assertEq(sub.notifySubscribeCount(), 1); assertEq(abi.decode(sub.subscribeData(), (address)), address(this)); @@ -362,7 +364,7 @@ contract PositionManagerNotifierTest is Test, PosmTestSetup, GasSnapshot { lpm.unsubscribe(tokenId, subData); assertEq(sub.notifyUnsubscribeCount(), 1); - assertEq(lpm.hasSubscriber(tokenId), false); + assertEq(lpm.positionInfo(tokenId).hasSubscriber(), false); assertEq(address(lpm.subscriber(tokenId)), address(0)); assertEq(abi.decode(sub.unsubscribeData(), (address)), address(this)); } diff --git a/test/shared/fuzz/LiquidityFuzzers.sol b/test/shared/fuzz/LiquidityFuzzers.sol index 1182606ae..ca14dbdf6 100644 --- a/test/shared/fuzz/LiquidityFuzzers.sol +++ b/test/shared/fuzz/LiquidityFuzzers.sol @@ -15,6 +15,8 @@ import {Planner, Plan} from "../../shared/Planner.sol"; contract LiquidityFuzzers is Fuzzers { using Planner for Plan; + uint128 constant _MAX_SLIPPAGE_INCREASE = type(uint128).max; + function addFuzzyLiquidity( IPositionManager lpm, address recipient, @@ -24,26 +26,23 @@ contract LiquidityFuzzers is Fuzzers { bytes memory hookData ) internal returns (uint256, IPoolManager.ModifyLiquidityParams memory) { params = Fuzzers.createFuzzyLiquidityParams(key, params, sqrtPriceX96); - PositionConfig memory config = - PositionConfig({poolKey: key, tickLower: params.tickLower, tickUpper: params.tickUpper}); - uint128 MAX_SLIPPAGE_INCREASE = type(uint128).max; Plan memory planner = Planner.init().add( Actions.MINT_POSITION, abi.encode( - config.poolKey, - config.tickLower, - config.tickUpper, + key, + params.tickLower, + params.tickUpper, uint256(params.liquidityDelta), - MAX_SLIPPAGE_INCREASE, - MAX_SLIPPAGE_INCREASE, + _MAX_SLIPPAGE_INCREASE, + _MAX_SLIPPAGE_INCREASE, recipient, hookData ) ); uint256 tokenId = lpm.nextTokenId(); - bytes memory calls = planner.finalizeModifyLiquidityWithClose(config.poolKey); + bytes memory calls = planner.finalizeModifyLiquidityWithClose(key); lpm.modifyLiquidities(calls, block.timestamp + 1); return (tokenId, params); From 7e8fc42e5f022bbdf5325678d0909205157ff2ed Mon Sep 17 00:00:00 2001 From: Sara Reynolds Date: Thu, 29 Aug 2024 00:48:22 -0400 Subject: [PATCH 06/45] use external --- src/PositionManager.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PositionManager.sol b/src/PositionManager.sol index 7c8e11ec0..8405ca7eb 100644 --- a/src/PositionManager.sol +++ b/src/PositionManager.sol @@ -425,7 +425,7 @@ contract PositionManager is } /// @inheritdoc IPositionManager - function getPositionLiquidity(uint256 tokenId) public view returns (uint128 liquidity) { + function getPositionLiquidity(uint256 tokenId) external view returns (uint128 liquidity) { (PackedPositionInfo info, PoolKey memory poolKey) = getPoolPositionInfo(tokenId); liquidity = _getLiquidity(tokenId, poolKey, info.tickLower(), info.tickUpper()); } From f94823f6eb663b9d10b31a46fa1a671e3627c721 Mon Sep 17 00:00:00 2001 From: Sara Reynolds Date: Thu, 29 Aug 2024 12:33:38 -0400 Subject: [PATCH 07/45] review comments --- src/PositionManager.sol | 26 ++++++++----------- src/base/Notifier.sol | 5 ++-- src/interfaces/IPositionManager.sol | 6 ++--- src/libraries/PositionInfoLibrary.sol | 22 ++++++++-------- test/libraries/PositionInfoLibrary.t.sol | 14 +++++----- .../PositionManager.notifier.t.sol | 4 +-- 6 files changed, 37 insertions(+), 40 deletions(-) diff --git a/src/PositionManager.sol b/src/PositionManager.sol index 8405ca7eb..580a7b9f6 100644 --- a/src/PositionManager.sol +++ b/src/PositionManager.sol @@ -28,7 +28,7 @@ import {INotifier} from "./interfaces/INotifier.sol"; import {Permit2Forwarder} from "./base/Permit2Forwarder.sol"; import {SlippageCheckLibrary} from "./libraries/SlippageCheck.sol"; import {PoolKeyChecker} from "./libraries/PoolKeyChecker.sol"; -import {PackedPositionInfo, PositionInfoLibrary} from "./libraries/PositionInfoLibrary.sol"; +import {PositionInfo, PositionInfoLibrary} from "./libraries/PositionInfoLibrary.sol"; // 444444444 // 444444444444 444444 @@ -117,12 +117,12 @@ contract PositionManager is using CalldataDecoder for bytes; using SlippageCheckLibrary for BalanceDelta; using PoolKeyChecker for PoolKey; - using PositionInfoLibrary for PackedPositionInfo; + using PositionInfoLibrary for PositionInfo; /// @dev The ID of the next token that will be minted. Skips 0 uint256 public nextTokenId = 1; - mapping(uint256 tokenId => PackedPositionInfo info) public positionInfo; + mapping(uint256 tokenId => PositionInfo info) public positionInfo; mapping(bytes25 poolId => PoolKey poolKey) public poolKeys; constructor(IPoolManager _poolManager, IAllowanceTransfer _permit2) @@ -237,7 +237,7 @@ contract PositionManager is uint128 amount1Max, bytes calldata hookData ) internal onlyIfApproved(msgSender(), tokenId) { - (PackedPositionInfo info, PoolKey memory poolKey) = getPoolPositionInfo(tokenId); + (PositionInfo info, PoolKey memory poolKey) = getPoolPositionInfo(tokenId); // Note: The tokenId is used as the salt for this position, so every minted position has unique storage in the pool manager. (BalanceDelta liquidityDelta, BalanceDelta feesAccrued) = @@ -254,7 +254,7 @@ contract PositionManager is uint128 amount1Min, bytes calldata hookData ) internal onlyIfApproved(msgSender(), tokenId) { - (PackedPositionInfo info, PoolKey memory poolKey) = getPoolPositionInfo(tokenId); + (PositionInfo info, PoolKey memory poolKey) = getPoolPositionInfo(tokenId); // Note: the tokenId is used as the salt. (BalanceDelta liquidityDelta, BalanceDelta feesAccrued) = @@ -282,7 +282,7 @@ contract PositionManager is _mint(owner, tokenId); // Initialize the position info - PackedPositionInfo info = PositionInfoLibrary.initialize(poolKey, tickLower, tickUpper); + PositionInfo info = PositionInfoLibrary.initialize(poolKey, tickLower, tickUpper); positionInfo[tokenId] = info; (BalanceDelta liquidityDelta, BalanceDelta feesAccrued) = @@ -301,7 +301,7 @@ contract PositionManager is internal onlyIfApproved(msgSender(), tokenId) { - (PackedPositionInfo info, PoolKey memory poolKey) = getPoolPositionInfo(tokenId); + (PositionInfo info, PoolKey memory poolKey) = getPoolPositionInfo(tokenId); uint256 liquidity = uint256(_getLiquidity(tokenId, poolKey, info.tickLower(), info.tickUpper())); @@ -375,7 +375,7 @@ contract PositionManager is } function _modifyLiquidity( - PackedPositionInfo info, + PositionInfo info, PoolKey memory poolKey, int256 liquidityChange, bytes32 salt, @@ -402,8 +402,8 @@ contract PositionManager is function _setSubscribe(uint256 tokenId) internal override { positionInfo[tokenId] = positionInfo[tokenId].setSubscribe(); } - /// @notice an internal helper used by Notifier + function _setUnsubscribe(uint256 tokenId) internal override { positionInfo[tokenId] = positionInfo[tokenId].setUnsubscribe(); } @@ -415,18 +415,14 @@ contract PositionManager is } /// @inheritdoc IPositionManager - function getPoolPositionInfo(uint256 tokenId) - public - view - returns (PackedPositionInfo info, PoolKey memory poolKey) - { + function getPoolPositionInfo(uint256 tokenId) public view returns (PositionInfo info, PoolKey memory poolKey) { info = positionInfo[tokenId]; poolKey = poolKeys[info.poolId()]; } /// @inheritdoc IPositionManager function getPositionLiquidity(uint256 tokenId) external view returns (uint128 liquidity) { - (PackedPositionInfo info, PoolKey memory poolKey) = getPoolPositionInfo(tokenId); + (PositionInfo info, PoolKey memory poolKey) = getPoolPositionInfo(tokenId); liquidity = _getLiquidity(tokenId, poolKey, info.tickLower(), info.tickUpper()); } diff --git a/src/base/Notifier.sol b/src/base/Notifier.sol index 7b0d1b6b4..d27f40070 100644 --- a/src/base/Notifier.sol +++ b/src/base/Notifier.sol @@ -6,8 +6,7 @@ import {BipsLibrary} from "../libraries/BipsLibrary.sol"; import {INotifier} from "../interfaces/INotifier.sol"; import {CustomRevert} from "@uniswap/v4-core/src/libraries/CustomRevert.sol"; import {BalanceDelta} from "@uniswap/v4-core/src/types/BalanceDelta.sol"; -import {PositionManager} from "../PositionManager.sol"; -import {PackedPositionInfo} from "../libraries/PositionInfoLibrary.sol"; +import {PositionInfo} from "../libraries/PositionInfoLibrary.sol"; /// @notice Notifier is used to opt in to sending updates to external contracts about position modifications or transfers abstract contract Notifier is INotifier { @@ -29,7 +28,9 @@ abstract contract Notifier is INotifier { mapping(uint256 tokenId => ISubscriber subscriber) public subscriber; modifier onlyIfApproved(address caller, uint256 tokenId) virtual; + function _setUnsubscribe(uint256 tokenId) internal virtual; + function _setSubscribe(uint256 tokenId) internal virtual; /// @inheritdoc INotifier diff --git a/src/interfaces/IPositionManager.sol b/src/interfaces/IPositionManager.sol index abd0195ec..02ce17e53 100644 --- a/src/interfaces/IPositionManager.sol +++ b/src/interfaces/IPositionManager.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.24; import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol"; import {Currency} from "@uniswap/v4-core/src/types/Currency.sol"; -import {PackedPositionInfo} from "../libraries/PositionInfoLibrary.sol"; +import {PositionInfo} from "../libraries/PositionInfoLibrary.sol"; import {INotifier} from "./INotifier.sol"; @@ -33,7 +33,7 @@ interface IPositionManager is INotifier { function getPositionLiquidity(uint256 tokenId) external view returns (uint128 liquidity); /// @param tokenId the ERC721 tokenId - /// @return PackedPositionInfo a uint256 packed value holding information about the position including the range (tickLower, tickUpper) + /// @return a uint256 packed value holding information about the position including the range (tickLower, tickUpper) /// @return poolKey the pool key of the position - function getPoolPositionInfo(uint256 tokenId) external view returns (PackedPositionInfo, PoolKey memory); + function getPoolPositionInfo(uint256 tokenId) external view returns (PositionInfo, PoolKey memory); } diff --git a/src/libraries/PositionInfoLibrary.sol b/src/libraries/PositionInfoLibrary.sol index 23df5c87e..dee592d56 100644 --- a/src/libraries/PositionInfoLibrary.sol +++ b/src/libraries/PositionInfoLibrary.sol @@ -5,7 +5,7 @@ import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol"; import {PoolId, PoolIdLibrary} from "@uniswap/v4-core/src/types/PoolId.sol"; /** - * @dev PackedPositionInfo is a packed version of solidity structure. + * @dev PositionInfo is a packed version of solidity structure. * Using the packaged version saves gas and memory by not storing the structure fields in memory slots. * * Layout: @@ -22,18 +22,18 @@ import {PoolId, PoolIdLibrary} from "@uniswap/v4-core/src/types/PoolId.sol"; * The tickLower of the position * int24 tickLower; * - * The truncated poolId + * The truncated poolId. Truncates a bytes32 value so the most signifcant (highest) 200 bits are used. * bytes25 poolId; * * Note: If more bits are needed, hasSubscriber can be a single bit. * */ -type PackedPositionInfo is uint256; +type PositionInfo is uint256; library PositionInfoLibrary { using PoolIdLibrary for PoolKey; - PackedPositionInfo internal constant EMPTY_POSITION_INFO = PackedPositionInfo.wrap(0); + PositionInfo internal constant EMPTY_POSITION_INFO = PositionInfo.wrap(0); uint256 internal constant MASK_UPPER_200_BITS = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000000000; uint256 internal constant MASK_8_BITS = 0xFF; @@ -42,39 +42,39 @@ library PositionInfoLibrary { uint8 internal constant TICK_LOWER_OFFSET = 8; uint8 internal constant TICK_UPPER_OFFSET = 32; - function poolId(PackedPositionInfo info) internal pure returns (bytes25 _poolId) { + function poolId(PositionInfo info) internal pure returns (bytes25 _poolId) { assembly ("memory-safe") { _poolId := and(MASK_UPPER_200_BITS, info) } } - function tickLower(PackedPositionInfo info) internal pure returns (int24 _tickLower) { + function tickLower(PositionInfo info) internal pure returns (int24 _tickLower) { assembly ("memory-safe") { _tickLower := signextend(2, shr(TICK_LOWER_OFFSET, info)) } } - function tickUpper(PackedPositionInfo info) internal pure returns (int24 _tickUpper) { + function tickUpper(PositionInfo info) internal pure returns (int24 _tickUpper) { assembly ("memory-safe") { _tickUpper := signextend(2, shr(TICK_UPPER_OFFSET, info)) } } - function hasSubscriber(PackedPositionInfo info) internal pure returns (bool _hasSubscriber) { + function hasSubscriber(PositionInfo info) internal pure returns (bool _hasSubscriber) { assembly ("memory-safe") { _hasSubscriber := and(MASK_8_BITS, info) } } /// @dev this does not actually set any storage - function setSubscribe(PackedPositionInfo info) internal pure returns (PackedPositionInfo _info) { + function setSubscribe(PositionInfo info) internal pure returns (PositionInfo _info) { assembly ("memory-safe") { _info := or(info, SET_SUBSCRIBE) } } /// @dev this does not actually set any storage - function setUnsubscribe(PackedPositionInfo info) internal pure returns (PackedPositionInfo _info) { + function setUnsubscribe(PositionInfo info) internal pure returns (PositionInfo _info) { assembly ("memory-safe") { _info := and(info, not(SET_SUBSCRIBE)) } @@ -89,7 +89,7 @@ library PositionInfoLibrary { function initialize(PoolKey memory _poolKey, int24 _tickLower, int24 _tickUpper) internal pure - returns (PackedPositionInfo info) + returns (PositionInfo info) { bytes25 _poolId = bytes25(PoolId.unwrap(_poolKey.toId())); assembly { diff --git a/test/libraries/PositionInfoLibrary.t.sol b/test/libraries/PositionInfoLibrary.t.sol index d87170ea6..3ce25f095 100644 --- a/test/libraries/PositionInfoLibrary.t.sol +++ b/test/libraries/PositionInfoLibrary.t.sol @@ -4,18 +4,18 @@ pragma solidity ^0.8.24; import "forge-std/Test.sol"; import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol"; import {PoolIdLibrary} from "@uniswap/v4-core/src/types/PoolId.sol"; -import {PackedPositionInfo, PositionInfoLibrary, PoolId} from "../../src/libraries/PositionInfoLibrary.sol"; +import {PositionInfo, PositionInfoLibrary, PoolId} from "../../src/libraries/PositionInfoLibrary.sol"; import "forge-std/console2.sol"; contract PositionInfoLibraryTest is Test { - using PositionInfoLibrary for PackedPositionInfo; + using PositionInfoLibrary for PositionInfo; using PoolIdLibrary for PoolKey; function setUp() public {} function test_fuzz_initialize(PoolKey memory poolKey, int24 tickLower, int24 tickUpper) public pure { - PackedPositionInfo info = PositionInfoLibrary.initialize(poolKey, tickLower, tickUpper); + PositionInfo info = PositionInfoLibrary.initialize(poolKey, tickLower, tickUpper); assertEq(info.poolId(), bytes25(PoolId.unwrap(poolKey.toId()))); assertEq(info.tickLower(), tickLower); @@ -24,7 +24,7 @@ contract PositionInfoLibraryTest is Test { } function test_fuzz_initialize_setSubscribe(PoolKey memory poolKey, int24 tickLower, int24 tickUpper) public pure { - PackedPositionInfo info = PositionInfoLibrary.initialize(poolKey, tickLower, tickUpper); + PositionInfo info = PositionInfoLibrary.initialize(poolKey, tickLower, tickUpper); assertEq(info.hasSubscriber(), false); info = info.setSubscribe(); assertEq(info.hasSubscriber(), true); @@ -37,7 +37,7 @@ contract PositionInfoLibraryTest is Test { public pure { - PackedPositionInfo info = PositionInfoLibrary.initialize(poolKey, tickLower, tickUpper); + PositionInfo info = PositionInfoLibrary.initialize(poolKey, tickLower, tickUpper); assertEq(info.hasSubscriber(), false); info = info.setSubscribe(); assertEq(info.hasSubscriber(), true); @@ -53,7 +53,7 @@ contract PositionInfoLibraryTest is Test { } function test_fuzz_setSubscribe(PoolKey memory poolKey, int24 tickLower, int24 tickUpper) public pure { - PackedPositionInfo info = PositionInfoLibrary.initialize(poolKey, tickLower, tickUpper); + PositionInfo info = PositionInfoLibrary.initialize(poolKey, tickLower, tickUpper); assertEq(info.hasSubscriber(), false); info = info.setSubscribe(); assertEq(info.hasSubscriber(), true); @@ -64,7 +64,7 @@ contract PositionInfoLibraryTest is Test { } function test_fuzz_setUnsubscribe(PoolKey memory poolKey, int24 tickLower, int24 tickUpper) public pure { - PackedPositionInfo info = PositionInfoLibrary.initialize(poolKey, tickLower, tickUpper); + PositionInfo info = PositionInfoLibrary.initialize(poolKey, tickLower, tickUpper); assertEq(info.hasSubscriber(), false); info = info.setSubscribe(); assertEq(info.hasSubscriber(), true); diff --git a/test/position-managers/PositionManager.notifier.t.sol b/test/position-managers/PositionManager.notifier.t.sol index 3746bc95e..4f296fd4a 100644 --- a/test/position-managers/PositionManager.notifier.t.sol +++ b/test/position-managers/PositionManager.notifier.t.sol @@ -19,13 +19,13 @@ import {Plan, Planner} from "../shared/Planner.sol"; import {Actions} from "../../src/libraries/Actions.sol"; import {INotifier} from "../../src/interfaces/INotifier.sol"; import {MockReturnDataSubscriber, MockRevertSubscriber} from "../mocks/MockBadSubscribers.sol"; -import {PositionInfoLibrary, PackedPositionInfo} from "../../src/libraries/PositionInfoLibrary.sol"; +import {PositionInfoLibrary, PositionInfo} from "../../src/libraries/PositionInfoLibrary.sol"; contract PositionManagerNotifierTest is Test, PosmTestSetup, GasSnapshot { using PoolIdLibrary for PoolKey; using StateLibrary for IPoolManager; using Planner for Plan; - using PositionInfoLibrary for PackedPositionInfo; + using PositionInfoLibrary for PositionInfo; MockSubscriber sub; MockReturnDataSubscriber badSubscriber; From 547c8939fe72d0f18ad109157f9c49ceef48f0c6 Mon Sep 17 00:00:00 2001 From: Sara Reynolds Date: Thu, 29 Aug 2024 16:13:26 -0400 Subject: [PATCH 08/45] clear lower 8 bits on unsubscribe --- src/libraries/PositionInfoLibrary.sol | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libraries/PositionInfoLibrary.sol b/src/libraries/PositionInfoLibrary.sol index dee592d56..24dbe367c 100644 --- a/src/libraries/PositionInfoLibrary.sol +++ b/src/libraries/PositionInfoLibrary.sol @@ -38,6 +38,7 @@ library PositionInfoLibrary { uint256 internal constant MASK_UPPER_200_BITS = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000000000; uint256 internal constant MASK_8_BITS = 0xFF; uint24 internal constant MASK_24_BITS = 0xFFFFFF; + uint256 internal constant SET_UNSUBSCRIBE = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00; uint256 internal constant SET_SUBSCRIBE = 0x01; uint8 internal constant TICK_LOWER_OFFSET = 8; uint8 internal constant TICK_UPPER_OFFSET = 32; @@ -76,7 +77,7 @@ library PositionInfoLibrary { /// @dev this does not actually set any storage function setUnsubscribe(PositionInfo info) internal pure returns (PositionInfo _info) { assembly ("memory-safe") { - _info := and(info, not(SET_SUBSCRIBE)) + _info := and(info, SET_UNSUBSCRIBE) } } From e695f43a9d90a235460f36e2a2767ffbbd55cf80 Mon Sep 17 00:00:00 2001 From: Sara Reynolds Date: Thu, 29 Aug 2024 16:22:17 -0400 Subject: [PATCH 09/45] move _pay, minimize diff --- src/PositionManager.sol | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/PositionManager.sol b/src/PositionManager.sol index 8f18105a2..c7e3aa1d4 100644 --- a/src/PositionManager.sol +++ b/src/PositionManager.sol @@ -359,16 +359,6 @@ contract PositionManager is if (balance > 0) currency.transfer(to, balance); } - // implementation of abstract function DeltaResolver._pay - function _pay(Currency currency, address payer, uint256 amount) internal override { - if (payer == address(this)) { - // TODO: currency is guaranteed to not be eth so the native check in transfer is not optimal. - currency.transfer(address(poolManager), amount); - } else { - permit2.transferFrom(payer, address(poolManager), uint160(amount), Currency.unwrap(currency)); - } - } - function _modifyLiquidity( PositionInfo info, PoolKey memory poolKey, @@ -393,6 +383,16 @@ contract PositionManager is } } + // implementation of abstract function DeltaResolver._pay + function _pay(Currency currency, address payer, uint256 amount) internal override { + if (payer == address(this)) { + // TODO: currency is guaranteed to not be eth so the native check in transfer is not optimal. + currency.transfer(address(poolManager), amount); + } else { + permit2.transferFrom(payer, address(poolManager), uint160(amount), Currency.unwrap(currency)); + } + } + /// @notice an internal helper used by Notifier function _setSubscribe(uint256 tokenId) internal override { positionInfo[tokenId] = positionInfo[tokenId].setSubscribe(); From df58a26f2e1f4e43499e5fe7b8be14a163db4c4f Mon Sep 17 00:00:00 2001 From: Sara Reynolds Date: Thu, 29 Aug 2024 16:30:47 -0400 Subject: [PATCH 10/45] pr comments --- .forge-snapshots/PositionManager_mint_native.snap | 2 +- .../PositionManager_mint_nativeWithSweep_withClose.snap | 2 +- ...sitionManager_mint_nativeWithSweep_withSettlePair.snap | 2 +- .../PositionManager_mint_onSameTickLower.snap | 2 +- .../PositionManager_mint_onSameTickUpper.snap | 2 +- .forge-snapshots/PositionManager_mint_sameRange.snap | 2 +- .../PositionManager_mint_settleWithBalance_sweep.snap | 2 +- .../PositionManager_mint_warmedPool_differentRange.snap | 2 +- .forge-snapshots/PositionManager_mint_withClose.snap | 2 +- .forge-snapshots/PositionManager_mint_withSettlePair.snap | 2 +- .../PositionManager_multicall_initialize_mint.snap | 2 +- src/PositionManager.sol | 6 ++++-- src/base/Notifier.sol | 8 ++++++-- src/interfaces/IPositionManager.sol | 2 +- 14 files changed, 22 insertions(+), 16 deletions(-) diff --git a/.forge-snapshots/PositionManager_mint_native.snap b/.forge-snapshots/PositionManager_mint_native.snap index 30fb00004..113672cfa 100644 --- a/.forge-snapshots/PositionManager_mint_native.snap +++ b/.forge-snapshots/PositionManager_mint_native.snap @@ -1 +1 @@ -366040 \ No newline at end of file +365659 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_mint_nativeWithSweep_withClose.snap b/.forge-snapshots/PositionManager_mint_nativeWithSweep_withClose.snap index 666a0da57..9d172cf91 100644 --- a/.forge-snapshots/PositionManager_mint_nativeWithSweep_withClose.snap +++ b/.forge-snapshots/PositionManager_mint_nativeWithSweep_withClose.snap @@ -1 +1 @@ -374532 \ No newline at end of file +374151 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_mint_nativeWithSweep_withSettlePair.snap b/.forge-snapshots/PositionManager_mint_nativeWithSweep_withSettlePair.snap index 07b59f08d..f91b0a35c 100644 --- a/.forge-snapshots/PositionManager_mint_nativeWithSweep_withSettlePair.snap +++ b/.forge-snapshots/PositionManager_mint_nativeWithSweep_withSettlePair.snap @@ -1 +1 @@ -373834 \ No newline at end of file +373453 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_mint_onSameTickLower.snap b/.forge-snapshots/PositionManager_mint_onSameTickLower.snap index 3f680f37b..9e5d5693c 100644 --- a/.forge-snapshots/PositionManager_mint_onSameTickLower.snap +++ b/.forge-snapshots/PositionManager_mint_onSameTickLower.snap @@ -1 +1 @@ -322676 \ No newline at end of file +318295 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_mint_onSameTickUpper.snap b/.forge-snapshots/PositionManager_mint_onSameTickUpper.snap index 3a062951b..dd7e6643e 100644 --- a/.forge-snapshots/PositionManager_mint_onSameTickUpper.snap +++ b/.forge-snapshots/PositionManager_mint_onSameTickUpper.snap @@ -1 +1 @@ -323318 \ No newline at end of file +318937 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_mint_sameRange.snap b/.forge-snapshots/PositionManager_mint_sameRange.snap index 7dfb320a0..97a4e900f 100644 --- a/.forge-snapshots/PositionManager_mint_sameRange.snap +++ b/.forge-snapshots/PositionManager_mint_sameRange.snap @@ -1 +1 @@ -248900 \ No newline at end of file +244519 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_mint_settleWithBalance_sweep.snap b/.forge-snapshots/PositionManager_mint_settleWithBalance_sweep.snap index 33db8cb92..04f4407bd 100644 --- a/.forge-snapshots/PositionManager_mint_settleWithBalance_sweep.snap +++ b/.forge-snapshots/PositionManager_mint_settleWithBalance_sweep.snap @@ -1 +1 @@ -419970 \ No newline at end of file +419583 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_mint_warmedPool_differentRange.snap b/.forge-snapshots/PositionManager_mint_warmedPool_differentRange.snap index 0ed39857d..38e749485 100644 --- a/.forge-snapshots/PositionManager_mint_warmedPool_differentRange.snap +++ b/.forge-snapshots/PositionManager_mint_warmedPool_differentRange.snap @@ -1 +1 @@ -328694 \ No newline at end of file +324313 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_mint_withClose.snap b/.forge-snapshots/PositionManager_mint_withClose.snap index d2f14e912..e2cb8b54b 100644 --- a/.forge-snapshots/PositionManager_mint_withClose.snap +++ b/.forge-snapshots/PositionManager_mint_withClose.snap @@ -1 +1 @@ -421240 \ No newline at end of file +420859 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_mint_withSettlePair.snap b/.forge-snapshots/PositionManager_mint_withSettlePair.snap index 352a0a7f3..21e363de7 100644 --- a/.forge-snapshots/PositionManager_mint_withSettlePair.snap +++ b/.forge-snapshots/PositionManager_mint_withSettlePair.snap @@ -1 +1 @@ -420380 \ No newline at end of file +419999 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_multicall_initialize_mint.snap b/.forge-snapshots/PositionManager_multicall_initialize_mint.snap index ab3b994c6..7c48882ae 100644 --- a/.forge-snapshots/PositionManager_multicall_initialize_mint.snap +++ b/.forge-snapshots/PositionManager_multicall_initialize_mint.snap @@ -1 +1 @@ -465629 \ No newline at end of file +465248 \ No newline at end of file diff --git a/src/PositionManager.sol b/src/PositionManager.sol index c7e3aa1d4..a04e4f6d8 100644 --- a/src/PositionManager.sol +++ b/src/PositionManager.sol @@ -285,9 +285,11 @@ contract PositionManager is // Slippage checks should be done on the principal liquidityDelta which is the liquidityDelta - feesAccrued (liquidityDelta - feesAccrued).validateMaxIn(amount0Max, amount1Max); + bytes25 poolId = info.poolId(); // Store the poolKey if it is not already stored. - if (poolKeys[info.poolId()].isEmpty()) { - poolKeys[info.poolId()] = poolKey; + // On UniswapV4, the minimum tick spacing is 1, which means that if the tick spacing is 0, the pool key has not been set. + if (poolKeys[poolId].tickSpacing == 0) { + poolKeys[poolId] = poolKey; } } diff --git a/src/base/Notifier.sol b/src/base/Notifier.sol index bcf9d2c17..341a5ac50 100644 --- a/src/base/Notifier.sol +++ b/src/base/Notifier.sol @@ -14,6 +14,7 @@ abstract contract Notifier is INotifier { using CustomRevert for bytes4; error AlreadySubscribed(address subscriber); + error AlreadyUnsubscribed(); event Subscribed(uint256 tokenId, address subscriber); event Unsubscribed(uint256 tokenId, address subscriber); @@ -39,10 +40,11 @@ abstract contract Notifier is INotifier { payable onlyIfApproved(msg.sender, tokenId) { - _setSubscribe(tokenId); ISubscriber _subscriber = subscriber[tokenId]; if (_subscriber != NO_SUBSCRIBER) revert AlreadySubscribed(address(_subscriber)); + + _setSubscribe(tokenId); subscriber[tokenId] = ISubscriber(newSubscriber); bool success = _call(address(newSubscriber), abi.encodeCall(ISubscriber.notifySubscribe, (tokenId, data))); @@ -56,9 +58,11 @@ abstract contract Notifier is INotifier { /// @inheritdoc INotifier function unsubscribe(uint256 tokenId, bytes calldata data) external payable onlyIfApproved(msg.sender, tokenId) { - _setUnsubscribe(tokenId); ISubscriber _subscriber = subscriber[tokenId]; + if (_subscriber == NO_SUBSCRIBER) revert AlreadyUnsubscribed(); + _setUnsubscribe(tokenId); + delete subscriber[tokenId]; uint256 subscriberGasLimit = block.gaslimit.calculatePortion(BLOCK_LIMIT_BPS); diff --git a/src/interfaces/IPositionManager.sol b/src/interfaces/IPositionManager.sol index a2bccde79..12d86b1cc 100644 --- a/src/interfaces/IPositionManager.sol +++ b/src/interfaces/IPositionManager.sol @@ -32,7 +32,7 @@ interface IPositionManager is INotifier { function getPositionLiquidity(uint256 tokenId) external view returns (uint128 liquidity); /// @param tokenId the ERC721 tokenId - /// @return a uint256 packed value holding information about the position including the range (tickLower, tickUpper) + /// @return PositionInfo a uint256 packed value holding information about the position including the range (tickLower, tickUpper) /// @return poolKey the pool key of the position function getPoolPositionInfo(uint256 tokenId) external view returns (PositionInfo, PoolKey memory); } From d6d13539cf9d357a614550ec2d3eeae679d3fc87 Mon Sep 17 00:00:00 2001 From: Sara Reynolds Date: Thu, 29 Aug 2024 16:31:39 -0400 Subject: [PATCH 11/45] fix: test_fuzz_erc721Permit_SignatureDeadlineExpired --- test/erc721Permit/ERC721Permit.permit.t.sol | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/erc721Permit/ERC721Permit.permit.t.sol b/test/erc721Permit/ERC721Permit.permit.t.sol index b37cf527c..654c20c93 100644 --- a/test/erc721Permit/ERC721Permit.permit.t.sol +++ b/test/erc721Permit/ERC721Permit.permit.t.sol @@ -229,8 +229,7 @@ contract ERC721PermitTest is Test { assertEq(erc721Permit.nonces(alice, wordPos) & (1 << bitPos), 0); } - function test_fuzz_erc721Permit_SignatureDeadlineExpired() public { - address spender = 0xf30b182eA406CC5A71b8A44e2FE50cC80383F914; + function test_fuzz_erc721Permit_SignatureDeadlineExpired(address spender) public { vm.prank(alice); uint256 tokenId = erc721Permit.mint(); From 7fd6b5bf069eded239939a71f5c7a9dad3cfed46 Mon Sep 17 00:00:00 2001 From: Sara Reynolds Date: Thu, 29 Aug 2024 16:33:36 -0400 Subject: [PATCH 12/45] naming --- src/PositionManager.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PositionManager.sol b/src/PositionManager.sol index a04e4f6d8..ddc3116c8 100644 --- a/src/PositionManager.sol +++ b/src/PositionManager.sol @@ -412,7 +412,7 @@ contract PositionManager is } /// @inheritdoc IPositionManager - function getPoolPositionInfo(uint256 tokenId) public view returns (PositionInfo info, PoolKey memory poolKey) { + function getPoolAndPositionInfo(uint256 tokenId) public view returns (PositionInfo info, PoolKey memory poolKey) { info = positionInfo[tokenId]; poolKey = poolKeys[info.poolId()]; } From 84cc704df422a1b427faead1205b3185e46bfb8f Mon Sep 17 00:00:00 2001 From: Sara Reynolds Date: Thu, 29 Aug 2024 16:36:03 -0400 Subject: [PATCH 13/45] fix --- src/PositionManager.sol | 8 ++++---- src/interfaces/IPositionManager.sol | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/PositionManager.sol b/src/PositionManager.sol index ddc3116c8..502a66b45 100644 --- a/src/PositionManager.sol +++ b/src/PositionManager.sol @@ -232,7 +232,7 @@ contract PositionManager is uint128 amount1Max, bytes calldata hookData ) internal onlyIfApproved(msgSender(), tokenId) { - (PositionInfo info, PoolKey memory poolKey) = getPoolPositionInfo(tokenId); + (PositionInfo info, PoolKey memory poolKey) = getPoolAndPositionInfo(tokenId); // Note: The tokenId is used as the salt for this position, so every minted position has unique storage in the pool manager. (BalanceDelta liquidityDelta, BalanceDelta feesAccrued) = @@ -249,7 +249,7 @@ contract PositionManager is uint128 amount1Min, bytes calldata hookData ) internal onlyIfApproved(msgSender(), tokenId) { - (PositionInfo info, PoolKey memory poolKey) = getPoolPositionInfo(tokenId); + (PositionInfo info, PoolKey memory poolKey) = getPoolAndPositionInfo(tokenId); // Note: the tokenId is used as the salt. (BalanceDelta liquidityDelta, BalanceDelta feesAccrued) = @@ -298,7 +298,7 @@ contract PositionManager is internal onlyIfApproved(msgSender(), tokenId) { - (PositionInfo info, PoolKey memory poolKey) = getPoolPositionInfo(tokenId); + (PositionInfo info, PoolKey memory poolKey) = getPoolAndPositionInfo(tokenId); uint256 liquidity = uint256(_getLiquidity(tokenId, poolKey, info.tickLower(), info.tickUpper())); @@ -419,7 +419,7 @@ contract PositionManager is /// @inheritdoc IPositionManager function getPositionLiquidity(uint256 tokenId) external view returns (uint128 liquidity) { - (PositionInfo info, PoolKey memory poolKey) = getPoolPositionInfo(tokenId); + (PositionInfo info, PoolKey memory poolKey) = getPoolAndPositionInfo(tokenId); liquidity = _getLiquidity(tokenId, poolKey, info.tickLower(), info.tickUpper()); } diff --git a/src/interfaces/IPositionManager.sol b/src/interfaces/IPositionManager.sol index 12d86b1cc..9b52bf1bd 100644 --- a/src/interfaces/IPositionManager.sol +++ b/src/interfaces/IPositionManager.sol @@ -34,5 +34,5 @@ interface IPositionManager is INotifier { /// @param tokenId the ERC721 tokenId /// @return PositionInfo a uint256 packed value holding information about the position including the range (tickLower, tickUpper) /// @return poolKey the pool key of the position - function getPoolPositionInfo(uint256 tokenId) external view returns (PositionInfo, PoolKey memory); + function getPoolAndPositionInfo(uint256 tokenId) external view returns (PositionInfo, PoolKey memory); } From ec4ebdd4ea15b3c9186d3c159dbef4502de4411e Mon Sep 17 00:00:00 2001 From: Sara Reynolds Date: Thu, 29 Aug 2024 16:40:49 -0400 Subject: [PATCH 14/45] natspec --- src/PositionManager.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PositionManager.sol b/src/PositionManager.sol index 502a66b45..2753332d8 100644 --- a/src/PositionManager.sol +++ b/src/PositionManager.sol @@ -399,8 +399,8 @@ contract PositionManager is function _setSubscribe(uint256 tokenId) internal override { positionInfo[tokenId] = positionInfo[tokenId].setSubscribe(); } - /// @notice an internal helper used by Notifier + /// @notice an internal helper used by Notifier function _setUnsubscribe(uint256 tokenId) internal override { positionInfo[tokenId] = positionInfo[tokenId].setUnsubscribe(); } From 5a880428b077dd9a0a695f9613c14b52fc649437 Mon Sep 17 00:00:00 2001 From: Sara Reynolds Date: Thu, 29 Aug 2024 16:45:46 -0400 Subject: [PATCH 15/45] fix: stack too deep in tests --- test/libraries/CalldataDecoder.t.sol | 68 ++++++++++++---------------- test/mocks/MockCalldataDecoder.sol | 33 ++++++++++---- 2 files changed, 55 insertions(+), 46 deletions(-) diff --git a/test/libraries/CalldataDecoder.t.sol b/test/libraries/CalldataDecoder.t.sol index c8b303d16..b5205dd49 100644 --- a/test/libraries/CalldataDecoder.t.sol +++ b/test/libraries/CalldataDecoder.t.sol @@ -52,44 +52,36 @@ contract CalldataDecoderTest is Test { } // TODO: fix stack too deep here - // function test_fuzz_decodeMintParams( - // PositionConfig calldata _config, - // uint256 _liquidity, - // uint128 _amount0Max, - // uint128 _amount1Max, - // address _owner, - // bytes calldata _hookData - // ) public view { - // bytes memory params = abi.encode( - // _config.poolKey, - // _config.tickLower, - // _config.tickUpper, - // _liquidity, - // _amount0Max, - // _amount1Max, - // _owner, - // _hookData - // ); - // ( - // PoolKey memory poolKey, - // int24 tickLower, - // int24 tickUpper, - // uint256 liquidity, - // uint128 amount0Max, - // uint128 amount1Max, - // address owner, - // bytes memory hookData - // ) = decoder.decodeMintParams(params); - - // assertEq(liquidity, _liquidity); - // assertEq(amount0Max, _amount0Max); - // assertEq(amount1Max, _amount1Max); - // assertEq(owner, _owner); - // assertEq(hookData, _hookData); - // _assertEq(_config.poolKey, poolKey); - // assertEq(_config.tickLower, tickLower); - // assertEq(_config.tickUpper, tickUpper); - // } + function test_fuzz_decodeMintParams( + PositionConfig calldata _config, + uint256 _liquidity, + uint128 _amount0Max, + uint128 _amount1Max, + address _owner, + bytes calldata _hookData + ) public view { + bytes memory params = abi.encode( + _config.poolKey, + _config.tickLower, + _config.tickUpper, + _liquidity, + _amount0Max, + _amount1Max, + _owner, + _hookData + ); + + (MockCalldataDecoder.MintParams memory mintParams) = decoder.decodeMintParams(params); + + assertEq(mintParams.liquidity, _liquidity); + assertEq(mintParams.amount0Max, _amount0Max); + assertEq(mintParams.amount1Max, _amount1Max); + assertEq(mintParams.owner, _owner); + assertEq(mintParams.hookData, _hookData); + _assertEq(mintParams.poolKey, _config.poolKey); + assertEq(mintParams.tickLower, _config.tickLower); + assertEq(mintParams.tickUpper, _config.tickUpper); + } function test_fuzz_decodeSwapExactInParams(IV4Router.ExactInputParams calldata _swapParams) public view { bytes memory params = abi.encode(_swapParams); diff --git a/test/mocks/MockCalldataDecoder.sol b/test/mocks/MockCalldataDecoder.sol index d7aa33c1f..11643f3c2 100644 --- a/test/mocks/MockCalldataDecoder.sol +++ b/test/mocks/MockCalldataDecoder.sol @@ -11,6 +11,17 @@ import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol"; contract MockCalldataDecoder { using CalldataDecoder for bytes; + struct MintParams { + PoolKey poolKey; + int24 tickLower; + int24 tickUpper; + uint256 liquidity; + uint128 amount0Max; + uint128 amount1Max; + address owner; + bytes hookData; + } + function decodeModifyLiquidityParams(bytes calldata params) external pure @@ -59,10 +70,8 @@ contract MockCalldataDecoder { return params.decodeSwapExactOutSingleParams(); } - function decodeMintParams(bytes calldata params) - external - pure - returns ( + function decodeMintParams(bytes calldata params) external pure returns (MintParams memory mintParams) { + ( PoolKey memory poolKey, int24 tickLower, int24 tickUpper, @@ -70,10 +79,18 @@ contract MockCalldataDecoder { uint128 amount0Max, uint128 amount1Max, address owner, - bytes calldata hookData - ) - { - return params.decodeMintParams(); + bytes memory hookData + ) = params.decodeMintParams(); + return MintParams({ + poolKey: poolKey, + tickLower: tickLower, + tickUpper: tickUpper, + liquidity: liquidity, + amount0Max: amount0Max, + amount1Max: amount1Max, + owner: owner, + hookData: hookData + }); } function decodeCurrencyAndAddress(bytes calldata params) From fe1d709b2fb792634d4c409458ca76888a501f22 Mon Sep 17 00:00:00 2001 From: Sara Reynolds Date: Thu, 29 Aug 2024 16:46:37 -0400 Subject: [PATCH 16/45] remove console --- test/libraries/PositionInfoLibrary.t.sol | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/libraries/PositionInfoLibrary.t.sol b/test/libraries/PositionInfoLibrary.t.sol index 3ce25f095..451f6e135 100644 --- a/test/libraries/PositionInfoLibrary.t.sol +++ b/test/libraries/PositionInfoLibrary.t.sol @@ -6,8 +6,6 @@ import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol"; import {PoolIdLibrary} from "@uniswap/v4-core/src/types/PoolId.sol"; import {PositionInfo, PositionInfoLibrary, PoolId} from "../../src/libraries/PositionInfoLibrary.sol"; -import "forge-std/console2.sol"; - contract PositionInfoLibraryTest is Test { using PositionInfoLibrary for PositionInfo; using PoolIdLibrary for PoolKey; From ca37b8c3a08dc015e40826d297c5673515884c77 Mon Sep 17 00:00:00 2001 From: Sara Reynolds Date: Thu, 29 Aug 2024 17:24:32 -0400 Subject: [PATCH 17/45] move PositionConfig to shared testing infra --- src/interfaces/INotifier.sol | 1 - src/interfaces/ISubscriber.sol | 1 - src/libraries/CalldataDecoder.sol | 3 +-- test/libraries/CalldataDecoder.t.sol | 18 +----------------- test/mocks/MockBadSubscribers.sol | 1 - test/mocks/MockCalldataDecoder.sol | 1 - test/mocks/MockSubscriber.sol | 1 - test/position-managers/Execute.t.sol | 2 +- test/position-managers/FeeCollection.t.sol | 2 +- test/position-managers/IncreaseLiquidity.t.sol | 2 +- test/position-managers/NativeToken.t.sol | 2 +- test/position-managers/Permit.t.sol | 2 +- .../PositionManager.gas.t.sol | 2 +- .../PositionManager.modifyLiquidities.t.sol | 2 +- .../PositionManager.multicall.t.sol | 2 +- .../PositionManager.notifier.t.sol | 2 +- test/position-managers/PositionManager.t.sol | 2 +- test/shared/FeeMath.sol | 2 +- test/shared/LiquidityOperations.sol | 2 +- .../shared}/PositionConfig.sol | 2 +- test/shared/fuzz/LiquidityFuzzers.sol | 1 - 21 files changed, 15 insertions(+), 38 deletions(-) rename {src/libraries => test/shared}/PositionConfig.sol (74%) diff --git a/src/interfaces/INotifier.sol b/src/interfaces/INotifier.sol index d9002342e..45b63c354 100644 --- a/src/interfaces/INotifier.sol +++ b/src/interfaces/INotifier.sol @@ -1,7 +1,6 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.24; -import {PositionConfig} from "../libraries/PositionConfig.sol"; import {ISubscriber} from "./ISubscriber.sol"; /// @notice This interface is used to opt in to sending updates to external contracts about position modifications or transfers diff --git a/src/interfaces/ISubscriber.sol b/src/interfaces/ISubscriber.sol index 316930a2e..05cd6cc11 100644 --- a/src/interfaces/ISubscriber.sol +++ b/src/interfaces/ISubscriber.sol @@ -1,7 +1,6 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.24; -import {PositionConfig} from "../libraries/PositionConfig.sol"; import {BalanceDelta} from "@uniswap/v4-core/src/types/BalanceDelta.sol"; /// @notice Interface that a Subscriber contract should implement to receive updates from the v4 position manager diff --git a/src/libraries/CalldataDecoder.sol b/src/libraries/CalldataDecoder.sol index 063a049c0..ebaa418e1 100644 --- a/src/libraries/CalldataDecoder.sol +++ b/src/libraries/CalldataDecoder.sol @@ -1,7 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.0; -import {PositionConfig} from "./PositionConfig.sol"; import {Currency} from "@uniswap/v4-core/src/types/Currency.sol"; import {IV4Router} from "../interfaces/IV4Router.sol"; import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol"; @@ -45,7 +44,7 @@ library CalldataDecoder { } } - /// @dev equivalent to: abi.decode(params, (uint256, PositionConfig, uint256, uint128, uint128, bytes)) in calldata + /// @dev equivalent to: abi.decode(params, (uint256, uint256, uint128, uint128, bytes)) in calldata function decodeModifyLiquidityParams(bytes calldata params) internal pure diff --git a/test/libraries/CalldataDecoder.t.sol b/test/libraries/CalldataDecoder.t.sol index b5205dd49..da24aa959 100644 --- a/test/libraries/CalldataDecoder.t.sol +++ b/test/libraries/CalldataDecoder.t.sol @@ -6,7 +6,7 @@ import {Currency} from "@uniswap/v4-core/src/types/Currency.sol"; import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol"; import {MockCalldataDecoder} from "../mocks/MockCalldataDecoder.sol"; -import {PositionConfig} from "../../src/libraries/PositionConfig.sol"; +import {PositionConfig} from "../shared/PositionConfig.sol"; import {IV4Router} from "../../src/interfaces/IV4Router.sol"; import {PathKey} from "../../src/libraries/PathKey.sol"; @@ -51,7 +51,6 @@ contract CalldataDecoderTest is Test { assertEq(amount1Min, _amount1Min); } - // TODO: fix stack too deep here function test_fuzz_decodeMintParams( PositionConfig calldata _config, uint256 _liquidity, @@ -199,12 +198,6 @@ contract CalldataDecoderTest is Test { } } - function _assertEq(PositionConfig memory config1, PositionConfig memory config2) internal pure { - _assertEq(config1.poolKey, config2.poolKey); - assertEq(config1.tickLower, config2.tickLower); - assertEq(config1.tickUpper, config2.tickUpper); - } - function _assertEq(PoolKey memory key1, PoolKey memory key2) internal pure { assertEq(Currency.unwrap(key1.currency0), Currency.unwrap(key2.currency0)); assertEq(Currency.unwrap(key1.currency1), Currency.unwrap(key2.currency1)); @@ -212,13 +205,4 @@ contract CalldataDecoderTest is Test { assertEq(key1.tickSpacing, key2.tickSpacing); assertEq(address(key1.hooks), address(key2.hooks)); } - - function _assertEq(PositionConfig memory config, PoolKey memory poolKey, int24 tickLower, int24 tickUpper) - internal - pure - { - _assertEq(config.poolKey, poolKey); - assertEq(config.tickLower, tickLower); - assertEq(config.tickUpper, tickUpper); - } } diff --git a/test/mocks/MockBadSubscribers.sol b/test/mocks/MockBadSubscribers.sol index 4e7c2255b..75a2c639f 100644 --- a/test/mocks/MockBadSubscribers.sol +++ b/test/mocks/MockBadSubscribers.sol @@ -2,7 +2,6 @@ pragma solidity ^0.8.20; import {ISubscriber} from "../../src/interfaces/ISubscriber.sol"; -import {PositionConfig} from "../../src/libraries/PositionConfig.sol"; import {PositionManager} from "../../src/PositionManager.sol"; import {BalanceDelta} from "@uniswap/v4-core/src/types/BalanceDelta.sol"; diff --git a/test/mocks/MockCalldataDecoder.sol b/test/mocks/MockCalldataDecoder.sol index 11643f3c2..2fbf7afe6 100644 --- a/test/mocks/MockCalldataDecoder.sol +++ b/test/mocks/MockCalldataDecoder.sol @@ -1,7 +1,6 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.24; -import {PositionConfig} from "../../src/libraries/PositionConfig.sol"; import {CalldataDecoder} from "../../src/libraries/CalldataDecoder.sol"; import {IV4Router} from "../../src/interfaces/IV4Router.sol"; import {Currency} from "@uniswap/v4-core/src/types/Currency.sol"; diff --git a/test/mocks/MockSubscriber.sol b/test/mocks/MockSubscriber.sol index 9ab7ae220..236791806 100644 --- a/test/mocks/MockSubscriber.sol +++ b/test/mocks/MockSubscriber.sol @@ -2,7 +2,6 @@ pragma solidity ^0.8.20; import {ISubscriber} from "../../src/interfaces/ISubscriber.sol"; -import {PositionConfig} from "../../src/libraries/PositionConfig.sol"; import {PositionManager} from "../../src/PositionManager.sol"; import {BalanceDelta} from "@uniswap/v4-core/src/types/BalanceDelta.sol"; diff --git a/test/position-managers/Execute.t.sol b/test/position-managers/Execute.t.sol index d2e1f7c12..42fb1a7b6 100644 --- a/test/position-managers/Execute.t.sol +++ b/test/position-managers/Execute.t.sol @@ -19,7 +19,7 @@ import {IERC20} from "forge-std/interfaces/IERC20.sol"; import {IPositionManager} from "../../src/interfaces/IPositionManager.sol"; import {PositionManager} from "../../src/PositionManager.sol"; -import {PositionConfig} from "../../src/libraries/PositionConfig.sol"; +import {PositionConfig} from "../shared/PositionConfig.sol"; import {ActionConstants} from "../../src/libraries/ActionConstants.sol"; import {Actions} from "../../src/libraries/Actions.sol"; diff --git a/test/position-managers/FeeCollection.t.sol b/test/position-managers/FeeCollection.t.sol index 54ad44ad3..837e2a82d 100644 --- a/test/position-managers/FeeCollection.t.sol +++ b/test/position-managers/FeeCollection.t.sol @@ -14,7 +14,7 @@ import {FixedPointMathLib} from "solmate/src/utils/FixedPointMathLib.sol"; import {IERC20} from "forge-std/interfaces/IERC20.sol"; import {PositionManager} from "../../src/PositionManager.sol"; -import {PositionConfig} from "../../src/libraries/PositionConfig.sol"; +import {PositionConfig} from "../shared/PositionConfig.sol"; import {LiquidityFuzzers} from "../shared/fuzz/LiquidityFuzzers.sol"; import {PosmTestSetup} from "../shared/PosmTestSetup.sol"; diff --git a/test/position-managers/IncreaseLiquidity.t.sol b/test/position-managers/IncreaseLiquidity.t.sol index 02bb69cf0..fe0689060 100644 --- a/test/position-managers/IncreaseLiquidity.t.sol +++ b/test/position-managers/IncreaseLiquidity.t.sol @@ -21,7 +21,7 @@ import {IERC20} from "forge-std/interfaces/IERC20.sol"; import {PositionManager} from "../../src/PositionManager.sol"; import {DeltaResolver} from "../../src/base/DeltaResolver.sol"; -import {PositionConfig} from "../../src/libraries/PositionConfig.sol"; +import {PositionConfig} from "../shared/PositionConfig.sol"; import {SlippageCheckLibrary} from "../../src/libraries/SlippageCheck.sol"; import {IPositionManager} from "../../src/interfaces/IPositionManager.sol"; import {Actions} from "../../src/libraries/Actions.sol"; diff --git a/test/position-managers/NativeToken.t.sol b/test/position-managers/NativeToken.t.sol index b8898c1a9..9029c5802 100644 --- a/test/position-managers/NativeToken.t.sol +++ b/test/position-managers/NativeToken.t.sol @@ -32,7 +32,7 @@ import {MockSubscriber} from "../mocks/MockSubscriber.sol"; import {LiquidityFuzzers} from "../shared/fuzz/LiquidityFuzzers.sol"; import {PosmTestSetup} from "../shared/PosmTestSetup.sol"; import {Planner, Plan} from "../shared/Planner.sol"; -import {PositionConfig} from "../../src/libraries/PositionConfig.sol"; +import {PositionConfig} from "../shared/PositionConfig.sol"; contract PositionManagerTest is Test, PosmTestSetup, LiquidityFuzzers { using FixedPointMathLib for uint256; diff --git a/test/position-managers/Permit.t.sol b/test/position-managers/Permit.t.sol index e5d89b26f..ea3a9c6d9 100644 --- a/test/position-managers/Permit.t.sol +++ b/test/position-managers/Permit.t.sol @@ -17,7 +17,7 @@ import {IERC721Permit_v4} from "../../src/interfaces/IERC721Permit_v4.sol"; import {ERC721Permit_v4} from "../../src/base/ERC721Permit_v4.sol"; import {UnorderedNonce} from "../../src/base/UnorderedNonce.sol"; -import {PositionConfig} from "../../src/libraries/PositionConfig.sol"; +import {PositionConfig} from "../shared/PositionConfig.sol"; import {IPositionManager} from "../../src/interfaces/IPositionManager.sol"; import {PosmTestSetup} from "../shared/PosmTestSetup.sol"; diff --git a/test/position-managers/PositionManager.gas.t.sol b/test/position-managers/PositionManager.gas.t.sol index 03e82636f..b706e1e11 100644 --- a/test/position-managers/PositionManager.gas.t.sol +++ b/test/position-managers/PositionManager.gas.t.sol @@ -19,7 +19,7 @@ import {IERC20} from "forge-std/interfaces/IERC20.sol"; import {IPositionManager} from "../../src/interfaces/IPositionManager.sol"; import {Actions} from "../../src/libraries/Actions.sol"; import {PositionManager} from "../../src/PositionManager.sol"; -import {PositionConfig} from "../../src/libraries/PositionConfig.sol"; +import {PositionConfig} from "../shared/PositionConfig.sol"; import {IMulticall_v4} from "../../src/interfaces/IMulticall_v4.sol"; import {Planner, Plan} from "../shared/Planner.sol"; import {PosmTestSetup} from "../shared/PosmTestSetup.sol"; diff --git a/test/position-managers/PositionManager.modifyLiquidities.t.sol b/test/position-managers/PositionManager.modifyLiquidities.t.sol index 52a1afd91..9b135e4f3 100644 --- a/test/position-managers/PositionManager.modifyLiquidities.t.sol +++ b/test/position-managers/PositionManager.modifyLiquidities.t.sol @@ -18,7 +18,7 @@ import {IPositionManager} from "../../src/interfaces/IPositionManager.sol"; import {ReentrancyLock} from "../../src/base/ReentrancyLock.sol"; import {Actions} from "../../src/libraries/Actions.sol"; import {PositionManager} from "../../src/PositionManager.sol"; -import {PositionConfig} from "../../src/libraries/PositionConfig.sol"; +import {PositionConfig} from "../shared/PositionConfig.sol"; import {LiquidityFuzzers} from "../shared/fuzz/LiquidityFuzzers.sol"; import {Planner, Plan} from "../shared/Planner.sol"; diff --git a/test/position-managers/PositionManager.multicall.t.sol b/test/position-managers/PositionManager.multicall.t.sol index 1497cad08..064281e8b 100644 --- a/test/position-managers/PositionManager.multicall.t.sol +++ b/test/position-managers/PositionManager.multicall.t.sol @@ -21,7 +21,7 @@ import {IPositionManager} from "../../src/interfaces/IPositionManager.sol"; import {PoolInitializer} from "../../src/base/PoolInitializer.sol"; import {Actions} from "../../src/libraries/Actions.sol"; import {PositionManager} from "../../src/PositionManager.sol"; -import {PositionConfig} from "../../src/libraries/PositionConfig.sol"; +import {PositionConfig} from "../shared/PositionConfig.sol"; import {IMulticall_v4} from "../../src/interfaces/IMulticall_v4.sol"; import {LiquidityFuzzers} from "../shared/fuzz/LiquidityFuzzers.sol"; import {Planner, Plan} from "../shared/Planner.sol"; diff --git a/test/position-managers/PositionManager.notifier.t.sol b/test/position-managers/PositionManager.notifier.t.sol index 4f296fd4a..1ca6dff02 100644 --- a/test/position-managers/PositionManager.notifier.t.sol +++ b/test/position-managers/PositionManager.notifier.t.sol @@ -13,7 +13,7 @@ import {GasSnapshot} from "forge-gas-snapshot/GasSnapshot.sol"; import {PosmTestSetup} from "../shared/PosmTestSetup.sol"; import {MockSubscriber} from "../mocks/MockSubscriber.sol"; import {ISubscriber} from "../../src/interfaces/ISubscriber.sol"; -import {PositionConfig} from "../../src/libraries/PositionConfig.sol"; +import {PositionConfig} from "../shared/PositionConfig.sol"; import {IPositionManager} from "../../src/interfaces/IPositionManager.sol"; import {Plan, Planner} from "../shared/Planner.sol"; import {Actions} from "../../src/libraries/Actions.sol"; diff --git a/test/position-managers/PositionManager.t.sol b/test/position-managers/PositionManager.t.sol index 6cb234354..19ad958c8 100644 --- a/test/position-managers/PositionManager.t.sol +++ b/test/position-managers/PositionManager.t.sol @@ -24,7 +24,7 @@ import {IPositionManager} from "../../src/interfaces/IPositionManager.sol"; import {Actions} from "../../src/libraries/Actions.sol"; import {PositionManager} from "../../src/PositionManager.sol"; import {DeltaResolver} from "../../src/base/DeltaResolver.sol"; -import {PositionConfig} from "../../src/libraries/PositionConfig.sol"; +import {PositionConfig} from "../shared/PositionConfig.sol"; import {SlippageCheckLibrary} from "../../src/libraries/SlippageCheck.sol"; import {BaseActionsRouter} from "../../src/base/BaseActionsRouter.sol"; import {ActionConstants} from "../../src/libraries/ActionConstants.sol"; diff --git a/test/shared/FeeMath.sol b/test/shared/FeeMath.sol index 25bdba5f4..72ad5659a 100644 --- a/test/shared/FeeMath.sol +++ b/test/shared/FeeMath.sol @@ -13,7 +13,7 @@ import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol"; import {IPositionManager} from "../../src/interfaces/IPositionManager.sol"; import {PositionManager} from "../../src/PositionManager.sol"; -import {PositionConfig} from "../../src/libraries/PositionConfig.sol"; +import {PositionConfig} from "../shared/PositionConfig.sol"; library FeeMath { using SafeCast for uint256; diff --git a/test/shared/LiquidityOperations.sol b/test/shared/LiquidityOperations.sol index 051e07c66..80d2d81e7 100644 --- a/test/shared/LiquidityOperations.sol +++ b/test/shared/LiquidityOperations.sol @@ -10,7 +10,7 @@ import {LiquidityAmounts} from "@uniswap/v4-core/test/utils/LiquidityAmounts.sol import {SafeCast} from "@uniswap/v4-core/src/libraries/SafeCast.sol"; import {PositionManager, Actions} from "../../src/PositionManager.sol"; -import {PositionConfig} from "../../src/libraries/PositionConfig.sol"; +import {PositionConfig} from "./PositionConfig.sol"; import {Planner, Plan} from "../shared/Planner.sol"; import {HookSavesDelta} from "./HookSavesDelta.sol"; diff --git a/src/libraries/PositionConfig.sol b/test/shared/PositionConfig.sol similarity index 74% rename from src/libraries/PositionConfig.sol rename to test/shared/PositionConfig.sol index 65558ab26..a2ab832bd 100644 --- a/src/libraries/PositionConfig.sol +++ b/test/shared/PositionConfig.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.24; import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol"; -// A PositionConfig is the input for creating and modifying a Position in core +// A helper struct used for tests struct PositionConfig { PoolKey poolKey; int24 tickLower; diff --git a/test/shared/fuzz/LiquidityFuzzers.sol b/test/shared/fuzz/LiquidityFuzzers.sol index ca14dbdf6..62f3f87bf 100644 --- a/test/shared/fuzz/LiquidityFuzzers.sol +++ b/test/shared/fuzz/LiquidityFuzzers.sol @@ -9,7 +9,6 @@ import {Fuzzers} from "@uniswap/v4-core/src/test/Fuzzers.sol"; import {IPositionManager} from "../../../src/interfaces/IPositionManager.sol"; import {Actions} from "../../../src/libraries/Actions.sol"; -import {PositionConfig} from "../../../src/libraries/PositionConfig.sol"; import {Planner, Plan} from "../../shared/Planner.sol"; contract LiquidityFuzzers is Fuzzers { From ec147293c8c2a84352886023faf3f393f01fdef2 Mon Sep 17 00:00:00 2001 From: Sara Reynolds Date: Thu, 29 Aug 2024 17:28:05 -0400 Subject: [PATCH 18/45] remove pool key checker lib --- src/PositionManager.sol | 2 -- src/libraries/PoolKeyChecker.sol | 12 ------------ 2 files changed, 14 deletions(-) delete mode 100644 src/libraries/PoolKeyChecker.sol diff --git a/src/PositionManager.sol b/src/PositionManager.sol index 66f17cb1f..279d000fb 100644 --- a/src/PositionManager.sol +++ b/src/PositionManager.sol @@ -24,7 +24,6 @@ import {Notifier} from "./base/Notifier.sol"; import {CalldataDecoder} from "./libraries/CalldataDecoder.sol"; import {Permit2Forwarder} from "./base/Permit2Forwarder.sol"; import {SlippageCheckLibrary} from "./libraries/SlippageCheck.sol"; -import {PoolKeyChecker} from "./libraries/PoolKeyChecker.sol"; import {PositionInfo, PositionInfoLibrary} from "./libraries/PositionInfoLibrary.sol"; // 444444444 @@ -111,7 +110,6 @@ contract PositionManager is using SafeCast for int256; using CalldataDecoder for bytes; using SlippageCheckLibrary for BalanceDelta; - using PoolKeyChecker for PoolKey; using PositionInfoLibrary for PositionInfo; /// @inheritdoc IPositionManager diff --git a/src/libraries/PoolKeyChecker.sol b/src/libraries/PoolKeyChecker.sol deleted file mode 100644 index 949230bf3..000000000 --- a/src/libraries/PoolKeyChecker.sol +++ /dev/null @@ -1,12 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.24; - -import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol"; - -library PoolKeyChecker { - // On UniswapV4, the minimum tick spacing is 1. - // This means that if the tick spacing is 0, the pool key has not been set. - function isEmpty(PoolKey memory poolKey) internal pure returns (bool) { - return poolKey.tickSpacing == 0; - } -} From 21ae9012b830baf85396fa2b643c3dd60c14c044 Mon Sep 17 00:00:00 2001 From: Sara Reynolds Date: Thu, 29 Aug 2024 19:32:58 -0400 Subject: [PATCH 19/45] pr commmeeennnnttsss --- .forge-snapshots/PositionManager_burn_empty.snap | 2 +- .../PositionManager_burn_empty_native.snap | 2 +- ...tionManager_burn_nonEmpty_native_withClose.snap | 2 +- ...nManager_burn_nonEmpty_native_withTakePair.snap | 2 +- .../PositionManager_burn_nonEmpty_withClose.snap | 2 +- ...PositionManager_burn_nonEmpty_withTakePair.snap | 2 +- .../PositionManager_collect_native.snap | 2 +- .../PositionManager_collect_sameRange.snap | 2 +- .../PositionManager_collect_withClose.snap | 2 +- .../PositionManager_collect_withTakePair.snap | 2 +- .../PositionManager_decreaseLiquidity_native.snap | 2 +- ...ositionManager_decreaseLiquidity_withClose.snap | 2 +- ...tionManager_decreaseLiquidity_withTakePair.snap | 2 +- .../PositionManager_decrease_burnEmpty.snap | 2 +- .../PositionManager_decrease_burnEmpty_native.snap | 2 +- ...ionManager_decrease_sameRange_allLiquidity.snap | 2 +- .../PositionManager_decrease_take_take.snap | 2 +- ...nManager_increaseLiquidity_erc20_withClose.snap | 2 +- ...ger_increaseLiquidity_erc20_withSettlePair.snap | 2 +- .../PositionManager_increaseLiquidity_native.snap | 2 +- ...er_increase_autocompoundExactUnclaimedFees.snap | 2 +- ...ager_increase_autocompoundExcessFeesCredit.snap | 2 +- ...nManager_increase_autocompound_clearExcess.snap | 2 +- src/PositionManager.sol | 14 +++++++------- src/base/Notifier.sol | 8 ++++---- src/interfaces/IPositionManager.sol | 2 +- src/libraries/PositionInfoLibrary.sol | 1 + test/libraries/PositionInfoLibrary.t.sol | 4 ++-- test/mocks/MockCalldataDecoder.sol | 3 ++- 29 files changed, 40 insertions(+), 38 deletions(-) diff --git a/.forge-snapshots/PositionManager_burn_empty.snap b/.forge-snapshots/PositionManager_burn_empty.snap index 103f8cbee..a958c1a85 100644 --- a/.forge-snapshots/PositionManager_burn_empty.snap +++ b/.forge-snapshots/PositionManager_burn_empty.snap @@ -1 +1 @@ -49954 \ No newline at end of file +49976 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_burn_empty_native.snap b/.forge-snapshots/PositionManager_burn_empty_native.snap index 103f8cbee..a958c1a85 100644 --- a/.forge-snapshots/PositionManager_burn_empty_native.snap +++ b/.forge-snapshots/PositionManager_burn_empty_native.snap @@ -1 +1 @@ -49954 \ No newline at end of file +49976 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_burn_nonEmpty_native_withClose.snap b/.forge-snapshots/PositionManager_burn_nonEmpty_native_withClose.snap index 4a524dd81..6f2f03083 100644 --- a/.forge-snapshots/PositionManager_burn_nonEmpty_native_withClose.snap +++ b/.forge-snapshots/PositionManager_burn_nonEmpty_native_withClose.snap @@ -1 +1 @@ -126207 \ No newline at end of file +126229 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_burn_nonEmpty_native_withTakePair.snap b/.forge-snapshots/PositionManager_burn_nonEmpty_native_withTakePair.snap index 2e228131e..63438b6bf 100644 --- a/.forge-snapshots/PositionManager_burn_nonEmpty_native_withTakePair.snap +++ b/.forge-snapshots/PositionManager_burn_nonEmpty_native_withTakePair.snap @@ -1 +1 @@ -125704 \ No newline at end of file +125727 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_burn_nonEmpty_withClose.snap b/.forge-snapshots/PositionManager_burn_nonEmpty_withClose.snap index b11d0da00..108ca145a 100644 --- a/.forge-snapshots/PositionManager_burn_nonEmpty_withClose.snap +++ b/.forge-snapshots/PositionManager_burn_nonEmpty_withClose.snap @@ -1 +1 @@ -133103 \ No newline at end of file +133125 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_burn_nonEmpty_withTakePair.snap b/.forge-snapshots/PositionManager_burn_nonEmpty_withTakePair.snap index bb2d92b47..f2db0b443 100644 --- a/.forge-snapshots/PositionManager_burn_nonEmpty_withTakePair.snap +++ b/.forge-snapshots/PositionManager_burn_nonEmpty_withTakePair.snap @@ -1 +1 @@ -132600 \ No newline at end of file +132623 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_collect_native.snap b/.forge-snapshots/PositionManager_collect_native.snap index 21cc20540..47dc9bae6 100644 --- a/.forge-snapshots/PositionManager_collect_native.snap +++ b/.forge-snapshots/PositionManager_collect_native.snap @@ -1 +1 @@ -146341 \ No newline at end of file +146373 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_collect_sameRange.snap b/.forge-snapshots/PositionManager_collect_sameRange.snap index a6bdd7250..ed7f314c9 100644 --- a/.forge-snapshots/PositionManager_collect_sameRange.snap +++ b/.forge-snapshots/PositionManager_collect_sameRange.snap @@ -1 +1 @@ -154961 \ No newline at end of file +154993 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_collect_withClose.snap b/.forge-snapshots/PositionManager_collect_withClose.snap index a6bdd7250..ed7f314c9 100644 --- a/.forge-snapshots/PositionManager_collect_withClose.snap +++ b/.forge-snapshots/PositionManager_collect_withClose.snap @@ -1 +1 @@ -154961 \ No newline at end of file +154993 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_collect_withTakePair.snap b/.forge-snapshots/PositionManager_collect_withTakePair.snap index a96db4e2b..a547f8b8d 100644 --- a/.forge-snapshots/PositionManager_collect_withTakePair.snap +++ b/.forge-snapshots/PositionManager_collect_withTakePair.snap @@ -1 +1 @@ -154345 \ No newline at end of file +154377 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_decreaseLiquidity_native.snap b/.forge-snapshots/PositionManager_decreaseLiquidity_native.snap index 8550686dc..b331d487f 100644 --- a/.forge-snapshots/PositionManager_decreaseLiquidity_native.snap +++ b/.forge-snapshots/PositionManager_decreaseLiquidity_native.snap @@ -1 +1 @@ -112548 \ No newline at end of file +112573 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_decreaseLiquidity_withClose.snap b/.forge-snapshots/PositionManager_decreaseLiquidity_withClose.snap index ed71e09b7..6f87f5ee2 100644 --- a/.forge-snapshots/PositionManager_decreaseLiquidity_withClose.snap +++ b/.forge-snapshots/PositionManager_decreaseLiquidity_withClose.snap @@ -1 +1 @@ -120504 \ No newline at end of file +120536 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_decreaseLiquidity_withTakePair.snap b/.forge-snapshots/PositionManager_decreaseLiquidity_withTakePair.snap index 3bb7829f7..4393c1d91 100644 --- a/.forge-snapshots/PositionManager_decreaseLiquidity_withTakePair.snap +++ b/.forge-snapshots/PositionManager_decreaseLiquidity_withTakePair.snap @@ -1 +1 @@ -119888 \ No newline at end of file +119920 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_decrease_burnEmpty.snap b/.forge-snapshots/PositionManager_decrease_burnEmpty.snap index 2621d201d..288e67b86 100644 --- a/.forge-snapshots/PositionManager_decrease_burnEmpty.snap +++ b/.forge-snapshots/PositionManager_decrease_burnEmpty.snap @@ -1 +1 @@ -135944 \ No newline at end of file +135992 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_decrease_burnEmpty_native.snap b/.forge-snapshots/PositionManager_decrease_burnEmpty_native.snap index c543dd683..85e7974ca 100644 --- a/.forge-snapshots/PositionManager_decrease_burnEmpty_native.snap +++ b/.forge-snapshots/PositionManager_decrease_burnEmpty_native.snap @@ -1 +1 @@ -129048 \ No newline at end of file +129096 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_decrease_sameRange_allLiquidity.snap b/.forge-snapshots/PositionManager_decrease_sameRange_allLiquidity.snap index 02a21f0e3..7833004c4 100644 --- a/.forge-snapshots/PositionManager_decrease_sameRange_allLiquidity.snap +++ b/.forge-snapshots/PositionManager_decrease_sameRange_allLiquidity.snap @@ -1 +1 @@ -133220 \ No newline at end of file +133252 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_decrease_take_take.snap b/.forge-snapshots/PositionManager_decrease_take_take.snap index 09b8ea95e..eef2a0204 100644 --- a/.forge-snapshots/PositionManager_decrease_take_take.snap +++ b/.forge-snapshots/PositionManager_decrease_take_take.snap @@ -1 +1 @@ -121037 \ No newline at end of file +121069 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_increaseLiquidity_erc20_withClose.snap b/.forge-snapshots/PositionManager_increaseLiquidity_erc20_withClose.snap index 0048705c8..f35a8a75f 100644 --- a/.forge-snapshots/PositionManager_increaseLiquidity_erc20_withClose.snap +++ b/.forge-snapshots/PositionManager_increaseLiquidity_erc20_withClose.snap @@ -1 +1 @@ -159211 \ No newline at end of file +159243 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_increaseLiquidity_erc20_withSettlePair.snap b/.forge-snapshots/PositionManager_increaseLiquidity_erc20_withSettlePair.snap index 5438704ab..000a55b24 100644 --- a/.forge-snapshots/PositionManager_increaseLiquidity_erc20_withSettlePair.snap +++ b/.forge-snapshots/PositionManager_increaseLiquidity_erc20_withSettlePair.snap @@ -1 +1 @@ -158237 \ No newline at end of file +158269 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_increaseLiquidity_native.snap b/.forge-snapshots/PositionManager_increaseLiquidity_native.snap index 1cbfad85b..e165e9044 100644 --- a/.forge-snapshots/PositionManager_increaseLiquidity_native.snap +++ b/.forge-snapshots/PositionManager_increaseLiquidity_native.snap @@ -1 +1 @@ -141239 \ No newline at end of file +141271 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_increase_autocompoundExactUnclaimedFees.snap b/.forge-snapshots/PositionManager_increase_autocompoundExactUnclaimedFees.snap index 262480fdc..780e30b5b 100644 --- a/.forge-snapshots/PositionManager_increase_autocompoundExactUnclaimedFees.snap +++ b/.forge-snapshots/PositionManager_increase_autocompoundExactUnclaimedFees.snap @@ -1 +1 @@ -137356 \ No newline at end of file +137388 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_increase_autocompoundExcessFeesCredit.snap b/.forge-snapshots/PositionManager_increase_autocompoundExcessFeesCredit.snap index d22c381d6..4c54d87d3 100644 --- a/.forge-snapshots/PositionManager_increase_autocompoundExcessFeesCredit.snap +++ b/.forge-snapshots/PositionManager_increase_autocompoundExcessFeesCredit.snap @@ -1 +1 @@ -178272 \ No newline at end of file +178304 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_increase_autocompound_clearExcess.snap b/.forge-snapshots/PositionManager_increase_autocompound_clearExcess.snap index b2ad3c815..28022cc91 100644 --- a/.forge-snapshots/PositionManager_increase_autocompound_clearExcess.snap +++ b/.forge-snapshots/PositionManager_increase_autocompound_clearExcess.snap @@ -1 +1 @@ -148228 \ No newline at end of file +148260 \ No newline at end of file diff --git a/src/PositionManager.sol b/src/PositionManager.sol index 279d000fb..6849f61f4 100644 --- a/src/PositionManager.sol +++ b/src/PositionManager.sol @@ -232,7 +232,7 @@ contract PositionManager is uint128 amount1Max, bytes calldata hookData ) internal onlyIfApproved(msgSender(), tokenId) { - (PositionInfo info, PoolKey memory poolKey) = getPoolAndPositionInfo(tokenId); + (PoolKey memory poolKey, PositionInfo info) = getPoolAndPositionInfo(tokenId); // Note: The tokenId is used as the salt for this position, so every minted position has unique storage in the pool manager. (BalanceDelta liquidityDelta, BalanceDelta feesAccrued) = @@ -249,7 +249,7 @@ contract PositionManager is uint128 amount1Min, bytes calldata hookData ) internal onlyIfApproved(msgSender(), tokenId) { - (PositionInfo info, PoolKey memory poolKey) = getPoolAndPositionInfo(tokenId); + (PoolKey memory poolKey, PositionInfo info) = getPoolAndPositionInfo(tokenId); // Note: the tokenId is used as the salt. (BalanceDelta liquidityDelta, BalanceDelta feesAccrued) = @@ -298,7 +298,7 @@ contract PositionManager is internal onlyIfApproved(msgSender(), tokenId) { - (PositionInfo info, PoolKey memory poolKey) = getPoolAndPositionInfo(tokenId); + (PoolKey memory poolKey, PositionInfo info) = getPoolAndPositionInfo(tokenId); uint256 liquidity = uint256(_getLiquidity(tokenId, poolKey, info.tickLower(), info.tickUpper())); @@ -396,12 +396,12 @@ contract PositionManager is } /// @notice an internal helper used by Notifier - function _setSubscribe(uint256 tokenId) internal override { + function _setSubscribed(uint256 tokenId) internal override { positionInfo[tokenId] = positionInfo[tokenId].setSubscribe(); } /// @notice an internal helper used by Notifier - function _setUnsubscribe(uint256 tokenId) internal override { + function _setUnsubscribed(uint256 tokenId) internal override { positionInfo[tokenId] = positionInfo[tokenId].setUnsubscribe(); } @@ -412,14 +412,14 @@ contract PositionManager is } /// @inheritdoc IPositionManager - function getPoolAndPositionInfo(uint256 tokenId) public view returns (PositionInfo info, PoolKey memory poolKey) { + function getPoolAndPositionInfo(uint256 tokenId) public view returns (PoolKey memory poolKey, PositionInfo info) { info = positionInfo[tokenId]; poolKey = poolKeys[info.poolId()]; } /// @inheritdoc IPositionManager function getPositionLiquidity(uint256 tokenId) external view returns (uint128 liquidity) { - (PositionInfo info, PoolKey memory poolKey) = getPoolAndPositionInfo(tokenId); + (PoolKey memory poolKey, PositionInfo info) = getPoolAndPositionInfo(tokenId); liquidity = _getLiquidity(tokenId, poolKey, info.tickLower(), info.tickUpper()); } diff --git a/src/base/Notifier.sol b/src/base/Notifier.sol index 7ce5ed93f..144d71814 100644 --- a/src/base/Notifier.sol +++ b/src/base/Notifier.sol @@ -31,9 +31,9 @@ abstract contract Notifier is INotifier { modifier onlyIfApproved(address caller, uint256 tokenId) virtual; - function _setUnsubscribe(uint256 tokenId) internal virtual; + function _setUnsubscribed(uint256 tokenId) internal virtual; - function _setSubscribe(uint256 tokenId) internal virtual; + function _setSubscribed(uint256 tokenId) internal virtual; /// @inheritdoc INotifier function subscribe(uint256 tokenId, address newSubscriber, bytes calldata data) @@ -45,7 +45,7 @@ abstract contract Notifier is INotifier { if (_subscriber != NO_SUBSCRIBER) revert AlreadySubscribed(address(_subscriber)); - _setSubscribe(tokenId); + _setSubscribed(tokenId); subscriber[tokenId] = ISubscriber(newSubscriber); bool success = _call(address(newSubscriber), abi.encodeCall(ISubscriber.notifySubscribe, (tokenId, data))); @@ -62,7 +62,7 @@ abstract contract Notifier is INotifier { ISubscriber _subscriber = subscriber[tokenId]; if (_subscriber == NO_SUBSCRIBER) revert AlreadyUnsubscribed(); - _setUnsubscribe(tokenId); + _setUnsubscribed(tokenId); delete subscriber[tokenId]; diff --git a/src/interfaces/IPositionManager.sol b/src/interfaces/IPositionManager.sol index 13cd95b93..46e4f6232 100644 --- a/src/interfaces/IPositionManager.sol +++ b/src/interfaces/IPositionManager.sol @@ -34,5 +34,5 @@ interface IPositionManager is INotifier { /// @param tokenId the ERC721 tokenId /// @return PositionInfo a uint256 packed value holding information about the position including the range (tickLower, tickUpper) /// @return poolKey the pool key of the position - function getPoolAndPositionInfo(uint256 tokenId) external view returns (PositionInfo, PoolKey memory); + function getPoolAndPositionInfo(uint256 tokenId) external view returns (PoolKey memory, PositionInfo); } diff --git a/src/libraries/PositionInfoLibrary.sol b/src/libraries/PositionInfoLibrary.sol index 24dbe367c..981500559 100644 --- a/src/libraries/PositionInfoLibrary.sol +++ b/src/libraries/PositionInfoLibrary.sol @@ -43,6 +43,7 @@ library PositionInfoLibrary { uint8 internal constant TICK_LOWER_OFFSET = 8; uint8 internal constant TICK_UPPER_OFFSET = 32; + /// @dev This poolId is NOT compatible with the poolId used in UniswapV4 core. It is truncated to 25 bytes, and just used to lookup PoolKey in the poolKeys mapping. function poolId(PositionInfo info) internal pure returns (bytes25 _poolId) { assembly ("memory-safe") { _poolId := and(MASK_UPPER_200_BITS, info) diff --git a/test/libraries/PositionInfoLibrary.t.sol b/test/libraries/PositionInfoLibrary.t.sol index 451f6e135..12af50151 100644 --- a/test/libraries/PositionInfoLibrary.t.sol +++ b/test/libraries/PositionInfoLibrary.t.sol @@ -21,7 +21,7 @@ contract PositionInfoLibraryTest is Test { assertEq(info.hasSubscriber(), false); } - function test_fuzz_initialize_setSubscribe(PoolKey memory poolKey, int24 tickLower, int24 tickUpper) public pure { + function test_fuzz_initialize_setSubscribed(PoolKey memory poolKey, int24 tickLower, int24 tickUpper) public pure { PositionInfo info = PositionInfoLibrary.initialize(poolKey, tickLower, tickUpper); assertEq(info.hasSubscriber(), false); info = info.setSubscribe(); @@ -31,7 +31,7 @@ contract PositionInfoLibraryTest is Test { assertEq(info.poolId(), bytes25(PoolId.unwrap(poolKey.toId()))); } - function test_fuzz_initialize_setUnsubscribe(PoolKey memory poolKey, int24 tickLower, int24 tickUpper) + function test_fuzz_initialize_setUnsubscribed(PoolKey memory poolKey, int24 tickLower, int24 tickUpper) public pure { diff --git a/test/mocks/MockCalldataDecoder.sol b/test/mocks/MockCalldataDecoder.sol index 2fbf7afe6..e503e4b0c 100644 --- a/test/mocks/MockCalldataDecoder.sol +++ b/test/mocks/MockCalldataDecoder.sol @@ -9,7 +9,8 @@ import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol"; // we need to use a mock contract to make the calls happen in calldata not memory contract MockCalldataDecoder { using CalldataDecoder for bytes; - + + // This is used to avoid compiling with via-ir. struct MintParams { PoolKey poolKey; int24 tickLower; From b29c5fe3c0d3a8ab79198dabb0d65d96f0e8c59b Mon Sep 17 00:00:00 2001 From: Sara Reynolds Date: Thu, 29 Aug 2024 19:40:25 -0400 Subject: [PATCH 20/45] fmt --- test/mocks/MockCalldataDecoder.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/mocks/MockCalldataDecoder.sol b/test/mocks/MockCalldataDecoder.sol index e503e4b0c..d5edbb64b 100644 --- a/test/mocks/MockCalldataDecoder.sol +++ b/test/mocks/MockCalldataDecoder.sol @@ -9,7 +9,7 @@ import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol"; // we need to use a mock contract to make the calls happen in calldata not memory contract MockCalldataDecoder { using CalldataDecoder for bytes; - + // This is used to avoid compiling with via-ir. struct MintParams { PoolKey poolKey; From 69208ecbf383be3760f2ac474d2adce8f657940a Mon Sep 17 00:00:00 2001 From: dianakocsis Date: Wed, 18 Sep 2024 12:03:01 -0400 Subject: [PATCH 21/45] new files --- test/Descriptor.t.sol | 18 ++++++ test/base64.sol | 129 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 147 insertions(+) create mode 100644 test/Descriptor.t.sol create mode 100644 test/base64.sol diff --git a/test/Descriptor.t.sol b/test/Descriptor.t.sol new file mode 100644 index 000000000..bab1e90b9 --- /dev/null +++ b/test/Descriptor.t.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +import "forge-std/Test.sol"; +import {Deployers} from "@uniswap/v4-core/test/utils/Deployers.sol"; +import {PositionDescriptor} from "../src/PositionDescriptor.sol"; +import {CurrencyRatioSortOrder} from "../src/libraries/CurrencyRatioSortOrder.sol"; +import {IHooks} from "@uniswap/v4-core/src/interfaces/IHooks.sol"; +import {LiquidityAmounts} from "@uniswap/v4-core/test/utils/LiquidityAmounts.sol"; +import {TickMath} from "@uniswap/v4-core/src/libraries/TickMath.sol"; +import {PositionConfig} from "./shared/PositionConfig.sol"; +import {PosmTestSetup} from "./shared/PosmTestSetup.sol"; +import {ActionConstants} from "../src/libraries/ActionConstants.sol"; +import {Base64} from "./base64.sol"; + +contract DescriptorTest is Test, Deployers { + +} \ No newline at end of file diff --git a/test/base64.sol b/test/base64.sol new file mode 100644 index 000000000..444e16d72 --- /dev/null +++ b/test/base64.sol @@ -0,0 +1,129 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +/// @title Base64 +/// @author Brecht Devos - +/// @notice Provides functions for encoding/decoding base64 +library Base64 { + string internal constant TABLE_ENCODE = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; + bytes internal constant TABLE_DECODE = hex"0000000000000000000000000000000000000000000000000000000000000000" + hex"00000000000000000000003e0000003f3435363738393a3b3c3d000000000000" + hex"00000102030405060708090a0b0c0d0e0f101112131415161718190000000000" + hex"001a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132330000000000"; + + function encode(bytes memory data) internal pure returns (string memory) { + if (data.length == 0) return ''; + + // load the table into memory + string memory table = TABLE_ENCODE; + + // multiply by 4/3 rounded up + uint256 encodedLen = 4 * ((data.length + 2) / 3); + + // add some extra buffer at the end required for the writing + string memory result = new string(encodedLen + 32); + + assembly { + // set the actual output length + mstore(result, encodedLen) + + // prepare the lookup table + let tablePtr := add(table, 1) + + // input ptr + let dataPtr := data + let endPtr := add(dataPtr, mload(data)) + + // result ptr, jump over length + let resultPtr := add(result, 32) + + // run over the input, 3 bytes at a time + for {} lt(dataPtr, endPtr) {} + { + // read 3 bytes + dataPtr := add(dataPtr, 3) + let input := mload(dataPtr) + + // write 4 characters + mstore8(resultPtr, mload(add(tablePtr, and(shr(18, input), 0x3F)))) + resultPtr := add(resultPtr, 1) + mstore8(resultPtr, mload(add(tablePtr, and(shr(12, input), 0x3F)))) + resultPtr := add(resultPtr, 1) + mstore8(resultPtr, mload(add(tablePtr, and(shr( 6, input), 0x3F)))) + resultPtr := add(resultPtr, 1) + mstore8(resultPtr, mload(add(tablePtr, and( input, 0x3F)))) + resultPtr := add(resultPtr, 1) + } + + // padding with '=' + switch mod(mload(data), 3) + case 1 { mstore(sub(resultPtr, 2), shl(240, 0x3d3d)) } + case 2 { mstore(sub(resultPtr, 1), shl(248, 0x3d)) } + } + + return result; + } + + function decode(string memory _data) internal pure returns (bytes memory) { + bytes memory data = bytes(_data); + + if (data.length == 0) return new bytes(0); + require(data.length % 4 == 0, "invalid base64 decoder input"); + + // load the table into memory + bytes memory table = TABLE_DECODE; + + // every 4 characters represent 3 bytes + uint256 decodedLen = (data.length / 4) * 3; + + // add some extra buffer at the end required for the writing + bytes memory result = new bytes(decodedLen + 32); + + assembly { + // padding with '=' + let lastBytes := mload(add(data, mload(data))) + if eq(and(lastBytes, 0xFF), 0x3d) { + decodedLen := sub(decodedLen, 1) + if eq(and(lastBytes, 0xFFFF), 0x3d3d) { + decodedLen := sub(decodedLen, 1) + } + } + + // set the actual output length + mstore(result, decodedLen) + + // prepare the lookup table + let tablePtr := add(table, 1) + + // input ptr + let dataPtr := data + let endPtr := add(dataPtr, mload(data)) + + // result ptr, jump over length + let resultPtr := add(result, 32) + + // run over the input, 4 characters at a time + for {} lt(dataPtr, endPtr) {} + { + // read 4 characters + dataPtr := add(dataPtr, 4) + let input := mload(dataPtr) + + // write 3 bytes + let output := add( + add( + shl(18, and(mload(add(tablePtr, and(shr(24, input), 0xFF))), 0xFF)), + shl(12, and(mload(add(tablePtr, and(shr(16, input), 0xFF))), 0xFF))), + add( + shl( 6, and(mload(add(tablePtr, and(shr( 8, input), 0xFF))), 0xFF)), + and(mload(add(tablePtr, and( input , 0xFF))), 0xFF) + ) + ) + mstore(resultPtr, shl(232, output)) + resultPtr := add(resultPtr, 3) + } + } + + return result; + } +} \ No newline at end of file From e6dfb703d4d5a5f56779940586f429764004cb52 Mon Sep 17 00:00:00 2001 From: dianakocsis Date: Wed, 18 Sep 2024 18:07:37 -0400 Subject: [PATCH 22/45] commentsss --- src/PositionDescriptor.sol | 12 ++++-- src/libraries/AddressStringUtil.sol | 11 +++++- src/libraries/Descriptor.sol | 33 ++++++++++++---- src/libraries/HexStrings.sol | 4 ++ src/libraries/{NFTSVG.sol => SVG.sol} | 6 +-- src/libraries/SafeERC20Namer.sol | 55 ++++++++------------------ test/Descriptor.t.sol | 18 --------- test/base64.sol | 56 +-------------------------- test/libraries/Descriptor.t.sol | 5 ++- test/libraries/SVG.t.sol | 50 ++++++++++++++++++++++++ 10 files changed, 119 insertions(+), 131 deletions(-) rename src/libraries/{NFTSVG.sol => SVG.sol} (99%) delete mode 100644 test/Descriptor.t.sol create mode 100644 test/libraries/SVG.t.sol diff --git a/src/PositionDescriptor.sol b/src/PositionDescriptor.sol index d0c9a53ae..9eb667990 100644 --- a/src/PositionDescriptor.sol +++ b/src/PositionDescriptor.sol @@ -31,7 +31,7 @@ contract PositionDescriptor is IPositionDescriptor { address private constant TBTC = 0x8dAEBADE922dF735c38C80C7eBD708Af50815fAa; address private constant WBTC = 0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599; - // optimism, polygon, arbitrum, base? + // WBTC, DAI, TBTC, USDC, USDT address public immutable WETH9; /// @dev A null-terminated string @@ -77,11 +77,10 @@ contract PositionDescriptor is IPositionDescriptor { tokenId: tokenId, quoteCurrency: quoteCurrency, baseCurrency: baseCurrency, - // if currency is weth, use 'WETH' as symbol? - quoteCurrencySymbol: Currency.unwrap(quoteCurrency) == WETH9 || quoteCurrency.isAddressZero() + quoteCurrencySymbol: quoteCurrency.isAddressZero() ? nativeCurrencyLabel() : SafeERC20Namer.tokenSymbol(Currency.unwrap(quoteCurrency)), - baseCurrencySymbol: Currency.unwrap(baseCurrency) == WETH9 || baseCurrency.isAddressZero() + baseCurrencySymbol: baseCurrency.isAddressZero() ? nativeCurrencyLabel() : SafeERC20Namer.tokenSymbol(Currency.unwrap(baseCurrency)), quoteCurrencyDecimals: quoteCurrency.isAddressZero() @@ -116,6 +115,11 @@ contract PositionDescriptor is IPositionDescriptor { /// @return priority The priority of the currency function currencyRatioPriority(address currency) public view returns (int256) { // Currencies in order of priority on mainnet: USDC, USDT, DAI, WETH, TBTC, WBTC + // USDC > USDT > DAI > WETH > TBTC > WBTC + // or native currency + // weth is different address on different chains. passed in constructor + + // if currency is WETH OR currency is native if (currency == WETH9) { return CurrencyRatioSortOrder.DENOMINATOR; } diff --git a/src/libraries/AddressStringUtil.sol b/src/libraries/AddressStringUtil.sol index 3edb6c16a..823641d26 100644 --- a/src/libraries/AddressStringUtil.sol +++ b/src/libraries/AddressStringUtil.sol @@ -1,8 +1,13 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.24; +/// @title AddressStringUtil +/// @notice provides utility functions for converting addresses to strings library AddressStringUtil { - // converts an address to the uppercase hex string, extracting only len bytes (up to 20, multiple of 2) + /// @notice Converts an address to the uppercase hex string, extracting only len bytes (up to 20, multiple of 2) + /// @param addr the address to convert + /// @param len the number of bytes to extract + /// @return the hex string function toAsciiString(address addr, uint256 len) internal pure returns (string memory) { require(len % 2 == 0 && len > 0 && len <= 40, "AddressStringUtil: INVALID_LEN"); @@ -21,9 +26,11 @@ library AddressStringUtil { return string(s); } + /// @notice Converts a value into is corresponding ASCII character for the hex representation // hi and lo are only 4 bits and between 0 and 16 - // this method converts those values to the unicode/ascii code point for the hex representation // uses upper case for the characters + /// @param b the value to convert + /// @return c the ASCII character function char(uint8 b) private pure returns (bytes1 c) { if (b < 10) { return bytes1(b + 0x30); diff --git a/src/libraries/Descriptor.sol b/src/libraries/Descriptor.sol index 21f1731be..9298a56b6 100644 --- a/src/libraries/Descriptor.sol +++ b/src/libraries/Descriptor.sol @@ -7,7 +7,7 @@ import {TickMath} from "@uniswap/v4-core/src/libraries/TickMath.sol"; import {FullMath} from "@uniswap/v4-core/src/libraries/FullMath.sol"; import "@openzeppelin/contracts/utils/Strings.sol"; import "@openzeppelin/contracts/utils/Base64.sol"; -import "./NFTSVG.sol"; +import "./SVG.sol"; import "./HexStrings.sol"; /// @title Descriptor @@ -120,6 +120,7 @@ library Descriptor { string memory poolManager, string memory quoteCurrency ) private pure returns (string memory) { + // displays quote currency first, then base currency return string( abi.encodePacked( "This NFT represents a liquidity position in a Uniswap V4 ", @@ -466,8 +467,8 @@ library Descriptor { /// @param params Parameters needed to generate the SVG image /// @return svg The SVG image as a string function generateSVGImage(ConstructTokenURIParams memory params) internal pure returns (string memory svg) { - NFTSVG.SVGParams memory svgParams = - NFTSVG.SVGParams({ + SVG.SVGParams memory svgParams = + SVG.SVGParams({ quoteCurrency: addressToString(Currency.unwrap(params.quoteCurrency)), baseCurrency: addressToString(Currency.unwrap(params.baseCurrency)), hooks: params.hooks, @@ -491,7 +492,7 @@ library Descriptor { y3: scale(getCircleCoord(params.baseCurrency.toId(), 48, params.tokenId), 0, 255, 100, 484) }); - return NFTSVG.generateSVG(svgParams); + return SVG.generateSVG(svgParams); } /// @notice Checks if the current tick is within the tick range, above, or below @@ -513,6 +514,13 @@ library Descriptor { } } + /// @notice Scales a number from one range to another + /// @param n The number to scale + /// @param inMn The minimum of the input range + /// @param inMx The maximum of the input range + /// @param outMn The minimum of the output range + /// @param outMx The maximum of the output range + /// @return The scaled number as a string function scale( uint256 n, uint256 inMn, @@ -523,23 +531,32 @@ library Descriptor { return (n - inMn * (outMx - outMn) / (inMx - inMn) + outMn).toString(); } - /// @notice Converts a currency ID to a color hex to be used in the SVG - /// @param currency The currency ID + /// @notice Converts a currency to a color in hex to be used in the SVG + /// @param currency The currency /// @param offset The offset to slice the token hex /// @return str The color hex as a string function currencyToColorHex(uint256 currency, uint256 offset) internal pure returns (string memory str) { return string((currency >> offset).toHexStringNoPrefix(3)); } + /// @notice Gets the coordinate for a circle + /// @param currency The currency ID + /// @param offset The offset to slice the token hex + /// @param tokenId The token ID + /// @return The coordinate function getCircleCoord( uint256 currency, uint256 offset, uint256 tokenId ) internal pure returns (uint256) { - return (sliceTokenHex(currency, offset) * tokenId) % 255; + return (sliceCurrencyHex(currency, offset) * tokenId) % 255; } - function sliceTokenHex(uint256 currency, uint256 offset) internal pure returns (uint256) { + /// @notice Slices the currency ID hex + /// @param currency The currency ID + /// @param offset The offset to slice the token hex + /// @return The sliced hex + function sliceCurrencyHex(uint256 currency, uint256 offset) internal pure returns (uint256) { return uint256(uint8(currency >> offset)); } } diff --git a/src/libraries/HexStrings.sol b/src/libraries/HexStrings.sol index 334b32475..aa2f80c0a 100644 --- a/src/libraries/HexStrings.sol +++ b/src/libraries/HexStrings.sol @@ -4,6 +4,10 @@ pragma solidity ^0.8.24; library HexStrings { bytes16 internal constant ALPHABET = "0123456789abcdef"; + /// @notice Convert a number to a hex string without the '0x' prefix with a fixed length + /// @param value The number to convert + /// @param length The length of the output string, starting from the last character of the string + /// @return The hex string function toHexStringNoPrefix(uint256 value, uint256 length) internal pure returns (string memory) { bytes memory buffer = new bytes(2 * length); for (uint256 i = buffer.length; i > 0; i--) { diff --git a/src/libraries/NFTSVG.sol b/src/libraries/SVG.sol similarity index 99% rename from src/libraries/NFTSVG.sol rename to src/libraries/SVG.sol index 993cfb6f3..0f30a8303 100644 --- a/src/libraries/NFTSVG.sol +++ b/src/libraries/SVG.sol @@ -7,11 +7,9 @@ import '@openzeppelin/contracts/utils/Strings.sol'; import '@uniswap/v4-core/src/libraries/BitMath.sol'; import '@openzeppelin/contracts/utils/Base64.sol'; -import "forge-std/console2.sol"; - -/// @title NFTSVG +/// @title SVG /// @notice Provides a function for generating an SVG associated with a Uniswap NFT -library NFTSVG { +library SVG { using Strings for uint256; // SVG path commands for the curve that represent the steepness of the position diff --git a/src/libraries/SafeERC20Namer.sol b/src/libraries/SafeERC20Namer.sol index 5660a9d17..81ad361fc 100644 --- a/src/libraries/SafeERC20Namer.sol +++ b/src/libraries/SafeERC20Namer.sol @@ -3,9 +3,13 @@ pragma solidity ^0.8.24; import "./AddressStringUtil.sol"; -// produces token descriptors from inconsistent or absent ERC20 symbol implementations that can return string or bytes32 -// this library will always produce a string symbol to represent the token +/// @title SafeERC20Namer +/// @notice produces token descriptors from inconsistent or absent ERC20 symbol implementations that can return string or bytes32 +/// this library will always produce a string symbol to represent the token library SafeERC20Namer { + /// @notice converts a bytes32 to a string + /// @param x the bytes32 to convert + /// @return the string representation function bytes32ToString(bytes32 x) private pure returns (string memory) { bytes memory bytesString = new bytes(32); uint256 charCount = 0; @@ -23,36 +27,18 @@ library SafeERC20Namer { return string(bytesStringTrimmed); } - // assumes the data is in position 2 - function parseStringData(bytes memory b) private pure returns (string memory) { - uint256 charCount = 0; - // first parse the charCount out of the data - for (uint256 i = 32; i < 64; i++) { - charCount <<= 8; - charCount += uint8(b[i]); - } - - bytes memory bytesStringTrimmed = new bytes(charCount); - for (uint256 i = 0; i < charCount; i++) { - bytesStringTrimmed[i] = b[i + 64]; - } - - return string(bytesStringTrimmed); - } - - // uses a heuristic to produce a token name from the address - // the heuristic returns the full hex of the address string in upper case - function addressToName(address token) private pure returns (string memory) { - return AddressStringUtil.toAsciiString(token, 40); - } - - // uses a heuristic to produce a token symbol from the address + /// @notice uses a heuristic to produce a token symbol from the address // the heuristic returns the first 6 hex of the address string in upper case + /// @param token the token address + /// @return the token symbol function addressToSymbol(address token) private pure returns (string memory) { return AddressStringUtil.toAsciiString(token, 6); } - // calls an external view token contract method that returns a symbol or name, and parses the output into a string + /// @notice calls an external view token contract method that returns a symbol, and parses the output into a string + /// @param token the token address + /// @param selector the selector of the symbol method + /// @return the token symbol function callAndParseStringReturn(address token, bytes4 selector) private view returns (string memory) { (bool success, bytes memory data) = token.staticcall(abi.encodeWithSelector(selector)); // if not implemented, or returns empty data, return empty string @@ -69,7 +55,9 @@ library SafeERC20Namer { return ""; } - // attempts to extract the token symbol. if it does not implement symbol, returns a symbol derived from the address + /// @notice attempts to extract the token symbol. if it does not implement symbol, returns a symbol derived from the address + /// @param token the token address + /// @return the token symbol function tokenSymbol(address token) internal view returns (string memory) { // 0x95d89b41 = bytes4(keccak256("symbol()")) string memory symbol = callAndParseStringReturn(token, 0x95d89b41); @@ -79,15 +67,4 @@ library SafeERC20Namer { } return symbol; } - - // attempts to extract the token name. if it does not implement name, returns a name derived from the address - function tokenName(address token) internal view returns (string memory) { - // 0x06fdde03 = bytes4(keccak256("name()")) - string memory name = callAndParseStringReturn(token, 0x06fdde03); - if (bytes(name).length == 0) { - // fallback to full hex of address - return addressToName(token); - } - return name; - } } diff --git a/test/Descriptor.t.sol b/test/Descriptor.t.sol deleted file mode 100644 index bab1e90b9..000000000 --- a/test/Descriptor.t.sol +++ /dev/null @@ -1,18 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.24; - -import "forge-std/Test.sol"; -import {Deployers} from "@uniswap/v4-core/test/utils/Deployers.sol"; -import {PositionDescriptor} from "../src/PositionDescriptor.sol"; -import {CurrencyRatioSortOrder} from "../src/libraries/CurrencyRatioSortOrder.sol"; -import {IHooks} from "@uniswap/v4-core/src/interfaces/IHooks.sol"; -import {LiquidityAmounts} from "@uniswap/v4-core/test/utils/LiquidityAmounts.sol"; -import {TickMath} from "@uniswap/v4-core/src/libraries/TickMath.sol"; -import {PositionConfig} from "./shared/PositionConfig.sol"; -import {PosmTestSetup} from "./shared/PosmTestSetup.sol"; -import {ActionConstants} from "../src/libraries/ActionConstants.sol"; -import {Base64} from "./base64.sol"; - -contract DescriptorTest is Test, Deployers { - -} \ No newline at end of file diff --git a/test/base64.sol b/test/base64.sol index 444e16d72..301833336 100644 --- a/test/base64.sol +++ b/test/base64.sol @@ -3,67 +3,13 @@ pragma solidity ^0.8.24; /// @title Base64 /// @author Brecht Devos - -/// @notice Provides functions for encoding/decoding base64 +/// @notice Provides functions for decoding base64 library Base64 { - string internal constant TABLE_ENCODE = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; bytes internal constant TABLE_DECODE = hex"0000000000000000000000000000000000000000000000000000000000000000" hex"00000000000000000000003e0000003f3435363738393a3b3c3d000000000000" hex"00000102030405060708090a0b0c0d0e0f101112131415161718190000000000" hex"001a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132330000000000"; - function encode(bytes memory data) internal pure returns (string memory) { - if (data.length == 0) return ''; - - // load the table into memory - string memory table = TABLE_ENCODE; - - // multiply by 4/3 rounded up - uint256 encodedLen = 4 * ((data.length + 2) / 3); - - // add some extra buffer at the end required for the writing - string memory result = new string(encodedLen + 32); - - assembly { - // set the actual output length - mstore(result, encodedLen) - - // prepare the lookup table - let tablePtr := add(table, 1) - - // input ptr - let dataPtr := data - let endPtr := add(dataPtr, mload(data)) - - // result ptr, jump over length - let resultPtr := add(result, 32) - - // run over the input, 3 bytes at a time - for {} lt(dataPtr, endPtr) {} - { - // read 3 bytes - dataPtr := add(dataPtr, 3) - let input := mload(dataPtr) - - // write 4 characters - mstore8(resultPtr, mload(add(tablePtr, and(shr(18, input), 0x3F)))) - resultPtr := add(resultPtr, 1) - mstore8(resultPtr, mload(add(tablePtr, and(shr(12, input), 0x3F)))) - resultPtr := add(resultPtr, 1) - mstore8(resultPtr, mload(add(tablePtr, and(shr( 6, input), 0x3F)))) - resultPtr := add(resultPtr, 1) - mstore8(resultPtr, mload(add(tablePtr, and( input, 0x3F)))) - resultPtr := add(resultPtr, 1) - } - - // padding with '=' - switch mod(mload(data), 3) - case 1 { mstore(sub(resultPtr, 2), shl(240, 0x3d3d)) } - case 2 { mstore(sub(resultPtr, 1), shl(248, 0x3d)) } - } - - return result; - } - function decode(string memory _data) internal pure returns (bytes memory) { bytes memory data = bytes(_data); diff --git a/test/libraries/Descriptor.t.sol b/test/libraries/Descriptor.t.sol index 968eb4adb..a6eb6a388 100644 --- a/test/libraries/Descriptor.t.sol +++ b/test/libraries/Descriptor.t.sol @@ -4,7 +4,6 @@ pragma solidity ^0.8.24; import {Descriptor} from "../../src/libraries/Descriptor.sol"; import {Test} from "forge-std/Test.sol"; import {TickMath} from "@uniswap/v4-core/src/libraries/TickMath.sol"; -import "forge-std/console2.sol"; contract DescriptorTest is Test { function test_feeToPercentString_succeeds() public { @@ -107,5 +106,9 @@ contract DescriptorTest is Test { assertEq(Descriptor.tickToDecimalString(1000, tickSpacing, 18, 10, true), "90484000"); assertEq(Descriptor.tickToDecimalString(1000, tickSpacing, 10, 18, true), "0.0000000090484"); } + + function test_fixedPointToDecimalString_succeeds() public { + + } } diff --git a/test/libraries/SVG.t.sol b/test/libraries/SVG.t.sol new file mode 100644 index 000000000..c322483e2 --- /dev/null +++ b/test/libraries/SVG.t.sol @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +import {SVG} from "../../src/libraries/SVG.sol"; +import {Test} from "forge-std/Test.sol"; + +contract DescriptorTest is Test { + function test_rangeLocation_succeeds() public pure { + (string memory x, string memory y) = SVG.rangeLocation(-887_272, -887_100); + assertEq(x, "8"); + assertEq(y, "7"); + (x, y) = SVG.rangeLocation(-100_000, -90_000); + assertEq(x, "8"); + assertEq(y, "10.5"); + (x, y) = SVG.rangeLocation(-50_000, -20_000); + assertEq(x, "8"); + assertEq(y, "14.25"); + (x, y) = SVG.rangeLocation(-10_000, -5_000); + assertEq(x, "10"); + assertEq(y, "18"); + (x, y) = SVG.rangeLocation(-5_000, -4_000); + assertEq(x, "11"); + assertEq(y, "21"); + (x, y) = SVG.rangeLocation(4_000, 5_000); + assertEq(x, "13"); + assertEq(y, "23"); + (x, y) = SVG.rangeLocation(10_000, 15_000); + assertEq(x, "15"); + assertEq(y, "25"); + (x, y) = SVG.rangeLocation(25_000, 50_000); + assertEq(x, "18"); + assertEq(y, "26"); + (x, y) = SVG.rangeLocation(100_000, 125_000); + assertEq(x, "21"); + assertEq(y, "27"); + (x, y) = SVG.rangeLocation(200_000, 100_000); + assertEq(x, "24"); + assertEq(y, "27"); + (x, y) = SVG.rangeLocation(887_272, 887_272); + assertEq(x, "24"); + assertEq(y, "27"); + } + + function test_isRare_succeeds() public pure { + bool result = SVG.isRare(1, 0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB); + assertTrue(result); + result = SVG.isRare(2, 0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB); + assertFalse(result); + } +} From 7d69f538e6bf927c63686d4e1efc62e58f7b971e Mon Sep 17 00:00:00 2001 From: dianakocsis Date: Wed, 18 Sep 2024 18:14:03 -0400 Subject: [PATCH 23/45] format --- ...utSingle_oneForZero_multiplePositions.snap | 2 +- ...utSingle_zeroForOne_multiplePositions.snap | 2 +- .../Quoter_exactOutputSingle_oneForZero.snap | 2 +- .../Quoter_exactOutputSingle_zeroForOne.snap | 2 +- ...er_quoteExactInput_oneHop_1TickLoaded.snap | 2 +- ...oteExactInput_oneHop_initializedAfter.snap | 2 +- ...ExactInput_oneHop_startingInitialized.snap | 2 +- .../Quoter_quoteExactInput_twoHops.snap | 2 +- ...r_quoteExactOutput_oneHop_1TickLoaded.snap | 2 +- ..._quoteExactOutput_oneHop_2TicksLoaded.snap | 2 +- ...teExactOutput_oneHop_initializedAfter.snap | 2 +- ...xactOutput_oneHop_startingInitialized.snap | 2 +- .../Quoter_quoteExactOutput_twoHops.snap | 2 +- script/DeployPosm.s.sol | 5 +- src/PositionDescriptor.sol | 10 +- src/PositionManager.sol | 7 +- src/libraries/Descriptor.sol | 72 ++++----- src/libraries/SVG.sol | 152 ++++++++---------- test/PositionDescriptor.t.sol | 2 +- test/base64.sol | 43 +++-- test/libraries/Descriptor.t.sol | 20 ++- 21 files changed, 166 insertions(+), 171 deletions(-) diff --git a/.forge-snapshots/Quoter_exactInputSingle_oneForZero_multiplePositions.snap b/.forge-snapshots/Quoter_exactInputSingle_oneForZero_multiplePositions.snap index 0c97c3620..364988846 100644 --- a/.forge-snapshots/Quoter_exactInputSingle_oneForZero_multiplePositions.snap +++ b/.forge-snapshots/Quoter_exactInputSingle_oneForZero_multiplePositions.snap @@ -1 +1 @@ -159043 \ No newline at end of file +158920 \ No newline at end of file diff --git a/.forge-snapshots/Quoter_exactInputSingle_zeroForOne_multiplePositions.snap b/.forge-snapshots/Quoter_exactInputSingle_zeroForOne_multiplePositions.snap index af5fa0fa6..27bfbb33b 100644 --- a/.forge-snapshots/Quoter_exactInputSingle_zeroForOne_multiplePositions.snap +++ b/.forge-snapshots/Quoter_exactInputSingle_zeroForOne_multiplePositions.snap @@ -1 +1 @@ -166396 \ No newline at end of file +166238 \ No newline at end of file diff --git a/.forge-snapshots/Quoter_exactOutputSingle_oneForZero.snap b/.forge-snapshots/Quoter_exactOutputSingle_oneForZero.snap index f43e94582..68bc18bbb 100644 --- a/.forge-snapshots/Quoter_exactOutputSingle_oneForZero.snap +++ b/.forge-snapshots/Quoter_exactOutputSingle_oneForZero.snap @@ -1 +1 @@ -93637 \ No newline at end of file +93496 \ No newline at end of file diff --git a/.forge-snapshots/Quoter_exactOutputSingle_zeroForOne.snap b/.forge-snapshots/Quoter_exactOutputSingle_zeroForOne.snap index 66a68794e..3d41a8397 100644 --- a/.forge-snapshots/Quoter_exactOutputSingle_zeroForOne.snap +++ b/.forge-snapshots/Quoter_exactOutputSingle_zeroForOne.snap @@ -1 +1 @@ -100303 \ No newline at end of file +100134 \ No newline at end of file diff --git a/.forge-snapshots/Quoter_quoteExactInput_oneHop_1TickLoaded.snap b/.forge-snapshots/Quoter_quoteExactInput_oneHop_1TickLoaded.snap index 87d96db17..77c1dc536 100644 --- a/.forge-snapshots/Quoter_quoteExactInput_oneHop_1TickLoaded.snap +++ b/.forge-snapshots/Quoter_quoteExactInput_oneHop_1TickLoaded.snap @@ -1 +1 @@ -141321 \ No newline at end of file +141172 \ No newline at end of file diff --git a/.forge-snapshots/Quoter_quoteExactInput_oneHop_initializedAfter.snap b/.forge-snapshots/Quoter_quoteExactInput_oneHop_initializedAfter.snap index 3315258ee..18741a08a 100644 --- a/.forge-snapshots/Quoter_quoteExactInput_oneHop_initializedAfter.snap +++ b/.forge-snapshots/Quoter_quoteExactInput_oneHop_initializedAfter.snap @@ -1 +1 @@ -164528 \ No newline at end of file +164405 \ No newline at end of file diff --git a/.forge-snapshots/Quoter_quoteExactInput_oneHop_startingInitialized.snap b/.forge-snapshots/Quoter_quoteExactInput_oneHop_startingInitialized.snap index 954792b66..db368a295 100644 --- a/.forge-snapshots/Quoter_quoteExactInput_oneHop_startingInitialized.snap +++ b/.forge-snapshots/Quoter_quoteExactInput_oneHop_startingInitialized.snap @@ -1 +1 @@ -98641 \ No newline at end of file +98506 \ No newline at end of file diff --git a/.forge-snapshots/Quoter_quoteExactInput_twoHops.snap b/.forge-snapshots/Quoter_quoteExactInput_twoHops.snap index b8ed8de2f..aebbedf7f 100644 --- a/.forge-snapshots/Quoter_quoteExactInput_twoHops.snap +++ b/.forge-snapshots/Quoter_quoteExactInput_twoHops.snap @@ -1 +1 @@ -234806 \ No newline at end of file +234516 \ No newline at end of file diff --git a/.forge-snapshots/Quoter_quoteExactOutput_oneHop_1TickLoaded.snap b/.forge-snapshots/Quoter_quoteExactOutput_oneHop_1TickLoaded.snap index 8a6db3df9..66ccea74d 100644 --- a/.forge-snapshots/Quoter_quoteExactOutput_oneHop_1TickLoaded.snap +++ b/.forge-snapshots/Quoter_quoteExactOutput_oneHop_1TickLoaded.snap @@ -1 +1 @@ -161346 \ No newline at end of file +161139 \ No newline at end of file diff --git a/.forge-snapshots/Quoter_quoteExactOutput_oneHop_2TicksLoaded.snap b/.forge-snapshots/Quoter_quoteExactOutput_oneHop_2TicksLoaded.snap index 7391d6be7..2a85658e8 100644 --- a/.forge-snapshots/Quoter_quoteExactOutput_oneHop_2TicksLoaded.snap +++ b/.forge-snapshots/Quoter_quoteExactOutput_oneHop_2TicksLoaded.snap @@ -1 +1 @@ -191453 \ No newline at end of file +191214 \ No newline at end of file diff --git a/.forge-snapshots/Quoter_quoteExactOutput_oneHop_initializedAfter.snap b/.forge-snapshots/Quoter_quoteExactOutput_oneHop_initializedAfter.snap index cc4af5c55..05214c6ac 100644 --- a/.forge-snapshots/Quoter_quoteExactOutput_oneHop_initializedAfter.snap +++ b/.forge-snapshots/Quoter_quoteExactOutput_oneHop_initializedAfter.snap @@ -1 +1 @@ -161661 \ No newline at end of file +161454 \ No newline at end of file diff --git a/.forge-snapshots/Quoter_quoteExactOutput_oneHop_startingInitialized.snap b/.forge-snapshots/Quoter_quoteExactOutput_oneHop_startingInitialized.snap index 5aed9cf81..dbf22d2b1 100644 --- a/.forge-snapshots/Quoter_quoteExactOutput_oneHop_startingInitialized.snap +++ b/.forge-snapshots/Quoter_quoteExactOutput_oneHop_startingInitialized.snap @@ -1 +1 @@ -136430 \ No newline at end of file +136248 \ No newline at end of file diff --git a/.forge-snapshots/Quoter_quoteExactOutput_twoHops.snap b/.forge-snapshots/Quoter_quoteExactOutput_twoHops.snap index da4bedcf8..68edf156c 100644 --- a/.forge-snapshots/Quoter_quoteExactOutput_twoHops.snap +++ b/.forge-snapshots/Quoter_quoteExactOutput_twoHops.snap @@ -1 +1 @@ -275720 \ No newline at end of file +275337 \ No newline at end of file diff --git a/script/DeployPosm.s.sol b/script/DeployPosm.s.sol index c2e35f5b9..e3f15bf44 100644 --- a/script/DeployPosm.s.sol +++ b/script/DeployPosm.s.sol @@ -20,7 +20,10 @@ contract DeployPosmTest is Script { vm.startBroadcast(); posm = new PositionManager{salt: hex"03"}( - IPoolManager(poolManager), IAllowanceTransfer(permit2), unsubscribeGasLimit, IPositionDescriptor(positionDescriptor) + IPoolManager(poolManager), + IAllowanceTransfer(permit2), + unsubscribeGasLimit, + IPositionDescriptor(positionDescriptor) ); console2.log("PositionManager", address(posm)); diff --git a/src/PositionDescriptor.sol b/src/PositionDescriptor.sol index 9eb667990..3b3467b48 100644 --- a/src/PositionDescriptor.sol +++ b/src/PositionDescriptor.sol @@ -59,8 +59,12 @@ contract PositionDescriptor is IPositionDescriptor { } /// @inheritdoc IPositionDescriptor - function tokenURI(IPositionManager positionManager, uint256 tokenId) external view override returns (string memory) { - + function tokenURI(IPositionManager positionManager, uint256 tokenId) + external + view + override + returns (string memory) + { (PoolKey memory poolKey, PositionInfo positionInfo) = positionManager.getPoolAndPositionInfo(tokenId); (, int24 tick,,) = poolManager.getSlot0(poolKey.toId()); @@ -109,7 +113,7 @@ contract PositionDescriptor is IPositionDescriptor { return currencyRatioPriority(currency0) > currencyRatioPriority(currency1); } - /// @notice Returns the priority of a currency. + /// @notice Returns the priority of a currency. /// For certain currencies on mainnet, the smaller the currency, the higher the priority /// @param currency The currency /// @return priority The priority of the currency diff --git a/src/PositionManager.sol b/src/PositionManager.sol index a5f781067..de5a2f76d 100644 --- a/src/PositionManager.sol +++ b/src/PositionManager.sol @@ -122,7 +122,12 @@ contract PositionManager is mapping(uint256 tokenId => PositionInfo info) public positionInfo; mapping(bytes25 poolId => PoolKey poolKey) public poolKeys; - constructor(IPoolManager _poolManager, IAllowanceTransfer _permit2, uint256 _unsubscribeGasLimit, IPositionDescriptor _tokenDescriptor) + constructor( + IPoolManager _poolManager, + IAllowanceTransfer _permit2, + uint256 _unsubscribeGasLimit, + IPositionDescriptor _tokenDescriptor + ) BaseActionsRouter(_poolManager) Permit2Forwarder(_permit2) ERC721Permit_v4("Uniswap V4 Positions NFT", "UNI-V4-POSM") diff --git a/src/libraries/Descriptor.sol b/src/libraries/Descriptor.sol index 9298a56b6..54729d88a 100644 --- a/src/libraries/Descriptor.sol +++ b/src/libraries/Descriptor.sol @@ -228,7 +228,6 @@ library Descriptor { bool isPercent; } - function generateDecimalString(DecimalStringParams memory params) private pure returns (string memory) { bytes memory buffer = new bytes(params.bufferLength); if (params.isPercent) { @@ -467,30 +466,29 @@ library Descriptor { /// @param params Parameters needed to generate the SVG image /// @return svg The SVG image as a string function generateSVGImage(ConstructTokenURIParams memory params) internal pure returns (string memory svg) { - SVG.SVGParams memory svgParams = - SVG.SVGParams({ - quoteCurrency: addressToString(Currency.unwrap(params.quoteCurrency)), - baseCurrency: addressToString(Currency.unwrap(params.baseCurrency)), - hooks: params.hooks, - quoteCurrencySymbol: params.quoteCurrencySymbol, - baseCurrencySymbol: params.baseCurrencySymbol, - feeTier: feeToPercentString(params.fee), - tickLower: params.tickLower, - tickUpper: params.tickUpper, - tickSpacing: params.tickSpacing, - overRange: overRange(params.tickLower, params.tickUpper, params.tickCurrent), - tokenId: params.tokenId, - color0: currencyToColorHex(params.quoteCurrency.toId(), 136), - color1: currencyToColorHex(params.baseCurrency.toId(), 136), - color2: currencyToColorHex(params.quoteCurrency.toId(), 0), - color3: currencyToColorHex(params.baseCurrency.toId(), 0), - x1: scale(getCircleCoord(params.quoteCurrency.toId(), 16, params.tokenId), 0, 255, 16, 274), - y1: scale(getCircleCoord(params.baseCurrency.toId(), 16, params.tokenId), 0, 255, 100, 484), - x2: scale(getCircleCoord(params.quoteCurrency.toId(), 32, params.tokenId), 0, 255, 16, 274), - y2: scale(getCircleCoord(params.baseCurrency.toId(), 32, params.tokenId), 0, 255, 100, 484), - x3: scale(getCircleCoord(params.quoteCurrency.toId(), 48, params.tokenId), 0, 255, 16, 274), - y3: scale(getCircleCoord(params.baseCurrency.toId(), 48, params.tokenId), 0, 255, 100, 484) - }); + SVG.SVGParams memory svgParams = SVG.SVGParams({ + quoteCurrency: addressToString(Currency.unwrap(params.quoteCurrency)), + baseCurrency: addressToString(Currency.unwrap(params.baseCurrency)), + hooks: params.hooks, + quoteCurrencySymbol: params.quoteCurrencySymbol, + baseCurrencySymbol: params.baseCurrencySymbol, + feeTier: feeToPercentString(params.fee), + tickLower: params.tickLower, + tickUpper: params.tickUpper, + tickSpacing: params.tickSpacing, + overRange: overRange(params.tickLower, params.tickUpper, params.tickCurrent), + tokenId: params.tokenId, + color0: currencyToColorHex(params.quoteCurrency.toId(), 136), + color1: currencyToColorHex(params.baseCurrency.toId(), 136), + color2: currencyToColorHex(params.quoteCurrency.toId(), 0), + color3: currencyToColorHex(params.baseCurrency.toId(), 0), + x1: scale(getCircleCoord(params.quoteCurrency.toId(), 16, params.tokenId), 0, 255, 16, 274), + y1: scale(getCircleCoord(params.baseCurrency.toId(), 16, params.tokenId), 0, 255, 100, 484), + x2: scale(getCircleCoord(params.quoteCurrency.toId(), 32, params.tokenId), 0, 255, 16, 274), + y2: scale(getCircleCoord(params.baseCurrency.toId(), 32, params.tokenId), 0, 255, 100, 484), + x3: scale(getCircleCoord(params.quoteCurrency.toId(), 48, params.tokenId), 0, 255, 16, 274), + y3: scale(getCircleCoord(params.baseCurrency.toId(), 48, params.tokenId), 0, 255, 100, 484) + }); return SVG.generateSVG(svgParams); } @@ -500,11 +498,7 @@ library Descriptor { /// @param tickUpper The upper tick /// @param tickCurrent The current tick /// @return 0 if current tick is within range, -1 if below, 1 if above - function overRange( - int24 tickLower, - int24 tickUpper, - int24 tickCurrent - ) private pure returns (int8) { + function overRange(int24 tickLower, int24 tickUpper, int24 tickCurrent) private pure returns (int8) { if (tickCurrent < tickLower) { return -1; } else if (tickCurrent > tickUpper) { @@ -521,13 +515,11 @@ library Descriptor { /// @param outMn The minimum of the output range /// @param outMx The maximum of the output range /// @return The scaled number as a string - function scale( - uint256 n, - uint256 inMn, - uint256 inMx, - uint256 outMn, - uint256 outMx - ) private pure returns (string memory) { + function scale(uint256 n, uint256 inMn, uint256 inMx, uint256 outMn, uint256 outMx) + private + pure + returns (string memory) + { return (n - inMn * (outMx - outMn) / (inMx - inMn) + outMn).toString(); } @@ -544,11 +536,7 @@ library Descriptor { /// @param offset The offset to slice the token hex /// @param tokenId The token ID /// @return The coordinate - function getCircleCoord( - uint256 currency, - uint256 offset, - uint256 tokenId - ) internal pure returns (uint256) { + function getCircleCoord(uint256 currency, uint256 offset, uint256 tokenId) internal pure returns (uint256) { return (sliceCurrencyHex(currency, offset) * tokenId) % 255; } diff --git a/src/libraries/SVG.sol b/src/libraries/SVG.sol index 0f30a8303..bb083b33b 100644 --- a/src/libraries/SVG.sol +++ b/src/libraries/SVG.sol @@ -3,9 +3,9 @@ pragma solidity ^0.8.24; import {Currency, CurrencyLibrary} from "@uniswap/v4-core/src/types/Currency.sol"; import {IHooks} from "@uniswap/v4-core/src/interfaces/IHooks.sol"; -import '@openzeppelin/contracts/utils/Strings.sol'; -import '@uniswap/v4-core/src/libraries/BitMath.sol'; -import '@openzeppelin/contracts/utils/Base64.sol'; +import "@openzeppelin/contracts/utils/Strings.sol"; +import "@uniswap/v4-core/src/libraries/BitMath.sol"; +import "@openzeppelin/contracts/utils/Base64.sol"; /// @title SVG /// @notice Provides a function for generating an SVG associated with a Uniswap NFT @@ -15,14 +15,14 @@ library SVG { // SVG path commands for the curve that represent the steepness of the position // defined using the Cubic Bezier Curve syntax // curve1 is the smallest (linear) curve, curve8 is the largest curve - string constant curve1 = 'M1 1C41 41 105 105 145 145'; - string constant curve2 = 'M1 1C33 49 97 113 145 145'; - string constant curve3 = 'M1 1C33 57 89 113 145 145'; - string constant curve4 = 'M1 1C25 65 81 121 145 145'; - string constant curve5 = 'M1 1C17 73 73 129 145 145'; - string constant curve6 = 'M1 1C9 81 65 137 145 145'; - string constant curve7 = 'M1 1C1 89 57.5 145 145 145'; - string constant curve8 = 'M1 1C1 97 49 145 145 145'; + string constant curve1 = "M1 1C41 41 105 105 145 145"; + string constant curve2 = "M1 1C33 49 97 113 145 145"; + string constant curve3 = "M1 1C33 57 89 113 145 145"; + string constant curve4 = "M1 1C25 65 81 121 145 145"; + string constant curve5 = "M1 1C17 73 73 129 145 145"; + string constant curve6 = "M1 1C9 81 65 137 145 145"; + string constant curve7 = "M1 1C1 89 57.5 145 145 145"; + string constant curve8 = "M1 1C1 97 49 145 145 145"; struct SVGParams { string quoteCurrency; @@ -58,28 +58,21 @@ library SVG { sig: "0x2df0e99d9cbfec33a705d83f75666d98b22dea7c1af412c584f7d626d83f02875993df740dc87563b9c73378f8462426da572d7989de88079a382ad96c57b68d1b", version: "2" */ - return - string( - abi.encodePacked( - generateSVGDefs(params), - generateSVGBorderText( - params.quoteCurrency, - params.baseCurrency, - params.quoteCurrencySymbol, - params.baseCurrencySymbol - ), - generateSVGCardMantle(params.quoteCurrencySymbol, params.baseCurrencySymbol, params.feeTier), - generageSvgCurve(params.tickLower, params.tickUpper, params.tickSpacing, params.overRange), - generateSVGPositionDataAndLocationCurve( - params.tokenId.toString(), - params.hooks, - params.tickLower, - params.tickUpper - ), - generateSVGRareSparkle(params.tokenId, params.hooks), - '' - ) - ); + return string( + abi.encodePacked( + generateSVGDefs(params), + generateSVGBorderText( + params.quoteCurrency, params.baseCurrency, params.quoteCurrencySymbol, params.baseCurrencySymbol + ), + generateSVGCardMantle(params.quoteCurrencySymbol, params.baseCurrencySymbol, params.feeTier), + generageSvgCurve(params.tickLower, params.tickUpper, params.tickSpacing, params.overRange), + generateSVGPositionDataAndLocationCurve( + params.tokenId.toString(), params.hooks, params.tickLower, params.tickUpper + ), + generateSVGRareSparkle(params.tokenId, params.hooks), + "" + ) + ); } /// @notice Generate the SVG defs that create the color scheme for the SVG @@ -90,7 +83,7 @@ library SVG { abi.encodePacked( '", - '', + "", '', '', baseCurrency, - unicode' • ', + unicode" • ", baseCurrencySymbol, ' ', ' ', baseCurrency, - unicode' • ', + unicode" • ", baseCurrencySymbol, ' ', '', quoteCurrency, - unicode' • ', + unicode" • ", quoteCurrencySymbol, ' ', quoteCurrency, - unicode' • ', + unicode" • ", quoteCurrencySymbol, ' ' ) @@ -223,11 +216,11 @@ library SVG { abi.encodePacked( ' ', quoteCurrencySymbol, - '/', + "/", baseCurrencySymbol, '', feeTier, - '', + "", '' ) ); @@ -240,13 +233,12 @@ library SVG { /// @param tickSpacing The tick spacing /// @param overRange Whether the current tick is in range, over range, or under range /// @return svg The SVG for the curve - function generageSvgCurve( - int24 tickLower, - int24 tickUpper, - int24 tickSpacing, - int8 overRange - ) private pure returns (string memory svg) { - string memory fade = overRange == 1 ? '#fade-up' : overRange == -1 ? '#fade-down' : '#none'; + function generageSvgCurve(int24 tickLower, int24 tickUpper, int24 tickSpacing, int8 overRange) + private + pure + returns (string memory svg) + { + string memory fade = overRange == 1 ? "#fade-up" : overRange == -1 ? "#fade-down" : "#none"; string memory curve = getCurve(tickLower, tickUpper, tickSpacing); svg = string( abi.encodePacked( @@ -254,8 +246,7 @@ library SVG { fade, ')"', ' style="transform:translate(72px,189px)">' - '' - '' '', '', 'ID: ', tokenId, - '', + "", ' ', '', 'Hook: ', hookSlice, - '', + "", ' ', '', 'Min Tick: ', tickLowerStr, - '', + "", ' ', '', 'Max Tick: ', tickUpperStr, - '' - '', + "" '', '', '', '' ) @@ -412,10 +402,10 @@ library SVG { /// @param startIndex The start index /// @param endIndex The end index /// @return The substring - function substring(string memory str, uint startIndex, uint endIndex) internal pure returns (string memory) { + function substring(string memory str, uint256 startIndex, uint256 endIndex) internal pure returns (string memory) { bytes memory strBytes = bytes(str); bytes memory result = new bytes(endIndex - startIndex); - for(uint i = startIndex; i < endIndex; i++) { + for (uint256 i = startIndex; i < endIndex; i++) { result[i - startIndex] = strBytes[i]; } return string(result); @@ -425,10 +415,10 @@ library SVG { /// @param tick The tick to convert /// @return The tick as a string function tickToString(int24 tick) private pure returns (string memory) { - string memory sign = ''; + string memory sign = ""; if (tick < 0) { tick = tick * -1; - sign = '-'; + sign = "-"; } return string(abi.encodePacked(sign, uint256(uint24(tick)).toString())); } @@ -440,25 +430,25 @@ library SVG { function rangeLocation(int24 tickLower, int24 tickUpper) internal pure returns (string memory, string memory) { int24 midPoint = (tickLower + tickUpper) / 2; if (midPoint < -125_000) { - return ('8', '7'); + return ("8", "7"); } else if (midPoint < -75_000) { - return ('8', '10.5'); + return ("8", "10.5"); } else if (midPoint < -25_000) { - return ('8', '14.25'); + return ("8", "14.25"); } else if (midPoint < -5_000) { - return ('10', '18'); + return ("10", "18"); } else if (midPoint < 0) { - return ('11', '21'); + return ("11", "21"); } else if (midPoint < 5_000) { - return ('13', '23'); + return ("13", "23"); } else if (midPoint < 25_000) { - return ('15', '25'); + return ("15", "25"); } else if (midPoint < 75_000) { - return ('18', '26'); + return ("18", "26"); } else if (midPoint < 125_000) { - return ('21', '27'); + return ("21", "27"); } else { - return ('24', '27'); + return ("24", "27"); } } @@ -472,13 +462,13 @@ library SVG { abi.encodePacked( '', '', '' ) ); } else { - svg = ''; + svg = ""; } } diff --git a/test/PositionDescriptor.t.sol b/test/PositionDescriptor.t.sol index c650d1726..81c7791d3 100644 --- a/test/PositionDescriptor.t.sol +++ b/test/PositionDescriptor.t.sol @@ -95,7 +95,7 @@ contract PositionDescriptorTest is Test, PosmTestSetup { mint(config, liquidityToAdd, ActionConstants.MSG_SENDER, ZERO_BYTES); // The prefix length is calculated by converting the string to bytes and finding its length - uint256 prefixLength = bytes('data:application/json;base64,').length; + uint256 prefixLength = bytes("data:application/json;base64,").length; string memory uri = positionDescriptor.tokenURI(lpm, tokenId); // Convert the uri to bytes diff --git a/test/base64.sol b/test/base64.sol index 301833336..811a8f09b 100644 --- a/test/base64.sol +++ b/test/base64.sol @@ -5,10 +5,10 @@ pragma solidity ^0.8.24; /// @author Brecht Devos - /// @notice Provides functions for decoding base64 library Base64 { - bytes internal constant TABLE_DECODE = hex"0000000000000000000000000000000000000000000000000000000000000000" - hex"00000000000000000000003e0000003f3435363738393a3b3c3d000000000000" - hex"00000102030405060708090a0b0c0d0e0f101112131415161718190000000000" - hex"001a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132330000000000"; + bytes internal constant TABLE_DECODE = hex"0000000000000000000000000000000000000000000000000000000000000000" + hex"00000000000000000000003e0000003f3435363738393a3b3c3d000000000000" + hex"00000102030405060708090a0b0c0d0e0f101112131415161718190000000000" + hex"001a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132330000000000"; function decode(string memory _data) internal pure returns (bytes memory) { bytes memory data = bytes(_data); @@ -30,9 +30,7 @@ library Base64 { let lastBytes := mload(add(data, mload(data))) if eq(and(lastBytes, 0xFF), 0x3d) { decodedLen := sub(decodedLen, 1) - if eq(and(lastBytes, 0xFFFF), 0x3d3d) { - decodedLen := sub(decodedLen, 1) - } + if eq(and(lastBytes, 0xFFFF), 0x3d3d) { decodedLen := sub(decodedLen, 1) } } // set the actual output length @@ -49,22 +47,23 @@ library Base64 { let resultPtr := add(result, 32) // run over the input, 4 characters at a time - for {} lt(dataPtr, endPtr) {} - { - // read 4 characters - dataPtr := add(dataPtr, 4) - let input := mload(dataPtr) + for {} lt(dataPtr, endPtr) {} { + // read 4 characters + dataPtr := add(dataPtr, 4) + let input := mload(dataPtr) - // write 3 bytes - let output := add( - add( - shl(18, and(mload(add(tablePtr, and(shr(24, input), 0xFF))), 0xFF)), - shl(12, and(mload(add(tablePtr, and(shr(16, input), 0xFF))), 0xFF))), - add( - shl( 6, and(mload(add(tablePtr, and(shr( 8, input), 0xFF))), 0xFF)), - and(mload(add(tablePtr, and( input , 0xFF))), 0xFF) + // write 3 bytes + let output := + add( + add( + shl(18, and(mload(add(tablePtr, and(shr(24, input), 0xFF))), 0xFF)), + shl(12, and(mload(add(tablePtr, and(shr(16, input), 0xFF))), 0xFF)) + ), + add( + shl(6, and(mload(add(tablePtr, and(shr(8, input), 0xFF))), 0xFF)), + and(mload(add(tablePtr, and(input, 0xFF))), 0xFF) + ) ) - ) mstore(resultPtr, shl(232, output)) resultPtr := add(resultPtr, 3) } @@ -72,4 +71,4 @@ library Base64 { return result; } -} \ No newline at end of file +} diff --git a/test/libraries/Descriptor.t.sol b/test/libraries/Descriptor.t.sol index a6eb6a388..e3e45a634 100644 --- a/test/libraries/Descriptor.t.sol +++ b/test/libraries/Descriptor.t.sol @@ -57,7 +57,10 @@ contract DescriptorTest is Test { assertEq(Descriptor.tickToDecimalString(maxTick, tickSpacing, 18, 18, false), "MAX"); assertEq(Descriptor.tickToDecimalString(1, tickSpacing, 18, 18, false), "1.0001"); int24 otherMinTick = (TickMath.MIN_TICK / 60) * 60; - assertEq(Descriptor.tickToDecimalString(otherMinTick, tickSpacing, 18, 18, false), "0.0000000000000000000000000000000000000029387"); + assertEq( + Descriptor.tickToDecimalString(otherMinTick, tickSpacing, 18, 18, false), + "0.0000000000000000000000000000000000000029387" + ); } function test_tickToDecimalString_withTickSpacing60() public { @@ -68,7 +71,10 @@ contract DescriptorTest is Test { assertEq(Descriptor.tickToDecimalString(maxTick, tickSpacing, 18, 18, false), "MAX"); assertEq(Descriptor.tickToDecimalString(-1, tickSpacing, 18, 18, false), "0.99990"); int24 otherMinTick = (TickMath.MIN_TICK / 200) * 200; - assertEq(Descriptor.tickToDecimalString(otherMinTick, tickSpacing, 18, 18, false), "0.0000000000000000000000000000000000000029387"); + assertEq( + Descriptor.tickToDecimalString(otherMinTick, tickSpacing, 18, 18, false), + "0.0000000000000000000000000000000000000029387" + ); } function test_tickToDecimalString_withTickSpacing200() public { @@ -79,7 +85,10 @@ contract DescriptorTest is Test { assertEq(Descriptor.tickToDecimalString(maxTick, tickSpacing, 18, 18, false), "MAX"); assertEq(Descriptor.tickToDecimalString(0, tickSpacing, 18, 18, false), "1.0000"); int24 otherMinTick = (TickMath.MIN_TICK / 60) * 60; - assertEq(Descriptor.tickToDecimalString(otherMinTick, tickSpacing, 18, 18, false), "0.0000000000000000000000000000000000000029387"); + assertEq( + Descriptor.tickToDecimalString(otherMinTick, tickSpacing, 18, 18, false), + "0.0000000000000000000000000000000000000029387" + ); } function test_tickToDecimalString_ratio_returnsInverseMediumNumbers() public { @@ -107,8 +116,5 @@ contract DescriptorTest is Test { assertEq(Descriptor.tickToDecimalString(1000, tickSpacing, 10, 18, true), "0.0000000090484"); } - function test_fixedPointToDecimalString_succeeds() public { - - } - + function test_fixedPointToDecimalString_succeeds() public {} } From 0ba4e38917075a90378b421bb143e86c76630295 Mon Sep 17 00:00:00 2001 From: dianakocsis Date: Wed, 18 Sep 2024 18:17:04 -0400 Subject: [PATCH 24/45] fix comment --- src/PositionDescriptor.sol | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/PositionDescriptor.sol b/src/PositionDescriptor.sol index 3b3467b48..1deb5426d 100644 --- a/src/PositionDescriptor.sol +++ b/src/PositionDescriptor.sol @@ -45,7 +45,8 @@ contract PositionDescriptor is IPositionDescriptor { nativeCurrencyLabelBytes = _nativeCurrencyLabel; } - /// @notice Returns the native currency label as a string + /// @notice Takes the native currency label in bytes32 and returns it as a string + /// @return nativeCurrencyLabel The native currency label function nativeCurrencyLabel() public view returns (string memory) { uint256 len = 0; while (len < 32 && nativeCurrencyLabelBytes[len] != 0) { From 2b518bf1a44ff82180b9adda57fbcc46ba059e05 Mon Sep 17 00:00:00 2001 From: dianakocsis Date: Thu, 19 Sep 2024 11:26:22 -0400 Subject: [PATCH 25/45] warnings --- src/libraries/Descriptor.sol | 30 ------------------------------ src/libraries/HexStrings.sol | 2 ++ src/libraries/SafeERC20Namer.sol | 6 +----- test/PositionDescriptor.t.sol | 1 - test/libraries/Descriptor.t.sol | 20 ++++++++++---------- test/mocks/MockERC721Permit.sol | 2 +- 6 files changed, 14 insertions(+), 47 deletions(-) diff --git a/src/libraries/Descriptor.sol b/src/libraries/Descriptor.sol index 54729d88a..cb546f22c 100644 --- a/src/libraries/Descriptor.sol +++ b/src/libraries/Descriptor.sol @@ -80,8 +80,6 @@ library Descriptor { } /// @notice Escapes double quotes in a string if they are present - /// @param symbol The string to escape - /// @return The string with double quotes escaped function escapeQuotes(string memory symbol) internal pure returns (string memory) { bytes memory symbolBytes = bytes(symbol); uint8 quotesCount = 0; @@ -339,11 +337,6 @@ library Descriptor { return uint256(x >= 0 ? x : -x); } - /// @notice Returns string that includes first 5 significant figures of a decimal number - /// @param sqrtRatioX96 a sqrt price - /// @param baseCurrencyDecimals The decimals of the base currency - /// @param quoteCurrencyDecimals The decimals of the quote currency - /// @return The decimal string function fixedPointToDecimalString(uint160 sqrtRatioX96, uint8 baseCurrencyDecimals, uint8 quoteCurrencyDecimals) internal pure @@ -455,9 +448,6 @@ library Descriptor { return generateDecimalString(params); } - /// @notice Converts an address to a string - /// @param addr The address to convert - /// @return The address as a string function addressToString(address addr) internal pure returns (string memory) { return (uint256(uint160(addr))).toHexString(20); } @@ -508,13 +498,6 @@ library Descriptor { } } - /// @notice Scales a number from one range to another - /// @param n The number to scale - /// @param inMn The minimum of the input range - /// @param inMx The maximum of the input range - /// @param outMn The minimum of the output range - /// @param outMx The maximum of the output range - /// @return The scaled number as a string function scale(uint256 n, uint256 inMn, uint256 inMx, uint256 outMn, uint256 outMx) private pure @@ -523,27 +506,14 @@ library Descriptor { return (n - inMn * (outMx - outMn) / (inMx - inMn) + outMn).toString(); } - /// @notice Converts a currency to a color in hex to be used in the SVG - /// @param currency The currency - /// @param offset The offset to slice the token hex - /// @return str The color hex as a string function currencyToColorHex(uint256 currency, uint256 offset) internal pure returns (string memory str) { return string((currency >> offset).toHexStringNoPrefix(3)); } - /// @notice Gets the coordinate for a circle - /// @param currency The currency ID - /// @param offset The offset to slice the token hex - /// @param tokenId The token ID - /// @return The coordinate function getCircleCoord(uint256 currency, uint256 offset, uint256 tokenId) internal pure returns (uint256) { return (sliceCurrencyHex(currency, offset) * tokenId) % 255; } - /// @notice Slices the currency ID hex - /// @param currency The currency ID - /// @param offset The offset to slice the token hex - /// @return The sliced hex function sliceCurrencyHex(uint256 currency, uint256 offset) internal pure returns (uint256) { return uint256(uint8(currency >> offset)); } diff --git a/src/libraries/HexStrings.sol b/src/libraries/HexStrings.sol index aa2f80c0a..8d662eaf4 100644 --- a/src/libraries/HexStrings.sol +++ b/src/libraries/HexStrings.sol @@ -1,6 +1,8 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.24; +/// @title HexStrings +/// @notice Provides function for converting numbers to hexadecimal strings library HexStrings { bytes16 internal constant ALPHABET = "0123456789abcdef"; diff --git a/src/libraries/SafeERC20Namer.sol b/src/libraries/SafeERC20Namer.sol index 81ad361fc..801c4d42b 100644 --- a/src/libraries/SafeERC20Namer.sol +++ b/src/libraries/SafeERC20Namer.sol @@ -7,9 +7,6 @@ import "./AddressStringUtil.sol"; /// @notice produces token descriptors from inconsistent or absent ERC20 symbol implementations that can return string or bytes32 /// this library will always produce a string symbol to represent the token library SafeERC20Namer { - /// @notice converts a bytes32 to a string - /// @param x the bytes32 to convert - /// @return the string representation function bytes32ToString(bytes32 x) private pure returns (string memory) { bytes memory bytesString = new bytes(32); uint256 charCount = 0; @@ -27,8 +24,7 @@ library SafeERC20Namer { return string(bytesStringTrimmed); } - /// @notice uses a heuristic to produce a token symbol from the address - // the heuristic returns the first 6 hex of the address string in upper case + /// @notice produces a token symbol from the address - the first 6 hex of the address string in upper case /// @param token the token address /// @return the token symbol function addressToSymbol(address token) private pure returns (string memory) { diff --git a/test/PositionDescriptor.t.sol b/test/PositionDescriptor.t.sol index 81c7791d3..8498a9a94 100644 --- a/test/PositionDescriptor.t.sol +++ b/test/PositionDescriptor.t.sol @@ -115,6 +115,5 @@ contract PositionDescriptorTest is Test, PosmTestSetup { // decode json bytes memory data = vm.parseJson(json); Token memory token = abi.decode(data, (Token)); - console2.log("token.image", token.image); } } diff --git a/test/libraries/Descriptor.t.sol b/test/libraries/Descriptor.t.sol index e3e45a634..a73f79f94 100644 --- a/test/libraries/Descriptor.t.sol +++ b/test/libraries/Descriptor.t.sol @@ -25,7 +25,7 @@ contract DescriptorTest is Test { assertEq(Descriptor.feeToPercentString(12300000), "1230%"); } - function test_addressToString_succeeds() public { + function test_addressToString_succeeds() public pure { assertEq(Descriptor.addressToString(address(0)), "0x0000000000000000000000000000000000000000"); assertEq(Descriptor.addressToString(address(1)), "0x0000000000000000000000000000000000000001"); assertEq( @@ -38,7 +38,7 @@ contract DescriptorTest is Test { ); } - function test_escapeQuotes_succeeds() public { + function test_escapeQuotes_succeeds() public pure { assertEq(Descriptor.escapeQuotes(""), ""); assertEq(Descriptor.escapeQuotes("a"), "a"); assertEq(Descriptor.escapeQuotes("abc"), "abc"); @@ -49,7 +49,7 @@ contract DescriptorTest is Test { assertEq(Descriptor.escapeQuotes("\"a\"b\"c\"\""), "\\\"a\\\"b\\\"c\\\"\\\""); } - function test_tickToDecimalString_withTickSpacing10() public { + function test_tickToDecimalString_withTickSpacing10() public pure { int24 tickSpacing = 10; int24 minTick = (TickMath.MIN_TICK / tickSpacing) * tickSpacing; int24 maxTick = (TickMath.MAX_TICK / tickSpacing) * tickSpacing; @@ -63,7 +63,7 @@ contract DescriptorTest is Test { ); } - function test_tickToDecimalString_withTickSpacing60() public { + function test_tickToDecimalString_withTickSpacing60() public pure { int24 tickSpacing = 60; int24 minTick = (TickMath.MIN_TICK / tickSpacing) * tickSpacing; int24 maxTick = (TickMath.MAX_TICK / tickSpacing) * tickSpacing; @@ -77,7 +77,7 @@ contract DescriptorTest is Test { ); } - function test_tickToDecimalString_withTickSpacing200() public { + function test_tickToDecimalString_withTickSpacing200() public pure { int24 tickSpacing = 200; int24 minTick = (TickMath.MIN_TICK / tickSpacing) * tickSpacing; int24 maxTick = (TickMath.MAX_TICK / tickSpacing) * tickSpacing; @@ -91,30 +91,30 @@ contract DescriptorTest is Test { ); } - function test_tickToDecimalString_ratio_returnsInverseMediumNumbers() public { + function test_tickToDecimalString_ratio_returnsInverseMediumNumbers() public pure { int24 tickSpacing = 200; assertEq(Descriptor.tickToDecimalString(10, tickSpacing, 18, 18, false), "1.0010"); assertEq(Descriptor.tickToDecimalString(10, tickSpacing, 18, 18, true), "0.99900"); } - function test_tickToDecimalString_ratio_returnsInverseLargeNumbers() public { + function test_tickToDecimalString_ratio_returnsInverseLargeNumbers() public pure { int24 tickSpacing = 200; assertEq(Descriptor.tickToDecimalString(487272, tickSpacing, 18, 18, false), "1448400000000000000000"); assertEq(Descriptor.tickToDecimalString(487272, tickSpacing, 18, 18, true), "0.00000000000000000000069041"); } - function test_tickToDecimalString_ratio_returnsInverseSmallNumbers() public { + function test_tickToDecimalString_ratio_returnsInverseSmallNumbers() public pure { int24 tickSpacing = 200; assertEq(Descriptor.tickToDecimalString(-387272, tickSpacing, 18, 18, false), "0.000000000000000015200"); assertEq(Descriptor.tickToDecimalString(-387272, tickSpacing, 18, 18, true), "65791000000000000"); } - function test_tickToDecimalString_differentDecimals() public { + function test_tickToDecimalString_differentDecimals() public pure { int24 tickSpacing = 200; assertEq(Descriptor.tickToDecimalString(1000, tickSpacing, 18, 18, true), "0.90484"); assertEq(Descriptor.tickToDecimalString(1000, tickSpacing, 18, 10, true), "90484000"); assertEq(Descriptor.tickToDecimalString(1000, tickSpacing, 10, 18, true), "0.0000000090484"); } - function test_fixedPointToDecimalString_succeeds() public {} + function test_fixedPointToDecimalString_succeeds() public pure {} } diff --git a/test/mocks/MockERC721Permit.sol b/test/mocks/MockERC721Permit.sol index 75bd1c6ba..bf3792084 100644 --- a/test/mocks/MockERC721Permit.sol +++ b/test/mocks/MockERC721Permit.sol @@ -13,7 +13,7 @@ contract MockERC721Permit is ERC721Permit_v4 { _mint(msg.sender, tokenId); } - function tokenURI(uint256 id) public view override returns (string memory) { + function tokenURI(uint256) public pure override returns (string memory) { return "mock"; } } From 04b719ae5559ef8f3953c5481ad654d4cffb4b56 Mon Sep 17 00:00:00 2001 From: dianakocsis Date: Thu, 19 Sep 2024 14:15:24 -0400 Subject: [PATCH 26/45] more fixes --- src/PositionDescriptor.sol | 13 ++++++------- src/libraries/AddressStringUtil.sol | 2 +- src/libraries/Descriptor.sol | 14 +++++++------- src/libraries/HexStrings.sol | 2 +- src/libraries/SVG.sol | 22 +++++++--------------- src/libraries/SafeERC20Namer.sol | 2 +- test/PositionDescriptor.t.sol | 4 ++-- 7 files changed, 25 insertions(+), 34 deletions(-) diff --git a/src/PositionDescriptor.sol b/src/PositionDescriptor.sol index 1deb5426d..af3478012 100644 --- a/src/PositionDescriptor.sol +++ b/src/PositionDescriptor.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.24; +pragma solidity 0.8.26; import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol"; import {Currency, CurrencyLibrary} from "@uniswap/v4-core/src/types/Currency.sol"; @@ -7,14 +7,13 @@ import {IHooks} from "@uniswap/v4-core/src/interfaces/IHooks.sol"; import {StateLibrary} from "@uniswap/v4-core/src/libraries/StateLibrary.sol"; import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol"; import {PoolId, PoolIdLibrary} from "@uniswap/v4-core/src/types/PoolId.sol"; +import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import {IPositionManager} from "./interfaces/IPositionManager.sol"; +import {IPositionDescriptor} from "./interfaces/IPositionDescriptor.sol"; import {PositionInfo, PositionInfoLibrary} from "./libraries/PositionInfoLibrary.sol"; -import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; -import "./interfaces/IPositionDescriptor.sol"; -import "./libraries/Descriptor.sol"; -import "./interfaces/IPositionManager.sol"; -import "./libraries/CurrencyRatioSortOrder.sol"; -import "./libraries/SafeERC20Namer.sol"; +import {Descriptor} from "./libraries/Descriptor.sol"; +import {CurrencyRatioSortOrder} from "./libraries/CurrencyRatioSortOrder.sol"; +import {SafeERC20Namer} from "./libraries/SafeERC20Namer.sol"; /// @title Describes NFT token positions /// @notice Produces a string containing the data URI for a JSON metadata string diff --git a/src/libraries/AddressStringUtil.sol b/src/libraries/AddressStringUtil.sol index 823641d26..bb6aff303 100644 --- a/src/libraries/AddressStringUtil.sol +++ b/src/libraries/AddressStringUtil.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.24; +pragma solidity ^0.8.20; /// @title AddressStringUtil /// @notice provides utility functions for converting addresses to strings diff --git a/src/libraries/Descriptor.sol b/src/libraries/Descriptor.sol index cb546f22c..0f04eb2c5 100644 --- a/src/libraries/Descriptor.sol +++ b/src/libraries/Descriptor.sol @@ -1,14 +1,14 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.24; +pragma solidity ^0.8.20; import {Currency, CurrencyLibrary} from "@uniswap/v4-core/src/types/Currency.sol"; import {IHooks} from "@uniswap/v4-core/src/interfaces/IHooks.sol"; import {TickMath} from "@uniswap/v4-core/src/libraries/TickMath.sol"; import {FullMath} from "@uniswap/v4-core/src/libraries/FullMath.sol"; -import "@openzeppelin/contracts/utils/Strings.sol"; -import "@openzeppelin/contracts/utils/Base64.sol"; -import "./SVG.sol"; -import "./HexStrings.sol"; +import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; +import {Base64} from "@openzeppelin/contracts/utils/Base64.sol"; +import {SVG} from "./SVG.sol"; +import {HexStrings} from "./HexStrings.sol"; /// @title Descriptor /// @notice Describes NFT token positions @@ -483,11 +483,11 @@ library Descriptor { return SVG.generateSVG(svgParams); } - /// @notice Checks if the current tick is within the tick range, above, or below + /// @notice Checks if the current price is within your position range, above, or below /// @param tickLower The lower tick /// @param tickUpper The upper tick /// @param tickCurrent The current tick - /// @return 0 if current tick is within range, -1 if below, 1 if above + /// @return 0 if the current price is within the position range, -1 if below, 1 if above function overRange(int24 tickLower, int24 tickUpper, int24 tickCurrent) private pure returns (int8) { if (tickCurrent < tickLower) { return -1; diff --git a/src/libraries/HexStrings.sol b/src/libraries/HexStrings.sol index 8d662eaf4..0be747d30 100644 --- a/src/libraries/HexStrings.sol +++ b/src/libraries/HexStrings.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.24; +pragma solidity ^0.8.20; /// @title HexStrings /// @notice Provides function for converting numbers to hexadecimal strings diff --git a/src/libraries/SVG.sol b/src/libraries/SVG.sol index bb083b33b..b12bd2855 100644 --- a/src/libraries/SVG.sol +++ b/src/libraries/SVG.sol @@ -1,11 +1,11 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.24; +pragma solidity ^0.8.20; import {Currency, CurrencyLibrary} from "@uniswap/v4-core/src/types/Currency.sol"; import {IHooks} from "@uniswap/v4-core/src/interfaces/IHooks.sol"; -import "@openzeppelin/contracts/utils/Strings.sol"; -import "@uniswap/v4-core/src/libraries/BitMath.sol"; -import "@openzeppelin/contracts/utils/Base64.sol"; +import {BitMath} from "@uniswap/v4-core/src/libraries/BitMath.sol"; +import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; +import {Base64} from "@openzeppelin/contracts/utils/Base64.sol"; /// @title SVG /// @notice Provides a function for generating an SVG associated with a Uniswap NFT @@ -226,7 +226,7 @@ library SVG { ); } - /// @notice Generate the SVG for the curve that represents the position. Fade up (top is faded) if position over range, fade down (bottom is faded) if under range, else no fade + /// @notice Generate the SVG for the curve that represents the position. Fade up (top is faded) if current price is above your position range, fade down (bottom is faded) if current price is below your position range /// Circles are generated at the ends of the curve if the position is in range, or at one end of the curve it is on if not in range /// @param tickLower The lower tick /// @param tickUpper The upper tick @@ -335,7 +335,7 @@ library SVG { } } - /// @notice Generate the SVG for the position data (token ID, hooks address, min tick, max tick) and the location curve (the LP's position in the curve where the provider is active in the pool) + /// @notice Generate the SVG for the position data (token ID, hooks address, min tick, max tick) and the location curve (where your position falls on the curve) /// @param tokenId The token ID /// @param hook The hooks address /// @param tickLower The lower tick @@ -397,11 +397,6 @@ library SVG { ); } - /// @notice Get a substring of a string - /// @param str The string - /// @param startIndex The start index - /// @param endIndex The end index - /// @return The substring function substring(string memory str, uint256 startIndex, uint256 endIndex) internal pure returns (string memory) { bytes memory strBytes = bytes(str); bytes memory result = new bytes(endIndex - startIndex); @@ -411,9 +406,6 @@ library SVG { return string(result); } - /// @notice Convert a tick to a string - /// @param tick The tick to convert - /// @return The tick as a string function tickToString(int24 tick) private pure returns (string memory) { string memory sign = ""; if (tick < 0) { @@ -423,7 +415,7 @@ library SVG { return string(abi.encodePacked(sign, uint256(uint24(tick)).toString())); } - /// @notice Get the location of where the liquidity is supplied based on the tick range of the position + /// @notice Get the location of where your position falls on the curve /// @param tickLower The lower tick /// @param tickUpper The upper tick /// @return The x and y coordinates of the location of the liquidity diff --git a/src/libraries/SafeERC20Namer.sol b/src/libraries/SafeERC20Namer.sol index 801c4d42b..04bd63a13 100644 --- a/src/libraries/SafeERC20Namer.sol +++ b/src/libraries/SafeERC20Namer.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.24; +pragma solidity ^0.8.20; import "./AddressStringUtil.sol"; diff --git a/test/PositionDescriptor.t.sol b/test/PositionDescriptor.t.sol index 8498a9a94..9e0fe839b 100644 --- a/test/PositionDescriptor.t.sol +++ b/test/PositionDescriptor.t.sol @@ -78,8 +78,8 @@ contract PositionDescriptorTest is Test, PosmTestSetup { } function test_tokenURI_succeeds() public { - int24 tickLower = -int24(key.tickSpacing); - int24 tickUpper = int24(key.tickSpacing); + int24 tickLower = int24(key.tickSpacing); + int24 tickUpper = int24(key.tickSpacing * 2); uint256 amount0Desired = 100e18; uint256 amount1Desired = 100e18; uint256 liquidityToAdd = LiquidityAmounts.getLiquidityForAmounts( From 7e110cc213458ad36111139a8881dd4f68405f25 Mon Sep 17 00:00:00 2001 From: dianakocsis Date: Thu, 19 Sep 2024 16:44:02 -0400 Subject: [PATCH 27/45] priority --- src/PositionDescriptor.sol | 13 ++++++------- src/libraries/CurrencyRatioSortOrder.sol | 3 ++- test/PositionDescriptor.t.sol | 4 ++-- test/libraries/Descriptor.t.sol | 4 +--- 4 files changed, 11 insertions(+), 13 deletions(-) diff --git a/src/PositionDescriptor.sol b/src/PositionDescriptor.sol index af3478012..522b449aa 100644 --- a/src/PositionDescriptor.sol +++ b/src/PositionDescriptor.sol @@ -30,8 +30,6 @@ contract PositionDescriptor is IPositionDescriptor { address private constant TBTC = 0x8dAEBADE922dF735c38C80C7eBD708Af50815fAa; address private constant WBTC = 0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599; - // WBTC, DAI, TBTC, USDC, USDT - address public immutable WETH9; /// @dev A null-terminated string bytes32 public immutable nativeCurrencyLabelBytes; @@ -118,15 +116,16 @@ contract PositionDescriptor is IPositionDescriptor { /// @param currency The currency /// @return priority The priority of the currency function currencyRatioPriority(address currency) public view returns (int256) { - // Currencies in order of priority on mainnet: USDC, USDT, DAI, WETH, TBTC, WBTC - // USDC > USDT > DAI > WETH > TBTC > WBTC - // or native currency + // Currencies in order of priority on mainnet: USDC, USDT, DAI, ETH, WETH, TBTC, WBTC // weth is different address on different chains. passed in constructor - // if currency is WETH OR currency is native - if (currency == WETH9) { + // native currency + if (currency == address(0)) { return CurrencyRatioSortOrder.DENOMINATOR; } + if (currency == WETH9) { + return CurrencyRatioSortOrder.DENOMINATOR_2; + } if (block.chainid == 1) { if (currency == USDC) { return CurrencyRatioSortOrder.NUMERATOR_MOST; diff --git a/src/libraries/CurrencyRatioSortOrder.sol b/src/libraries/CurrencyRatioSortOrder.sol index dc5f58686..857d74484 100644 --- a/src/libraries/CurrencyRatioSortOrder.sol +++ b/src/libraries/CurrencyRatioSortOrder.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.24; +pragma solidity ^0.8.20; library CurrencyRatioSortOrder { int256 constant NUMERATOR_MOST = 300; @@ -8,5 +8,6 @@ library CurrencyRatioSortOrder { int256 constant DENOMINATOR_MOST = -300; int256 constant DENOMINATOR_MORE = -200; + int256 constant DENOMINATOR_2 = -150; int256 constant DENOMINATOR = -100; } diff --git a/test/PositionDescriptor.t.sol b/test/PositionDescriptor.t.sol index 9e0fe839b..9bae6787f 100644 --- a/test/PositionDescriptor.t.sol +++ b/test/PositionDescriptor.t.sol @@ -48,7 +48,7 @@ contract PositionDescriptorTest is Test, PosmTestSetup { function test_currencyRatioPriority_mainnet_succeeds() public { vm.chainId(1); - assertEq(positionDescriptor.currencyRatioPriority(WETH9), CurrencyRatioSortOrder.DENOMINATOR); + assertEq(positionDescriptor.currencyRatioPriority(WETH9), CurrencyRatioSortOrder.DENOMINATOR_2); assertEq(positionDescriptor.currencyRatioPriority(USDC), CurrencyRatioSortOrder.NUMERATOR_MOST); assertEq(positionDescriptor.currencyRatioPriority(USDT), CurrencyRatioSortOrder.NUMERATOR_MORE); assertEq(positionDescriptor.currencyRatioPriority(DAI), CurrencyRatioSortOrder.NUMERATOR); @@ -58,7 +58,7 @@ contract PositionDescriptorTest is Test, PosmTestSetup { } function test_currencyRatioPriority_notMainnet_succeeds() public { - assertEq(positionDescriptor.currencyRatioPriority(WETH9), CurrencyRatioSortOrder.DENOMINATOR); + assertEq(positionDescriptor.currencyRatioPriority(WETH9), CurrencyRatioSortOrder.DENOMINATOR_2); assertEq(positionDescriptor.currencyRatioPriority(USDC), 0); assertEq(positionDescriptor.currencyRatioPriority(USDT), 0); assertEq(positionDescriptor.currencyRatioPriority(DAI), 0); diff --git a/test/libraries/Descriptor.t.sol b/test/libraries/Descriptor.t.sol index a73f79f94..2dd6339aa 100644 --- a/test/libraries/Descriptor.t.sol +++ b/test/libraries/Descriptor.t.sol @@ -6,7 +6,7 @@ import {Test} from "forge-std/Test.sol"; import {TickMath} from "@uniswap/v4-core/src/libraries/TickMath.sol"; contract DescriptorTest is Test { - function test_feeToPercentString_succeeds() public { + function test_feeToPercentString_succeeds() public pure { assertEq(Descriptor.feeToPercentString(0), "0%"); assertEq(Descriptor.feeToPercentString(1), "0.0001%"); assertEq(Descriptor.feeToPercentString(30), "0.003%"); @@ -115,6 +115,4 @@ contract DescriptorTest is Test { assertEq(Descriptor.tickToDecimalString(1000, tickSpacing, 18, 10, true), "90484000"); assertEq(Descriptor.tickToDecimalString(1000, tickSpacing, 10, 18, true), "0.0000000090484"); } - - function test_fixedPointToDecimalString_succeeds() public pure {} } From 924d12edaebc95290153cb9a7ce152ae8f26f64b Mon Sep 17 00:00:00 2001 From: dianakocsis Date: Thu, 19 Sep 2024 16:51:47 -0400 Subject: [PATCH 28/45] update core to fix snapshots --- .forge-snapshots/BaseActionsRouter_mock10commands.snap | 2 +- .../Payments_swap_settleFromCaller_takeAllToMsgSender.snap | 2 +- ...ayments_swap_settleFromCaller_takeAllToSpecifiedAddress.snap | 2 +- .../Payments_swap_settleWithBalance_takeAllToMsgSender.snap | 2 +- ...yments_swap_settleWithBalance_takeAllToSpecifiedAddress.snap | 2 +- .forge-snapshots/PositionManager_burn_empty.snap | 2 +- .forge-snapshots/PositionManager_burn_empty_native.snap | 2 +- .../PositionManager_burn_nonEmpty_native_withClose.snap | 2 +- .../PositionManager_burn_nonEmpty_native_withTakePair.snap | 2 +- .forge-snapshots/PositionManager_burn_nonEmpty_withClose.snap | 2 +- .../PositionManager_burn_nonEmpty_withTakePair.snap | 2 +- .forge-snapshots/PositionManager_collect_native.snap | 2 +- .forge-snapshots/PositionManager_collect_sameRange.snap | 2 +- .forge-snapshots/PositionManager_collect_withClose.snap | 2 +- .forge-snapshots/PositionManager_collect_withTakePair.snap | 2 +- .forge-snapshots/PositionManager_decreaseLiquidity_native.snap | 2 +- .../PositionManager_decreaseLiquidity_withClose.snap | 2 +- .../PositionManager_decreaseLiquidity_withTakePair.snap | 2 +- .forge-snapshots/PositionManager_decrease_burnEmpty.snap | 2 +- .forge-snapshots/PositionManager_decrease_burnEmpty_native.snap | 2 +- .../PositionManager_decrease_sameRange_allLiquidity.snap | 2 +- .forge-snapshots/PositionManager_decrease_take_take.snap | 2 +- .../PositionManager_increaseLiquidity_erc20_withClose.snap | 2 +- .../PositionManager_increaseLiquidity_erc20_withSettlePair.snap | 2 +- .forge-snapshots/PositionManager_increaseLiquidity_native.snap | 2 +- ...PositionManager_increase_autocompoundExactUnclaimedFees.snap | 2 +- .../PositionManager_increase_autocompoundExcessFeesCredit.snap | 2 +- .../PositionManager_increase_autocompound_clearExcess.snap | 2 +- .forge-snapshots/PositionManager_mint_native.snap | 2 +- .../PositionManager_mint_nativeWithSweep_withClose.snap | 2 +- .../PositionManager_mint_nativeWithSweep_withSettlePair.snap | 2 +- .forge-snapshots/PositionManager_mint_onSameTickLower.snap | 2 +- .forge-snapshots/PositionManager_mint_onSameTickUpper.snap | 2 +- .forge-snapshots/PositionManager_mint_sameRange.snap | 2 +- .../PositionManager_mint_settleWithBalance_sweep.snap | 2 +- .../PositionManager_mint_warmedPool_differentRange.snap | 2 +- .forge-snapshots/PositionManager_mint_withClose.snap | 2 +- .forge-snapshots/PositionManager_mint_withSettlePair.snap | 2 +- .forge-snapshots/PositionManager_multicall_initialize_mint.snap | 2 +- .../Quoter_exactInputSingle_oneForZero_multiplePositions.snap | 2 +- .../Quoter_exactInputSingle_zeroForOne_multiplePositions.snap | 2 +- .forge-snapshots/Quoter_exactOutputSingle_oneForZero.snap | 2 +- .forge-snapshots/Quoter_exactOutputSingle_zeroForOne.snap | 2 +- .forge-snapshots/Quoter_quoteExactInput_oneHop_1TickLoaded.snap | 2 +- .../Quoter_quoteExactInput_oneHop_initializedAfter.snap | 2 +- .../Quoter_quoteExactInput_oneHop_startingInitialized.snap | 2 +- .forge-snapshots/Quoter_quoteExactInput_twoHops.snap | 2 +- .../Quoter_quoteExactOutput_oneHop_1TickLoaded.snap | 2 +- .../Quoter_quoteExactOutput_oneHop_2TicksLoaded.snap | 2 +- .../Quoter_quoteExactOutput_oneHop_initializedAfter.snap | 2 +- .../Quoter_quoteExactOutput_oneHop_startingInitialized.snap | 2 +- .forge-snapshots/Quoter_quoteExactOutput_twoHops.snap | 2 +- .forge-snapshots/StateView_extsload_getFeeGrowthGlobals.snap | 2 +- .forge-snapshots/StateView_extsload_getFeeGrowthInside.snap | 2 +- .forge-snapshots/StateView_extsload_getPositionInfo.snap | 2 +- .../StateView_extsload_getTickFeeGrowthOutside.snap | 2 +- .forge-snapshots/StateView_extsload_getTickInfo.snap | 2 +- .forge-snapshots/V4Router_ExactIn1Hop_nativeIn.snap | 2 +- .forge-snapshots/V4Router_ExactIn1Hop_nativeOut.snap | 2 +- .forge-snapshots/V4Router_ExactIn1Hop_oneForZero.snap | 2 +- .forge-snapshots/V4Router_ExactIn1Hop_zeroForOne.snap | 2 +- .forge-snapshots/V4Router_ExactIn2Hops.snap | 2 +- .forge-snapshots/V4Router_ExactIn2Hops_nativeIn.snap | 2 +- .forge-snapshots/V4Router_ExactIn3Hops.snap | 2 +- .forge-snapshots/V4Router_ExactIn3Hops_nativeIn.snap | 2 +- .forge-snapshots/V4Router_ExactInputSingle.snap | 2 +- .forge-snapshots/V4Router_ExactInputSingle_nativeIn.snap | 2 +- .forge-snapshots/V4Router_ExactInputSingle_nativeOut.snap | 2 +- .forge-snapshots/V4Router_ExactOut1Hop_nativeIn_sweepETH.snap | 2 +- .forge-snapshots/V4Router_ExactOut1Hop_nativeOut.snap | 2 +- .forge-snapshots/V4Router_ExactOut1Hop_oneForZero.snap | 2 +- .forge-snapshots/V4Router_ExactOut1Hop_zeroForOne.snap | 2 +- .forge-snapshots/V4Router_ExactOut2Hops.snap | 2 +- .forge-snapshots/V4Router_ExactOut2Hops_nativeIn.snap | 2 +- .forge-snapshots/V4Router_ExactOut3Hops.snap | 2 +- .forge-snapshots/V4Router_ExactOut3Hops_nativeIn.snap | 2 +- .forge-snapshots/V4Router_ExactOut3Hops_nativeOut.snap | 2 +- .forge-snapshots/V4Router_ExactOutputSingle.snap | 2 +- .../V4Router_ExactOutputSingle_nativeIn_sweepETH.snap | 2 +- .forge-snapshots/V4Router_ExactOutputSingle_nativeOut.snap | 2 +- lib/v4-core | 2 +- 81 files changed, 81 insertions(+), 81 deletions(-) diff --git a/.forge-snapshots/BaseActionsRouter_mock10commands.snap b/.forge-snapshots/BaseActionsRouter_mock10commands.snap index 2f6f033f0..8a065fc31 100644 --- a/.forge-snapshots/BaseActionsRouter_mock10commands.snap +++ b/.forge-snapshots/BaseActionsRouter_mock10commands.snap @@ -1 +1 @@ -60674 \ No newline at end of file +60677 \ No newline at end of file diff --git a/.forge-snapshots/Payments_swap_settleFromCaller_takeAllToMsgSender.snap b/.forge-snapshots/Payments_swap_settleFromCaller_takeAllToMsgSender.snap index 5e7fe01cd..2cd533eed 100644 --- a/.forge-snapshots/Payments_swap_settleFromCaller_takeAllToMsgSender.snap +++ b/.forge-snapshots/Payments_swap_settleFromCaller_takeAllToMsgSender.snap @@ -1 +1 @@ -129856 \ No newline at end of file +129854 \ No newline at end of file diff --git a/.forge-snapshots/Payments_swap_settleFromCaller_takeAllToSpecifiedAddress.snap b/.forge-snapshots/Payments_swap_settleFromCaller_takeAllToSpecifiedAddress.snap index 841fafefc..89faf94ce 100644 --- a/.forge-snapshots/Payments_swap_settleFromCaller_takeAllToSpecifiedAddress.snap +++ b/.forge-snapshots/Payments_swap_settleFromCaller_takeAllToSpecifiedAddress.snap @@ -1 +1 @@ -131907 \ No newline at end of file +131905 \ No newline at end of file diff --git a/.forge-snapshots/Payments_swap_settleWithBalance_takeAllToMsgSender.snap b/.forge-snapshots/Payments_swap_settleWithBalance_takeAllToMsgSender.snap index 408d05d3e..55ac6b3ac 100644 --- a/.forge-snapshots/Payments_swap_settleWithBalance_takeAllToMsgSender.snap +++ b/.forge-snapshots/Payments_swap_settleWithBalance_takeAllToMsgSender.snap @@ -1 +1 @@ -124112 \ No newline at end of file +124110 \ No newline at end of file diff --git a/.forge-snapshots/Payments_swap_settleWithBalance_takeAllToSpecifiedAddress.snap b/.forge-snapshots/Payments_swap_settleWithBalance_takeAllToSpecifiedAddress.snap index 0be84864b..00e673a8c 100644 --- a/.forge-snapshots/Payments_swap_settleWithBalance_takeAllToSpecifiedAddress.snap +++ b/.forge-snapshots/Payments_swap_settleWithBalance_takeAllToSpecifiedAddress.snap @@ -1 +1 @@ -124254 \ No newline at end of file +124252 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_burn_empty.snap b/.forge-snapshots/PositionManager_burn_empty.snap index 16a0217a7..949dd08ae 100644 --- a/.forge-snapshots/PositionManager_burn_empty.snap +++ b/.forge-snapshots/PositionManager_burn_empty.snap @@ -1 +1 @@ -50479 \ No newline at end of file +50481 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_burn_empty_native.snap b/.forge-snapshots/PositionManager_burn_empty_native.snap index 16a0217a7..949dd08ae 100644 --- a/.forge-snapshots/PositionManager_burn_empty_native.snap +++ b/.forge-snapshots/PositionManager_burn_empty_native.snap @@ -1 +1 @@ -50479 \ No newline at end of file +50481 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_burn_nonEmpty_native_withClose.snap b/.forge-snapshots/PositionManager_burn_nonEmpty_native_withClose.snap index 69556b779..2c42da0a4 100644 --- a/.forge-snapshots/PositionManager_burn_nonEmpty_native_withClose.snap +++ b/.forge-snapshots/PositionManager_burn_nonEmpty_native_withClose.snap @@ -1 +1 @@ -125612 \ No newline at end of file +125619 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_burn_nonEmpty_native_withTakePair.snap b/.forge-snapshots/PositionManager_burn_nonEmpty_native_withTakePair.snap index 2c4b3075f..fc3b23085 100644 --- a/.forge-snapshots/PositionManager_burn_nonEmpty_native_withTakePair.snap +++ b/.forge-snapshots/PositionManager_burn_nonEmpty_native_withTakePair.snap @@ -1 +1 @@ -125059 \ No newline at end of file +125066 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_burn_nonEmpty_withClose.snap b/.forge-snapshots/PositionManager_burn_nonEmpty_withClose.snap index 79ad3f252..0bb10f570 100644 --- a/.forge-snapshots/PositionManager_burn_nonEmpty_withClose.snap +++ b/.forge-snapshots/PositionManager_burn_nonEmpty_withClose.snap @@ -1 +1 @@ -132462 \ No newline at end of file +132472 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_burn_nonEmpty_withTakePair.snap b/.forge-snapshots/PositionManager_burn_nonEmpty_withTakePair.snap index dd001ceb2..853415928 100644 --- a/.forge-snapshots/PositionManager_burn_nonEmpty_withTakePair.snap +++ b/.forge-snapshots/PositionManager_burn_nonEmpty_withTakePair.snap @@ -1 +1 @@ -131909 \ No newline at end of file +131919 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_collect_native.snap b/.forge-snapshots/PositionManager_collect_native.snap index 20dd49568..88dbe7495 100644 --- a/.forge-snapshots/PositionManager_collect_native.snap +++ b/.forge-snapshots/PositionManager_collect_native.snap @@ -1 +1 @@ -146329 \ No newline at end of file +146338 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_collect_sameRange.snap b/.forge-snapshots/PositionManager_collect_sameRange.snap index ab0425641..813d39998 100644 --- a/.forge-snapshots/PositionManager_collect_sameRange.snap +++ b/.forge-snapshots/PositionManager_collect_sameRange.snap @@ -1 +1 @@ -154892 \ No newline at end of file +154904 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_collect_withClose.snap b/.forge-snapshots/PositionManager_collect_withClose.snap index ab0425641..813d39998 100644 --- a/.forge-snapshots/PositionManager_collect_withClose.snap +++ b/.forge-snapshots/PositionManager_collect_withClose.snap @@ -1 +1 @@ -154892 \ No newline at end of file +154904 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_collect_withTakePair.snap b/.forge-snapshots/PositionManager_collect_withTakePair.snap index 7f09fc960..face41a78 100644 --- a/.forge-snapshots/PositionManager_collect_withTakePair.snap +++ b/.forge-snapshots/PositionManager_collect_withTakePair.snap @@ -1 +1 @@ -154213 \ No newline at end of file +154225 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_decreaseLiquidity_native.snap b/.forge-snapshots/PositionManager_decreaseLiquidity_native.snap index 73d142714..e8aef4e19 100644 --- a/.forge-snapshots/PositionManager_decreaseLiquidity_native.snap +++ b/.forge-snapshots/PositionManager_decreaseLiquidity_native.snap @@ -1 +1 @@ -112008 \ No newline at end of file +112016 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_decreaseLiquidity_withClose.snap b/.forge-snapshots/PositionManager_decreaseLiquidity_withClose.snap index 5a422f6f0..5a4e17861 100644 --- a/.forge-snapshots/PositionManager_decreaseLiquidity_withClose.snap +++ b/.forge-snapshots/PositionManager_decreaseLiquidity_withClose.snap @@ -1 +1 @@ -119773 \ No newline at end of file +119785 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_decreaseLiquidity_withTakePair.snap b/.forge-snapshots/PositionManager_decreaseLiquidity_withTakePair.snap index 7766b70bb..3dcc78ecd 100644 --- a/.forge-snapshots/PositionManager_decreaseLiquidity_withTakePair.snap +++ b/.forge-snapshots/PositionManager_decreaseLiquidity_withTakePair.snap @@ -1 +1 @@ -119094 \ No newline at end of file +119106 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_decrease_burnEmpty.snap b/.forge-snapshots/PositionManager_decrease_burnEmpty.snap index 83fd9ce44..de5c9570a 100644 --- a/.forge-snapshots/PositionManager_decrease_burnEmpty.snap +++ b/.forge-snapshots/PositionManager_decrease_burnEmpty.snap @@ -1 +1 @@ -135259 \ No newline at end of file +135268 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_decrease_burnEmpty_native.snap b/.forge-snapshots/PositionManager_decrease_burnEmpty_native.snap index d632cc339..2e25cfd75 100644 --- a/.forge-snapshots/PositionManager_decrease_burnEmpty_native.snap +++ b/.forge-snapshots/PositionManager_decrease_burnEmpty_native.snap @@ -1 +1 @@ -128408 \ No newline at end of file +128416 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_decrease_sameRange_allLiquidity.snap b/.forge-snapshots/PositionManager_decrease_sameRange_allLiquidity.snap index dea300773..0bb10f570 100644 --- a/.forge-snapshots/PositionManager_decrease_sameRange_allLiquidity.snap +++ b/.forge-snapshots/PositionManager_decrease_sameRange_allLiquidity.snap @@ -1 +1 @@ -132460 \ No newline at end of file +132472 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_decrease_take_take.snap b/.forge-snapshots/PositionManager_decrease_take_take.snap index ce76c0488..5f819ce0b 100644 --- a/.forge-snapshots/PositionManager_decrease_take_take.snap +++ b/.forge-snapshots/PositionManager_decrease_take_take.snap @@ -1 +1 @@ -120349 \ No newline at end of file +120361 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_increaseLiquidity_erc20_withClose.snap b/.forge-snapshots/PositionManager_increaseLiquidity_erc20_withClose.snap index b19f59962..c0e0019dc 100644 --- a/.forge-snapshots/PositionManager_increaseLiquidity_erc20_withClose.snap +++ b/.forge-snapshots/PositionManager_increaseLiquidity_erc20_withClose.snap @@ -1 +1 @@ -159354 \ No newline at end of file +159089 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_increaseLiquidity_erc20_withSettlePair.snap b/.forge-snapshots/PositionManager_increaseLiquidity_erc20_withSettlePair.snap index 6f4abd2d1..e5852a38e 100644 --- a/.forge-snapshots/PositionManager_increaseLiquidity_erc20_withSettlePair.snap +++ b/.forge-snapshots/PositionManager_increaseLiquidity_erc20_withSettlePair.snap @@ -1 +1 @@ -158294 \ No newline at end of file +158029 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_increaseLiquidity_native.snap b/.forge-snapshots/PositionManager_increaseLiquidity_native.snap index 346b07a08..1ab92e5e9 100644 --- a/.forge-snapshots/PositionManager_increaseLiquidity_native.snap +++ b/.forge-snapshots/PositionManager_increaseLiquidity_native.snap @@ -1 +1 @@ -141033 \ No newline at end of file +140916 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_increase_autocompoundExactUnclaimedFees.snap b/.forge-snapshots/PositionManager_increase_autocompoundExactUnclaimedFees.snap index ab1fb3cd6..b6d9ed62f 100644 --- a/.forge-snapshots/PositionManager_increase_autocompoundExactUnclaimedFees.snap +++ b/.forge-snapshots/PositionManager_increase_autocompoundExactUnclaimedFees.snap @@ -1 +1 @@ -136377 \ No newline at end of file +136403 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_increase_autocompoundExcessFeesCredit.snap b/.forge-snapshots/PositionManager_increase_autocompoundExcessFeesCredit.snap index f4086689e..3f867eb95 100644 --- a/.forge-snapshots/PositionManager_increase_autocompoundExcessFeesCredit.snap +++ b/.forge-snapshots/PositionManager_increase_autocompoundExcessFeesCredit.snap @@ -1 +1 @@ -177365 \ No newline at end of file +177396 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_increase_autocompound_clearExcess.snap b/.forge-snapshots/PositionManager_increase_autocompound_clearExcess.snap index d1eaffc76..b9e03ce8f 100644 --- a/.forge-snapshots/PositionManager_increase_autocompound_clearExcess.snap +++ b/.forge-snapshots/PositionManager_increase_autocompound_clearExcess.snap @@ -1 +1 @@ -148047 \ No newline at end of file +148072 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_mint_native.snap b/.forge-snapshots/PositionManager_mint_native.snap index 221a9d16f..880c3c396 100644 --- a/.forge-snapshots/PositionManager_mint_native.snap +++ b/.forge-snapshots/PositionManager_mint_native.snap @@ -1 +1 @@ -364906 \ No newline at end of file +364789 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_mint_nativeWithSweep_withClose.snap b/.forge-snapshots/PositionManager_mint_nativeWithSweep_withClose.snap index 9d4414e25..993ae4910 100644 --- a/.forge-snapshots/PositionManager_mint_nativeWithSweep_withClose.snap +++ b/.forge-snapshots/PositionManager_mint_nativeWithSweep_withClose.snap @@ -1 +1 @@ -373428 \ No newline at end of file +373312 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_mint_nativeWithSweep_withSettlePair.snap b/.forge-snapshots/PositionManager_mint_nativeWithSweep_withSettlePair.snap index f79de9f06..1caa3586f 100644 --- a/.forge-snapshots/PositionManager_mint_nativeWithSweep_withSettlePair.snap +++ b/.forge-snapshots/PositionManager_mint_nativeWithSweep_withSettlePair.snap @@ -1 +1 @@ -372652 \ No newline at end of file +372535 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_mint_onSameTickLower.snap b/.forge-snapshots/PositionManager_mint_onSameTickLower.snap index c64548eae..3e5aa25c8 100644 --- a/.forge-snapshots/PositionManager_mint_onSameTickLower.snap +++ b/.forge-snapshots/PositionManager_mint_onSameTickLower.snap @@ -1 +1 @@ -317902 \ No newline at end of file +317637 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_mint_onSameTickUpper.snap b/.forge-snapshots/PositionManager_mint_onSameTickUpper.snap index fbe22a690..438b892db 100644 --- a/.forge-snapshots/PositionManager_mint_onSameTickUpper.snap +++ b/.forge-snapshots/PositionManager_mint_onSameTickUpper.snap @@ -1 +1 @@ -318572 \ No newline at end of file +318307 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_mint_sameRange.snap b/.forge-snapshots/PositionManager_mint_sameRange.snap index 96f6fe06b..8bded3adb 100644 --- a/.forge-snapshots/PositionManager_mint_sameRange.snap +++ b/.forge-snapshots/PositionManager_mint_sameRange.snap @@ -1 +1 @@ -244141 \ No newline at end of file +243876 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_mint_settleWithBalance_sweep.snap b/.forge-snapshots/PositionManager_mint_settleWithBalance_sweep.snap index 207ed3010..10d0993c6 100644 --- a/.forge-snapshots/PositionManager_mint_settleWithBalance_sweep.snap +++ b/.forge-snapshots/PositionManager_mint_settleWithBalance_sweep.snap @@ -1 +1 @@ -419333 \ No newline at end of file +419068 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_mint_warmedPool_differentRange.snap b/.forge-snapshots/PositionManager_mint_warmedPool_differentRange.snap index 731203226..58152d0f1 100644 --- a/.forge-snapshots/PositionManager_mint_warmedPool_differentRange.snap +++ b/.forge-snapshots/PositionManager_mint_warmedPool_differentRange.snap @@ -1 +1 @@ -323933 \ No newline at end of file +323668 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_mint_withClose.snap b/.forge-snapshots/PositionManager_mint_withClose.snap index 6fa085c2b..e183be069 100644 --- a/.forge-snapshots/PositionManager_mint_withClose.snap +++ b/.forge-snapshots/PositionManager_mint_withClose.snap @@ -1 +1 @@ -420455 \ No newline at end of file +420190 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_mint_withSettlePair.snap b/.forge-snapshots/PositionManager_mint_withSettlePair.snap index 73803bd7e..8da1fc4dd 100644 --- a/.forge-snapshots/PositionManager_mint_withSettlePair.snap +++ b/.forge-snapshots/PositionManager_mint_withSettlePair.snap @@ -1 +1 @@ -419513 \ No newline at end of file +419248 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_multicall_initialize_mint.snap b/.forge-snapshots/PositionManager_multicall_initialize_mint.snap index be40bf656..c16ec80e6 100644 --- a/.forge-snapshots/PositionManager_multicall_initialize_mint.snap +++ b/.forge-snapshots/PositionManager_multicall_initialize_mint.snap @@ -1 +1 @@ -464645 \ No newline at end of file +464384 \ No newline at end of file diff --git a/.forge-snapshots/Quoter_exactInputSingle_oneForZero_multiplePositions.snap b/.forge-snapshots/Quoter_exactInputSingle_oneForZero_multiplePositions.snap index 364988846..0c97c3620 100644 --- a/.forge-snapshots/Quoter_exactInputSingle_oneForZero_multiplePositions.snap +++ b/.forge-snapshots/Quoter_exactInputSingle_oneForZero_multiplePositions.snap @@ -1 +1 @@ -158920 \ No newline at end of file +159043 \ No newline at end of file diff --git a/.forge-snapshots/Quoter_exactInputSingle_zeroForOne_multiplePositions.snap b/.forge-snapshots/Quoter_exactInputSingle_zeroForOne_multiplePositions.snap index 27bfbb33b..af5fa0fa6 100644 --- a/.forge-snapshots/Quoter_exactInputSingle_zeroForOne_multiplePositions.snap +++ b/.forge-snapshots/Quoter_exactInputSingle_zeroForOne_multiplePositions.snap @@ -1 +1 @@ -166238 \ No newline at end of file +166396 \ No newline at end of file diff --git a/.forge-snapshots/Quoter_exactOutputSingle_oneForZero.snap b/.forge-snapshots/Quoter_exactOutputSingle_oneForZero.snap index 68bc18bbb..f43e94582 100644 --- a/.forge-snapshots/Quoter_exactOutputSingle_oneForZero.snap +++ b/.forge-snapshots/Quoter_exactOutputSingle_oneForZero.snap @@ -1 +1 @@ -93496 \ No newline at end of file +93637 \ No newline at end of file diff --git a/.forge-snapshots/Quoter_exactOutputSingle_zeroForOne.snap b/.forge-snapshots/Quoter_exactOutputSingle_zeroForOne.snap index 3d41a8397..66a68794e 100644 --- a/.forge-snapshots/Quoter_exactOutputSingle_zeroForOne.snap +++ b/.forge-snapshots/Quoter_exactOutputSingle_zeroForOne.snap @@ -1 +1 @@ -100134 \ No newline at end of file +100303 \ No newline at end of file diff --git a/.forge-snapshots/Quoter_quoteExactInput_oneHop_1TickLoaded.snap b/.forge-snapshots/Quoter_quoteExactInput_oneHop_1TickLoaded.snap index 77c1dc536..87d96db17 100644 --- a/.forge-snapshots/Quoter_quoteExactInput_oneHop_1TickLoaded.snap +++ b/.forge-snapshots/Quoter_quoteExactInput_oneHop_1TickLoaded.snap @@ -1 +1 @@ -141172 \ No newline at end of file +141321 \ No newline at end of file diff --git a/.forge-snapshots/Quoter_quoteExactInput_oneHop_initializedAfter.snap b/.forge-snapshots/Quoter_quoteExactInput_oneHop_initializedAfter.snap index 18741a08a..3315258ee 100644 --- a/.forge-snapshots/Quoter_quoteExactInput_oneHop_initializedAfter.snap +++ b/.forge-snapshots/Quoter_quoteExactInput_oneHop_initializedAfter.snap @@ -1 +1 @@ -164405 \ No newline at end of file +164528 \ No newline at end of file diff --git a/.forge-snapshots/Quoter_quoteExactInput_oneHop_startingInitialized.snap b/.forge-snapshots/Quoter_quoteExactInput_oneHop_startingInitialized.snap index db368a295..954792b66 100644 --- a/.forge-snapshots/Quoter_quoteExactInput_oneHop_startingInitialized.snap +++ b/.forge-snapshots/Quoter_quoteExactInput_oneHop_startingInitialized.snap @@ -1 +1 @@ -98506 \ No newline at end of file +98641 \ No newline at end of file diff --git a/.forge-snapshots/Quoter_quoteExactInput_twoHops.snap b/.forge-snapshots/Quoter_quoteExactInput_twoHops.snap index aebbedf7f..b8ed8de2f 100644 --- a/.forge-snapshots/Quoter_quoteExactInput_twoHops.snap +++ b/.forge-snapshots/Quoter_quoteExactInput_twoHops.snap @@ -1 +1 @@ -234516 \ No newline at end of file +234806 \ No newline at end of file diff --git a/.forge-snapshots/Quoter_quoteExactOutput_oneHop_1TickLoaded.snap b/.forge-snapshots/Quoter_quoteExactOutput_oneHop_1TickLoaded.snap index 66ccea74d..8a6db3df9 100644 --- a/.forge-snapshots/Quoter_quoteExactOutput_oneHop_1TickLoaded.snap +++ b/.forge-snapshots/Quoter_quoteExactOutput_oneHop_1TickLoaded.snap @@ -1 +1 @@ -161139 \ No newline at end of file +161346 \ No newline at end of file diff --git a/.forge-snapshots/Quoter_quoteExactOutput_oneHop_2TicksLoaded.snap b/.forge-snapshots/Quoter_quoteExactOutput_oneHop_2TicksLoaded.snap index 2a85658e8..7391d6be7 100644 --- a/.forge-snapshots/Quoter_quoteExactOutput_oneHop_2TicksLoaded.snap +++ b/.forge-snapshots/Quoter_quoteExactOutput_oneHop_2TicksLoaded.snap @@ -1 +1 @@ -191214 \ No newline at end of file +191453 \ No newline at end of file diff --git a/.forge-snapshots/Quoter_quoteExactOutput_oneHop_initializedAfter.snap b/.forge-snapshots/Quoter_quoteExactOutput_oneHop_initializedAfter.snap index 05214c6ac..cc4af5c55 100644 --- a/.forge-snapshots/Quoter_quoteExactOutput_oneHop_initializedAfter.snap +++ b/.forge-snapshots/Quoter_quoteExactOutput_oneHop_initializedAfter.snap @@ -1 +1 @@ -161454 \ No newline at end of file +161661 \ No newline at end of file diff --git a/.forge-snapshots/Quoter_quoteExactOutput_oneHop_startingInitialized.snap b/.forge-snapshots/Quoter_quoteExactOutput_oneHop_startingInitialized.snap index dbf22d2b1..5aed9cf81 100644 --- a/.forge-snapshots/Quoter_quoteExactOutput_oneHop_startingInitialized.snap +++ b/.forge-snapshots/Quoter_quoteExactOutput_oneHop_startingInitialized.snap @@ -1 +1 @@ -136248 \ No newline at end of file +136430 \ No newline at end of file diff --git a/.forge-snapshots/Quoter_quoteExactOutput_twoHops.snap b/.forge-snapshots/Quoter_quoteExactOutput_twoHops.snap index 68edf156c..da4bedcf8 100644 --- a/.forge-snapshots/Quoter_quoteExactOutput_twoHops.snap +++ b/.forge-snapshots/Quoter_quoteExactOutput_twoHops.snap @@ -1 +1 @@ -275337 \ No newline at end of file +275720 \ No newline at end of file diff --git a/.forge-snapshots/StateView_extsload_getFeeGrowthGlobals.snap b/.forge-snapshots/StateView_extsload_getFeeGrowthGlobals.snap index 920727d2e..98665bfc6 100644 --- a/.forge-snapshots/StateView_extsload_getFeeGrowthGlobals.snap +++ b/.forge-snapshots/StateView_extsload_getFeeGrowthGlobals.snap @@ -1 +1 @@ -2256 \ No newline at end of file +2259 \ No newline at end of file diff --git a/.forge-snapshots/StateView_extsload_getFeeGrowthInside.snap b/.forge-snapshots/StateView_extsload_getFeeGrowthInside.snap index 6df672447..7db58ace5 100644 --- a/.forge-snapshots/StateView_extsload_getFeeGrowthInside.snap +++ b/.forge-snapshots/StateView_extsload_getFeeGrowthInside.snap @@ -1 +1 @@ -7994 \ No newline at end of file +8003 \ No newline at end of file diff --git a/.forge-snapshots/StateView_extsload_getPositionInfo.snap b/.forge-snapshots/StateView_extsload_getPositionInfo.snap index c173e591f..4b9661fc9 100644 --- a/.forge-snapshots/StateView_extsload_getPositionInfo.snap +++ b/.forge-snapshots/StateView_extsload_getPositionInfo.snap @@ -1 +1 @@ -2826 \ No newline at end of file +2829 \ No newline at end of file diff --git a/.forge-snapshots/StateView_extsload_getTickFeeGrowthOutside.snap b/.forge-snapshots/StateView_extsload_getTickFeeGrowthOutside.snap index 7a03b74c0..6870d0f23 100644 --- a/.forge-snapshots/StateView_extsload_getTickFeeGrowthOutside.snap +++ b/.forge-snapshots/StateView_extsload_getTickFeeGrowthOutside.snap @@ -1 +1 @@ -2543 \ No newline at end of file +2546 \ No newline at end of file diff --git a/.forge-snapshots/StateView_extsload_getTickInfo.snap b/.forge-snapshots/StateView_extsload_getTickInfo.snap index f538595cb..cd5ecabca 100644 --- a/.forge-snapshots/StateView_extsload_getTickInfo.snap +++ b/.forge-snapshots/StateView_extsload_getTickInfo.snap @@ -1 +1 @@ -2758 \ No newline at end of file +2761 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactIn1Hop_nativeIn.snap b/.forge-snapshots/V4Router_ExactIn1Hop_nativeIn.snap index 7b701dd59..bc9d99894 100644 --- a/.forge-snapshots/V4Router_ExactIn1Hop_nativeIn.snap +++ b/.forge-snapshots/V4Router_ExactIn1Hop_nativeIn.snap @@ -1 +1 @@ -115576 \ No newline at end of file +115722 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactIn1Hop_nativeOut.snap b/.forge-snapshots/V4Router_ExactIn1Hop_nativeOut.snap index 472a50b46..764f3bbb0 100644 --- a/.forge-snapshots/V4Router_ExactIn1Hop_nativeOut.snap +++ b/.forge-snapshots/V4Router_ExactIn1Hop_nativeOut.snap @@ -1 +1 @@ -116053 \ No newline at end of file +116043 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactIn1Hop_oneForZero.snap b/.forge-snapshots/V4Router_ExactIn1Hop_oneForZero.snap index d5c745a83..40990e3ce 100644 --- a/.forge-snapshots/V4Router_ExactIn1Hop_oneForZero.snap +++ b/.forge-snapshots/V4Router_ExactIn1Hop_oneForZero.snap @@ -1 +1 @@ -124868 \ No newline at end of file +124861 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactIn1Hop_zeroForOne.snap b/.forge-snapshots/V4Router_ExactIn1Hop_zeroForOne.snap index b0e15f0f4..a38c964a3 100644 --- a/.forge-snapshots/V4Router_ExactIn1Hop_zeroForOne.snap +++ b/.forge-snapshots/V4Router_ExactIn1Hop_zeroForOne.snap @@ -1 +1 @@ -130586 \ No newline at end of file +130584 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactIn2Hops.snap b/.forge-snapshots/V4Router_ExactIn2Hops.snap index 0b17fe2d7..3b3ffc197 100644 --- a/.forge-snapshots/V4Router_ExactIn2Hops.snap +++ b/.forge-snapshots/V4Router_ExactIn2Hops.snap @@ -1 +1 @@ -179594 \ No newline at end of file +179724 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactIn2Hops_nativeIn.snap b/.forge-snapshots/V4Router_ExactIn2Hops_nativeIn.snap index db029a347..2862d64cc 100644 --- a/.forge-snapshots/V4Router_ExactIn2Hops_nativeIn.snap +++ b/.forge-snapshots/V4Router_ExactIn2Hops_nativeIn.snap @@ -1 +1 @@ -170294 \ No newline at end of file +170577 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactIn3Hops.snap b/.forge-snapshots/V4Router_ExactIn3Hops.snap index 7286dc0c4..202b1b328 100644 --- a/.forge-snapshots/V4Router_ExactIn3Hops.snap +++ b/.forge-snapshots/V4Router_ExactIn3Hops.snap @@ -1 +1 @@ -228581 \ No newline at end of file +228843 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactIn3Hops_nativeIn.snap b/.forge-snapshots/V4Router_ExactIn3Hops_nativeIn.snap index 915eb438b..d3d293a5e 100644 --- a/.forge-snapshots/V4Router_ExactIn3Hops_nativeIn.snap +++ b/.forge-snapshots/V4Router_ExactIn3Hops_nativeIn.snap @@ -1 +1 @@ -219305 \ No newline at end of file +219720 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactInputSingle.snap b/.forge-snapshots/V4Router_ExactInputSingle.snap index 5e7fe01cd..2cd533eed 100644 --- a/.forge-snapshots/V4Router_ExactInputSingle.snap +++ b/.forge-snapshots/V4Router_ExactInputSingle.snap @@ -1 +1 @@ -129856 \ No newline at end of file +129854 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactInputSingle_nativeIn.snap b/.forge-snapshots/V4Router_ExactInputSingle_nativeIn.snap index bfee99530..5e5c5b3ba 100644 --- a/.forge-snapshots/V4Router_ExactInputSingle_nativeIn.snap +++ b/.forge-snapshots/V4Router_ExactInputSingle_nativeIn.snap @@ -1 +1 @@ -114846 \ No newline at end of file +114992 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactInputSingle_nativeOut.snap b/.forge-snapshots/V4Router_ExactInputSingle_nativeOut.snap index 14eaf857d..f36fa4504 100644 --- a/.forge-snapshots/V4Router_ExactInputSingle_nativeOut.snap +++ b/.forge-snapshots/V4Router_ExactInputSingle_nativeOut.snap @@ -1 +1 @@ -115292 \ No newline at end of file +115282 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactOut1Hop_nativeIn_sweepETH.snap b/.forge-snapshots/V4Router_ExactOut1Hop_nativeIn_sweepETH.snap index 70c8bd969..9c6beb911 100644 --- a/.forge-snapshots/V4Router_ExactOut1Hop_nativeIn_sweepETH.snap +++ b/.forge-snapshots/V4Router_ExactOut1Hop_nativeIn_sweepETH.snap @@ -1 +1 @@ -121804 \ No newline at end of file +121985 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactOut1Hop_nativeOut.snap b/.forge-snapshots/V4Router_ExactOut1Hop_nativeOut.snap index dae7fde20..adfd51ab4 100644 --- a/.forge-snapshots/V4Router_ExactOut1Hop_nativeOut.snap +++ b/.forge-snapshots/V4Router_ExactOut1Hop_nativeOut.snap @@ -1 +1 @@ -117105 \ No newline at end of file +117107 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactOut1Hop_oneForZero.snap b/.forge-snapshots/V4Router_ExactOut1Hop_oneForZero.snap index 66f93521e..7692da738 100644 --- a/.forge-snapshots/V4Router_ExactOut1Hop_oneForZero.snap +++ b/.forge-snapshots/V4Router_ExactOut1Hop_oneForZero.snap @@ -1 +1 @@ -125920 \ No newline at end of file +125925 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactOut1Hop_zeroForOne.snap b/.forge-snapshots/V4Router_ExactOut1Hop_zeroForOne.snap index 63c6512b8..93703d01e 100644 --- a/.forge-snapshots/V4Router_ExactOut1Hop_zeroForOne.snap +++ b/.forge-snapshots/V4Router_ExactOut1Hop_zeroForOne.snap @@ -1 +1 @@ -129837 \ No newline at end of file +129870 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactOut2Hops.snap b/.forge-snapshots/V4Router_ExactOut2Hops.snap index d605b413f..8e5c9c642 100644 --- a/.forge-snapshots/V4Router_ExactOut2Hops.snap +++ b/.forge-snapshots/V4Router_ExactOut2Hops.snap @@ -1 +1 @@ -179665 \ No newline at end of file +179842 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactOut2Hops_nativeIn.snap b/.forge-snapshots/V4Router_ExactOut2Hops_nativeIn.snap index 2680d0353..f236e20d6 100644 --- a/.forge-snapshots/V4Router_ExactOut2Hops_nativeIn.snap +++ b/.forge-snapshots/V4Router_ExactOut2Hops_nativeIn.snap @@ -1 +1 @@ -175549 \ No newline at end of file +175902 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactOut3Hops.snap b/.forge-snapshots/V4Router_ExactOut3Hops.snap index 80b32056c..9449e5745 100644 --- a/.forge-snapshots/V4Router_ExactOut3Hops.snap +++ b/.forge-snapshots/V4Router_ExactOut3Hops.snap @@ -1 +1 @@ -229500 \ No newline at end of file +229821 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactOut3Hops_nativeIn.snap b/.forge-snapshots/V4Router_ExactOut3Hops_nativeIn.snap index 4f905adf0..c1ab906cc 100644 --- a/.forge-snapshots/V4Router_ExactOut3Hops_nativeIn.snap +++ b/.forge-snapshots/V4Router_ExactOut3Hops_nativeIn.snap @@ -1 +1 @@ -225408 \ No newline at end of file +225905 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactOut3Hops_nativeOut.snap b/.forge-snapshots/V4Router_ExactOut3Hops_nativeOut.snap index 91dfd9106..7eb775722 100644 --- a/.forge-snapshots/V4Router_ExactOut3Hops_nativeOut.snap +++ b/.forge-snapshots/V4Router_ExactOut3Hops_nativeOut.snap @@ -1 +1 @@ -220709 \ No newline at end of file +221027 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactOutputSingle.snap b/.forge-snapshots/V4Router_ExactOutputSingle.snap index 5fe4c9aba..5de03712a 100644 --- a/.forge-snapshots/V4Router_ExactOutputSingle.snap +++ b/.forge-snapshots/V4Router_ExactOutputSingle.snap @@ -1 +1 @@ -129107 \ No newline at end of file +129140 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactOutputSingle_nativeIn_sweepETH.snap b/.forge-snapshots/V4Router_ExactOutputSingle_nativeIn_sweepETH.snap index 1a3d45e64..6120543a0 100644 --- a/.forge-snapshots/V4Router_ExactOutputSingle_nativeIn_sweepETH.snap +++ b/.forge-snapshots/V4Router_ExactOutputSingle_nativeIn_sweepETH.snap @@ -1 +1 @@ -121074 \ No newline at end of file +121255 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactOutputSingle_nativeOut.snap b/.forge-snapshots/V4Router_ExactOutputSingle_nativeOut.snap index 07d5b3d2d..b7b122f53 100644 --- a/.forge-snapshots/V4Router_ExactOutputSingle_nativeOut.snap +++ b/.forge-snapshots/V4Router_ExactOutputSingle_nativeOut.snap @@ -1 +1 @@ -116450 \ No newline at end of file +116452 \ No newline at end of file diff --git a/lib/v4-core b/lib/v4-core index 5a7990b12..18b223cab 160000 --- a/lib/v4-core +++ b/lib/v4-core @@ -1 +1 @@ -Subproject commit 5a7990b127314af61998f37b6689a74f9c2db453 +Subproject commit 18b223cab19dc778d9d287a82d29fee3e99162b0 From a6af3b7e3d817e3131061964e199cda413534bbe Mon Sep 17 00:00:00 2001 From: dianakocsis Date: Thu, 19 Sep 2024 16:56:16 -0400 Subject: [PATCH 29/45] re-add files --- src/libraries/PositionConfig.sol | 35 +++++++++++++++++++++++++++ src/libraries/PositionConfigId.sol | 39 ++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+) create mode 100644 src/libraries/PositionConfig.sol create mode 100644 src/libraries/PositionConfigId.sol diff --git a/src/libraries/PositionConfig.sol b/src/libraries/PositionConfig.sol new file mode 100644 index 000000000..007e7bb9d --- /dev/null +++ b/src/libraries/PositionConfig.sol @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity ^0.8.0; + +import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol"; + +// A PositionConfig is the input for creating and modifying a Position in core, whose truncated hash is set per tokenId +struct PositionConfig { + PoolKey poolKey; + int24 tickLower; + int24 tickUpper; +} + +/// @notice Library to calculate the PositionConfigId from the PositionConfig struct +library PositionConfigLibrary { + function toId(PositionConfig calldata config) internal pure returns (bytes32 id) { + // id = keccak256(abi.encodePacked(currency0, currency1, fee, tickSpacing, hooks, tickLower, tickUpper))) >> 1 + assembly ("memory-safe") { + let fmp := mload(0x40) + mstore(add(fmp, 0x34), calldataload(add(config, 0xc0))) // tickUpper: [0x51, 0x54) + mstore(add(fmp, 0x31), calldataload(add(config, 0xa0))) // tickLower: [0x4E, 0x51) + mstore(add(fmp, 0x2E), calldataload(add(config, 0x80))) // hooks: [0x3A, 0x4E) + mstore(add(fmp, 0x1A), calldataload(add(config, 0x60))) // tickSpacing: [0x37, 0x3A) + mstore(add(fmp, 0x17), calldataload(add(config, 0x40))) // fee: [0x34, 0x37) + mstore(add(fmp, 0x14), calldataload(add(config, 0x20))) // currency1: [0x20, 0x34) + mstore(fmp, calldataload(config)) // currency0: [0x0c, 0x20) + + id := shr(1, keccak256(add(fmp, 0x0c), 0x48)) // len is 72 bytes, truncate lower bit of the hash + + // now clean the memory we used + mstore(add(fmp, 0x40), 0) // fmp+0x40 held hooks (14 bytes), tickLower, tickUpper + mstore(add(fmp, 0x20), 0) // fmp+0x20 held currency1, fee, tickSpacing, hooks (6 bytes) + mstore(fmp, 0) // fmp held currency0 + } + } +} diff --git a/src/libraries/PositionConfigId.sol b/src/libraries/PositionConfigId.sol new file mode 100644 index 000000000..4e31c760c --- /dev/null +++ b/src/libraries/PositionConfigId.sol @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity ^0.8.0; + +/// @notice A configId is set per tokenId +/// The lower 255 bits are used to store the truncated hash of the corresponding PositionConfig +/// The upper bit is used to signal if the tokenId has a subscriber +struct PositionConfigId { + bytes32 id; +} + +library PositionConfigIdLibrary { + bytes32 constant MASK_UPPER_BIT = 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; + bytes32 constant DIRTY_UPPER_BIT = 0x8000000000000000000000000000000000000000000000000000000000000000; + + /// @notice returns the truncated hash of the PositionConfig for a given tokenId + function getConfigId(PositionConfigId storage _configId) internal view returns (bytes32 configId) { + configId = _configId.id & MASK_UPPER_BIT; + } + + /// @dev We only set the config on mint, guaranteeing that the most significant bit is unset, so we can just assign the entire 32 bytes to the id. + function setConfigId(PositionConfigId storage _configId, bytes32 configId) internal { + _configId.id = configId; + } + + function setSubscribe(PositionConfigId storage configId) internal { + configId.id |= DIRTY_UPPER_BIT; + } + + function setUnsubscribe(PositionConfigId storage configId) internal { + configId.id &= MASK_UPPER_BIT; + } + + function hasSubscriber(PositionConfigId storage configId) internal view returns (bool subscribed) { + bytes32 _id = configId.id; + assembly ("memory-safe") { + subscribed := shr(255, _id) + } + } +} From 69af3524ea3483bb34cb6598e538418c1dc73eb5 Mon Sep 17 00:00:00 2001 From: dianakocsis Date: Fri, 20 Sep 2024 10:31:18 -0400 Subject: [PATCH 30/45] deploy script --- script/DeployPositionDescriptor.s.sol | 28 +++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 script/DeployPositionDescriptor.s.sol diff --git a/script/DeployPositionDescriptor.s.sol b/script/DeployPositionDescriptor.s.sol new file mode 100644 index 000000000..42c351007 --- /dev/null +++ b/script/DeployPositionDescriptor.s.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.20; + +import "forge-std/console2.sol"; +import "forge-std/Script.sol"; + +import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol"; +import {PositionDescriptor} from "../src/PositionDescriptor.sol"; + +contract DeployPositionDescriptorTest is Script { + function setUp() public {} + + function run(address poolManager, address weth, bytes32 nativeCurrencyLabel) + public + returns (PositionDescriptor positionDescriptor) + { + vm.startBroadcast(); + + positionDescriptor = new PositionDescriptor( + IPoolManager(poolManager), + weth, + nativeCurrencyLabel + ); + console2.log("PositionDescriptor", address(positionDescriptor)); + + vm.stopBroadcast(); + } +} From 04b458e6465d902efbdda39494bc96c38326fb7f Mon Sep 17 00:00:00 2001 From: dianakocsis Date: Fri, 20 Sep 2024 12:02:01 -0400 Subject: [PATCH 31/45] does this work --- .../PositionManager_burn_nonEmpty_native_withClose.snap | 2 +- .../PositionManager_burn_nonEmpty_native_withTakePair.snap | 2 +- .forge-snapshots/PositionManager_burn_nonEmpty_withClose.snap | 2 +- .../PositionManager_burn_nonEmpty_withTakePair.snap | 2 +- .forge-snapshots/PositionManager_collect_native.snap | 2 +- .forge-snapshots/PositionManager_collect_sameRange.snap | 2 +- .forge-snapshots/PositionManager_collect_withClose.snap | 2 +- .forge-snapshots/PositionManager_collect_withTakePair.snap | 2 +- .forge-snapshots/PositionManager_decreaseLiquidity_native.snap | 2 +- .../PositionManager_decreaseLiquidity_withClose.snap | 2 +- .../PositionManager_decreaseLiquidity_withTakePair.snap | 2 +- .forge-snapshots/PositionManager_decrease_burnEmpty.snap | 2 +- .forge-snapshots/PositionManager_decrease_burnEmpty_native.snap | 2 +- .../PositionManager_decrease_sameRange_allLiquidity.snap | 2 +- .forge-snapshots/PositionManager_decrease_take_take.snap | 2 +- .../PositionManager_increaseLiquidity_erc20_withClose.snap | 2 +- .../PositionManager_increaseLiquidity_erc20_withSettlePair.snap | 2 +- .forge-snapshots/PositionManager_increaseLiquidity_native.snap | 2 +- .../PositionManager_increase_autocompoundExcessFeesCredit.snap | 2 +- .../PositionManager_increase_autocompound_clearExcess.snap | 2 +- .forge-snapshots/PositionManager_mint_native.snap | 2 +- .../PositionManager_mint_nativeWithSweep_withClose.snap | 2 +- .../PositionManager_mint_nativeWithSweep_withSettlePair.snap | 2 +- .forge-snapshots/PositionManager_mint_onSameTickLower.snap | 2 +- .forge-snapshots/PositionManager_mint_onSameTickUpper.snap | 2 +- .forge-snapshots/PositionManager_mint_sameRange.snap | 2 +- .../PositionManager_mint_settleWithBalance_sweep.snap | 2 +- .../PositionManager_mint_warmedPool_differentRange.snap | 2 +- .forge-snapshots/PositionManager_mint_withClose.snap | 2 +- .forge-snapshots/PositionManager_mint_withSettlePair.snap | 2 +- .forge-snapshots/PositionManager_multicall_initialize_mint.snap | 2 +- .forge-snapshots/PositionManager_permit.snap | 2 +- .forge-snapshots/PositionManager_permit_secondPosition.snap | 2 +- .forge-snapshots/PositionManager_permit_twice.snap | 2 +- .forge-snapshots/V4Router_ExactIn2Hops.snap | 2 +- .forge-snapshots/V4Router_ExactIn3Hops.snap | 2 +- .forge-snapshots/V4Router_ExactIn3Hops_nativeIn.snap | 2 +- .forge-snapshots/V4Router_ExactOut2Hops.snap | 2 +- .forge-snapshots/V4Router_ExactOut3Hops.snap | 2 +- .forge-snapshots/V4Router_ExactOut3Hops_nativeIn.snap | 2 +- .forge-snapshots/V4Router_ExactOut3Hops_nativeOut.snap | 2 +- test/position-managers/PositionManager.t.sol | 2 +- 42 files changed, 42 insertions(+), 42 deletions(-) diff --git a/.forge-snapshots/PositionManager_burn_nonEmpty_native_withClose.snap b/.forge-snapshots/PositionManager_burn_nonEmpty_native_withClose.snap index 2c42da0a4..75b6dc86d 100644 --- a/.forge-snapshots/PositionManager_burn_nonEmpty_native_withClose.snap +++ b/.forge-snapshots/PositionManager_burn_nonEmpty_native_withClose.snap @@ -1 +1 @@ -125619 \ No newline at end of file +125609 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_burn_nonEmpty_native_withTakePair.snap b/.forge-snapshots/PositionManager_burn_nonEmpty_native_withTakePair.snap index fc3b23085..046f49100 100644 --- a/.forge-snapshots/PositionManager_burn_nonEmpty_native_withTakePair.snap +++ b/.forge-snapshots/PositionManager_burn_nonEmpty_native_withTakePair.snap @@ -1 +1 @@ -125066 \ No newline at end of file +125056 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_burn_nonEmpty_withClose.snap b/.forge-snapshots/PositionManager_burn_nonEmpty_withClose.snap index 0bb10f570..79ad3f252 100644 --- a/.forge-snapshots/PositionManager_burn_nonEmpty_withClose.snap +++ b/.forge-snapshots/PositionManager_burn_nonEmpty_withClose.snap @@ -1 +1 @@ -132472 \ No newline at end of file +132462 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_burn_nonEmpty_withTakePair.snap b/.forge-snapshots/PositionManager_burn_nonEmpty_withTakePair.snap index 853415928..dd001ceb2 100644 --- a/.forge-snapshots/PositionManager_burn_nonEmpty_withTakePair.snap +++ b/.forge-snapshots/PositionManager_burn_nonEmpty_withTakePair.snap @@ -1 +1 @@ -131919 \ No newline at end of file +131909 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_collect_native.snap b/.forge-snapshots/PositionManager_collect_native.snap index 88dbe7495..8aca9b34b 100644 --- a/.forge-snapshots/PositionManager_collect_native.snap +++ b/.forge-snapshots/PositionManager_collect_native.snap @@ -1 +1 @@ -146338 \ No newline at end of file +146326 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_collect_sameRange.snap b/.forge-snapshots/PositionManager_collect_sameRange.snap index 813d39998..ab0425641 100644 --- a/.forge-snapshots/PositionManager_collect_sameRange.snap +++ b/.forge-snapshots/PositionManager_collect_sameRange.snap @@ -1 +1 @@ -154904 \ No newline at end of file +154892 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_collect_withClose.snap b/.forge-snapshots/PositionManager_collect_withClose.snap index 813d39998..ab0425641 100644 --- a/.forge-snapshots/PositionManager_collect_withClose.snap +++ b/.forge-snapshots/PositionManager_collect_withClose.snap @@ -1 +1 @@ -154904 \ No newline at end of file +154892 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_collect_withTakePair.snap b/.forge-snapshots/PositionManager_collect_withTakePair.snap index face41a78..7f09fc960 100644 --- a/.forge-snapshots/PositionManager_collect_withTakePair.snap +++ b/.forge-snapshots/PositionManager_collect_withTakePair.snap @@ -1 +1 @@ -154225 \ No newline at end of file +154213 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_decreaseLiquidity_native.snap b/.forge-snapshots/PositionManager_decreaseLiquidity_native.snap index e8aef4e19..7f9fa86d2 100644 --- a/.forge-snapshots/PositionManager_decreaseLiquidity_native.snap +++ b/.forge-snapshots/PositionManager_decreaseLiquidity_native.snap @@ -1 +1 @@ -112016 \ No newline at end of file +112006 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_decreaseLiquidity_withClose.snap b/.forge-snapshots/PositionManager_decreaseLiquidity_withClose.snap index 5a4e17861..5a422f6f0 100644 --- a/.forge-snapshots/PositionManager_decreaseLiquidity_withClose.snap +++ b/.forge-snapshots/PositionManager_decreaseLiquidity_withClose.snap @@ -1 +1 @@ -119785 \ No newline at end of file +119773 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_decreaseLiquidity_withTakePair.snap b/.forge-snapshots/PositionManager_decreaseLiquidity_withTakePair.snap index 3dcc78ecd..7766b70bb 100644 --- a/.forge-snapshots/PositionManager_decreaseLiquidity_withTakePair.snap +++ b/.forge-snapshots/PositionManager_decreaseLiquidity_withTakePair.snap @@ -1 +1 @@ -119106 \ No newline at end of file +119094 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_decrease_burnEmpty.snap b/.forge-snapshots/PositionManager_decrease_burnEmpty.snap index de5c9570a..83fd9ce44 100644 --- a/.forge-snapshots/PositionManager_decrease_burnEmpty.snap +++ b/.forge-snapshots/PositionManager_decrease_burnEmpty.snap @@ -1 +1 @@ -135268 \ No newline at end of file +135259 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_decrease_burnEmpty_native.snap b/.forge-snapshots/PositionManager_decrease_burnEmpty_native.snap index 2e25cfd75..acb7f1ade 100644 --- a/.forge-snapshots/PositionManager_decrease_burnEmpty_native.snap +++ b/.forge-snapshots/PositionManager_decrease_burnEmpty_native.snap @@ -1 +1 @@ -128416 \ No newline at end of file +128406 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_decrease_sameRange_allLiquidity.snap b/.forge-snapshots/PositionManager_decrease_sameRange_allLiquidity.snap index 0bb10f570..dea300773 100644 --- a/.forge-snapshots/PositionManager_decrease_sameRange_allLiquidity.snap +++ b/.forge-snapshots/PositionManager_decrease_sameRange_allLiquidity.snap @@ -1 +1 @@ -132472 \ No newline at end of file +132460 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_decrease_take_take.snap b/.forge-snapshots/PositionManager_decrease_take_take.snap index 5f819ce0b..ce76c0488 100644 --- a/.forge-snapshots/PositionManager_decrease_take_take.snap +++ b/.forge-snapshots/PositionManager_decrease_take_take.snap @@ -1 +1 @@ -120361 \ No newline at end of file +120349 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_increaseLiquidity_erc20_withClose.snap b/.forge-snapshots/PositionManager_increaseLiquidity_erc20_withClose.snap index c0e0019dc..0c1a9f6f0 100644 --- a/.forge-snapshots/PositionManager_increaseLiquidity_erc20_withClose.snap +++ b/.forge-snapshots/PositionManager_increaseLiquidity_erc20_withClose.snap @@ -1 +1 @@ -159089 \ No newline at end of file +159077 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_increaseLiquidity_erc20_withSettlePair.snap b/.forge-snapshots/PositionManager_increaseLiquidity_erc20_withSettlePair.snap index e5852a38e..87248cf4e 100644 --- a/.forge-snapshots/PositionManager_increaseLiquidity_erc20_withSettlePair.snap +++ b/.forge-snapshots/PositionManager_increaseLiquidity_erc20_withSettlePair.snap @@ -1 +1 @@ -158029 \ No newline at end of file +158017 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_increaseLiquidity_native.snap b/.forge-snapshots/PositionManager_increaseLiquidity_native.snap index 1ab92e5e9..d9bceda2e 100644 --- a/.forge-snapshots/PositionManager_increaseLiquidity_native.snap +++ b/.forge-snapshots/PositionManager_increaseLiquidity_native.snap @@ -1 +1 @@ -140916 \ No newline at end of file +140904 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_increase_autocompoundExcessFeesCredit.snap b/.forge-snapshots/PositionManager_increase_autocompoundExcessFeesCredit.snap index 3f867eb95..8249d48bb 100644 --- a/.forge-snapshots/PositionManager_increase_autocompoundExcessFeesCredit.snap +++ b/.forge-snapshots/PositionManager_increase_autocompoundExcessFeesCredit.snap @@ -1 +1 @@ -177396 \ No newline at end of file +177384 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_increase_autocompound_clearExcess.snap b/.forge-snapshots/PositionManager_increase_autocompound_clearExcess.snap index b9e03ce8f..270700ec7 100644 --- a/.forge-snapshots/PositionManager_increase_autocompound_clearExcess.snap +++ b/.forge-snapshots/PositionManager_increase_autocompound_clearExcess.snap @@ -1 +1 @@ -148072 \ No newline at end of file +148060 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_mint_native.snap b/.forge-snapshots/PositionManager_mint_native.snap index 880c3c396..920e0e3b7 100644 --- a/.forge-snapshots/PositionManager_mint_native.snap +++ b/.forge-snapshots/PositionManager_mint_native.snap @@ -1 +1 @@ -364789 \ No newline at end of file +364765 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_mint_nativeWithSweep_withClose.snap b/.forge-snapshots/PositionManager_mint_nativeWithSweep_withClose.snap index 993ae4910..951aef9d9 100644 --- a/.forge-snapshots/PositionManager_mint_nativeWithSweep_withClose.snap +++ b/.forge-snapshots/PositionManager_mint_nativeWithSweep_withClose.snap @@ -1 +1 @@ -373312 \ No newline at end of file +373288 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_mint_nativeWithSweep_withSettlePair.snap b/.forge-snapshots/PositionManager_mint_nativeWithSweep_withSettlePair.snap index 1caa3586f..83fb2c936 100644 --- a/.forge-snapshots/PositionManager_mint_nativeWithSweep_withSettlePair.snap +++ b/.forge-snapshots/PositionManager_mint_nativeWithSweep_withSettlePair.snap @@ -1 +1 @@ -372535 \ No newline at end of file +372511 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_mint_onSameTickLower.snap b/.forge-snapshots/PositionManager_mint_onSameTickLower.snap index 3e5aa25c8..bd4d6b2d9 100644 --- a/.forge-snapshots/PositionManager_mint_onSameTickLower.snap +++ b/.forge-snapshots/PositionManager_mint_onSameTickLower.snap @@ -1 +1 @@ -317637 \ No newline at end of file +317613 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_mint_onSameTickUpper.snap b/.forge-snapshots/PositionManager_mint_onSameTickUpper.snap index 438b892db..4e64773a1 100644 --- a/.forge-snapshots/PositionManager_mint_onSameTickUpper.snap +++ b/.forge-snapshots/PositionManager_mint_onSameTickUpper.snap @@ -1 +1 @@ -318307 \ No newline at end of file +318283 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_mint_sameRange.snap b/.forge-snapshots/PositionManager_mint_sameRange.snap index 8bded3adb..59ae573aa 100644 --- a/.forge-snapshots/PositionManager_mint_sameRange.snap +++ b/.forge-snapshots/PositionManager_mint_sameRange.snap @@ -1 +1 @@ -243876 \ No newline at end of file +243852 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_mint_settleWithBalance_sweep.snap b/.forge-snapshots/PositionManager_mint_settleWithBalance_sweep.snap index 10d0993c6..00d17c632 100644 --- a/.forge-snapshots/PositionManager_mint_settleWithBalance_sweep.snap +++ b/.forge-snapshots/PositionManager_mint_settleWithBalance_sweep.snap @@ -1 +1 @@ -419068 \ No newline at end of file +419032 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_mint_warmedPool_differentRange.snap b/.forge-snapshots/PositionManager_mint_warmedPool_differentRange.snap index 58152d0f1..a2b376010 100644 --- a/.forge-snapshots/PositionManager_mint_warmedPool_differentRange.snap +++ b/.forge-snapshots/PositionManager_mint_warmedPool_differentRange.snap @@ -1 +1 @@ -323668 \ No newline at end of file +323644 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_mint_withClose.snap b/.forge-snapshots/PositionManager_mint_withClose.snap index e183be069..03de054e0 100644 --- a/.forge-snapshots/PositionManager_mint_withClose.snap +++ b/.forge-snapshots/PositionManager_mint_withClose.snap @@ -1 +1 @@ -420190 \ No newline at end of file +420166 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_mint_withSettlePair.snap b/.forge-snapshots/PositionManager_mint_withSettlePair.snap index 8da1fc4dd..66e3f851a 100644 --- a/.forge-snapshots/PositionManager_mint_withSettlePair.snap +++ b/.forge-snapshots/PositionManager_mint_withSettlePair.snap @@ -1 +1 @@ -419248 \ No newline at end of file +419224 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_multicall_initialize_mint.snap b/.forge-snapshots/PositionManager_multicall_initialize_mint.snap index c16ec80e6..19ab0c1ee 100644 --- a/.forge-snapshots/PositionManager_multicall_initialize_mint.snap +++ b/.forge-snapshots/PositionManager_multicall_initialize_mint.snap @@ -1 +1 @@ -464384 \ No newline at end of file +464348 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_permit.snap b/.forge-snapshots/PositionManager_permit.snap index 227e327e4..ef9de9e8f 100644 --- a/.forge-snapshots/PositionManager_permit.snap +++ b/.forge-snapshots/PositionManager_permit.snap @@ -1 +1 @@ -79076 \ No newline at end of file +79064 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_permit_secondPosition.snap b/.forge-snapshots/PositionManager_permit_secondPosition.snap index 55940c398..31ad61876 100644 --- a/.forge-snapshots/PositionManager_permit_secondPosition.snap +++ b/.forge-snapshots/PositionManager_permit_secondPosition.snap @@ -1 +1 @@ -61964 \ No newline at end of file +61976 \ No newline at end of file diff --git a/.forge-snapshots/PositionManager_permit_twice.snap b/.forge-snapshots/PositionManager_permit_twice.snap index 379f96111..d650ccbd7 100644 --- a/.forge-snapshots/PositionManager_permit_twice.snap +++ b/.forge-snapshots/PositionManager_permit_twice.snap @@ -1 +1 @@ -44852 \ No newline at end of file +44876 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactIn2Hops.snap b/.forge-snapshots/V4Router_ExactIn2Hops.snap index 3b3ffc197..208b10238 100644 --- a/.forge-snapshots/V4Router_ExactIn2Hops.snap +++ b/.forge-snapshots/V4Router_ExactIn2Hops.snap @@ -1 +1 @@ -179724 \ No newline at end of file +185439 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactIn3Hops.snap b/.forge-snapshots/V4Router_ExactIn3Hops.snap index 202b1b328..c44d7bb06 100644 --- a/.forge-snapshots/V4Router_ExactIn3Hops.snap +++ b/.forge-snapshots/V4Router_ExactIn3Hops.snap @@ -1 +1 @@ -228843 \ No newline at end of file +240297 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactIn3Hops_nativeIn.snap b/.forge-snapshots/V4Router_ExactIn3Hops_nativeIn.snap index d3d293a5e..e98fcba77 100644 --- a/.forge-snapshots/V4Router_ExactIn3Hops_nativeIn.snap +++ b/.forge-snapshots/V4Router_ExactIn3Hops_nativeIn.snap @@ -1 +1 @@ -219720 \ No newline at end of file +225435 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactOut2Hops.snap b/.forge-snapshots/V4Router_ExactOut2Hops.snap index 8e5c9c642..abfba6e58 100644 --- a/.forge-snapshots/V4Router_ExactOut2Hops.snap +++ b/.forge-snapshots/V4Router_ExactOut2Hops.snap @@ -1 +1 @@ -179842 \ No newline at end of file +183787 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactOut3Hops.snap b/.forge-snapshots/V4Router_ExactOut3Hops.snap index 9449e5745..cfb3e8279 100644 --- a/.forge-snapshots/V4Router_ExactOut3Hops.snap +++ b/.forge-snapshots/V4Router_ExactOut3Hops.snap @@ -1 +1 @@ -229821 \ No newline at end of file +237735 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactOut3Hops_nativeIn.snap b/.forge-snapshots/V4Router_ExactOut3Hops_nativeIn.snap index c1ab906cc..c30911016 100644 --- a/.forge-snapshots/V4Router_ExactOut3Hops_nativeIn.snap +++ b/.forge-snapshots/V4Router_ExactOut3Hops_nativeIn.snap @@ -1 +1 @@ -225905 \ No newline at end of file +229850 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactOut3Hops_nativeOut.snap b/.forge-snapshots/V4Router_ExactOut3Hops_nativeOut.snap index 7eb775722..575b9ea9f 100644 --- a/.forge-snapshots/V4Router_ExactOut3Hops_nativeOut.snap +++ b/.forge-snapshots/V4Router_ExactOut3Hops_nativeOut.snap @@ -1 +1 @@ -221027 \ No newline at end of file +217090 \ No newline at end of file diff --git a/test/position-managers/PositionManager.t.sol b/test/position-managers/PositionManager.t.sol index c0c0b96dd..2804468d8 100644 --- a/test/position-managers/PositionManager.t.sol +++ b/test/position-managers/PositionManager.t.sol @@ -95,7 +95,7 @@ contract PositionManagerTest is Test, PosmTestSetup, LiquidityFuzzers { (key, poolId) = initPool(currency0, currency1, IHooks(address(0)), 3000, SQRT_PRICE_1_1, ZERO_BYTES); // Try to add liquidity at that range, but the token reenters posm - PositionConfig memory config = PositionConfig({poolKey: key, tickLower: 0, tickUpper: 60}); + PositionConfig memory config = PositionConfig({poolKey: key, tickLower: -int24(key.tickSpacing), tickUpper: int24(key.tickSpacing)}); bytes memory calls = getMintEncoded(config, 1e18, ActionConstants.MSG_SENDER, ""); // Permit2.transferFrom does not bubble the ContractLocked error and instead reverts with its own error From 9ff5137ee32a5ea02bbc30608a4746340321f240 Mon Sep 17 00:00:00 2001 From: dianakocsis Date: Fri, 20 Sep 2024 12:02:47 -0400 Subject: [PATCH 32/45] format --- script/DeployPositionDescriptor.s.sol | 6 +----- test/position-managers/PositionManager.t.sol | 3 ++- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/script/DeployPositionDescriptor.s.sol b/script/DeployPositionDescriptor.s.sol index 42c351007..9d13b39c9 100644 --- a/script/DeployPositionDescriptor.s.sol +++ b/script/DeployPositionDescriptor.s.sol @@ -16,11 +16,7 @@ contract DeployPositionDescriptorTest is Script { { vm.startBroadcast(); - positionDescriptor = new PositionDescriptor( - IPoolManager(poolManager), - weth, - nativeCurrencyLabel - ); + positionDescriptor = new PositionDescriptor(IPoolManager(poolManager), weth, nativeCurrencyLabel); console2.log("PositionDescriptor", address(positionDescriptor)); vm.stopBroadcast(); diff --git a/test/position-managers/PositionManager.t.sol b/test/position-managers/PositionManager.t.sol index 2804468d8..5762768b7 100644 --- a/test/position-managers/PositionManager.t.sol +++ b/test/position-managers/PositionManager.t.sol @@ -95,7 +95,8 @@ contract PositionManagerTest is Test, PosmTestSetup, LiquidityFuzzers { (key, poolId) = initPool(currency0, currency1, IHooks(address(0)), 3000, SQRT_PRICE_1_1, ZERO_BYTES); // Try to add liquidity at that range, but the token reenters posm - PositionConfig memory config = PositionConfig({poolKey: key, tickLower: -int24(key.tickSpacing), tickUpper: int24(key.tickSpacing)}); + PositionConfig memory config = + PositionConfig({poolKey: key, tickLower: -int24(key.tickSpacing), tickUpper: int24(key.tickSpacing)}); bytes memory calls = getMintEncoded(config, 1e18, ActionConstants.MSG_SENDER, ""); // Permit2.transferFrom does not bubble the ContractLocked error and instead reverts with its own error From 4542c5929d9b859d19c6e7ca3022002bcfd42c20 Mon Sep 17 00:00:00 2001 From: dianakocsis Date: Mon, 23 Sep 2024 16:46:59 -0400 Subject: [PATCH 33/45] pass in string directly --- script/DeployPositionDescriptor.s.sol | 2 +- src/PositionDescriptor.sol | 25 +++++-------------------- test/PositionDescriptor.t.sol | 8 ++------ 3 files changed, 8 insertions(+), 27 deletions(-) diff --git a/script/DeployPositionDescriptor.s.sol b/script/DeployPositionDescriptor.s.sol index 9d13b39c9..9599d7eae 100644 --- a/script/DeployPositionDescriptor.s.sol +++ b/script/DeployPositionDescriptor.s.sol @@ -10,7 +10,7 @@ import {PositionDescriptor} from "../src/PositionDescriptor.sol"; contract DeployPositionDescriptorTest is Script { function setUp() public {} - function run(address poolManager, address weth, bytes32 nativeCurrencyLabel) + function run(address poolManager, address weth, string memory nativeCurrencyLabel) public returns (PositionDescriptor positionDescriptor) { diff --git a/src/PositionDescriptor.sol b/src/PositionDescriptor.sol index 522b449aa..4b318e164 100644 --- a/src/PositionDescriptor.sol +++ b/src/PositionDescriptor.sol @@ -31,29 +31,14 @@ contract PositionDescriptor is IPositionDescriptor { address private constant WBTC = 0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599; address public immutable WETH9; - /// @dev A null-terminated string - bytes32 public immutable nativeCurrencyLabelBytes; + string public nativeCurrencyLabel; IPoolManager public immutable poolManager; - constructor(IPoolManager _poolManager, address _WETH9, bytes32 _nativeCurrencyLabel) { + constructor(IPoolManager _poolManager, address _WETH9, string memory _nativeCurrencyLabel) { poolManager = _poolManager; WETH9 = _WETH9; - nativeCurrencyLabelBytes = _nativeCurrencyLabel; - } - - /// @notice Takes the native currency label in bytes32 and returns it as a string - /// @return nativeCurrencyLabel The native currency label - function nativeCurrencyLabel() public view returns (string memory) { - uint256 len = 0; - while (len < 32 && nativeCurrencyLabelBytes[len] != 0) { - len++; - } - bytes memory b = new bytes(len); - for (uint256 i = 0; i < len; i++) { - b[i] = nativeCurrencyLabelBytes[i]; - } - return string(b); + nativeCurrencyLabel = _nativeCurrencyLabel; } /// @inheritdoc IPositionDescriptor @@ -80,10 +65,10 @@ contract PositionDescriptor is IPositionDescriptor { quoteCurrency: quoteCurrency, baseCurrency: baseCurrency, quoteCurrencySymbol: quoteCurrency.isAddressZero() - ? nativeCurrencyLabel() + ? nativeCurrencyLabel : SafeERC20Namer.tokenSymbol(Currency.unwrap(quoteCurrency)), baseCurrencySymbol: baseCurrency.isAddressZero() - ? nativeCurrencyLabel() + ? nativeCurrencyLabel : SafeERC20Namer.tokenSymbol(Currency.unwrap(baseCurrency)), quoteCurrencyDecimals: quoteCurrency.isAddressZero() ? 18 diff --git a/test/PositionDescriptor.t.sol b/test/PositionDescriptor.t.sol index 9bae6787f..9ba6f54a9 100644 --- a/test/PositionDescriptor.t.sol +++ b/test/PositionDescriptor.t.sol @@ -21,7 +21,7 @@ contract PositionDescriptorTest is Test, PosmTestSetup { address public USDT = 0xdAC17F958D2ee523a2206206994597C13D831ec7; address public TBTC = 0x8dAEBADE922dF735c38C80C7eBD708Af50815fAa; address public WBTC = 0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599; - bytes32 public nativeCurrencyLabel = "ETH"; + string public nativeCurrencyLabel = "ETH"; struct Token { string description; @@ -39,11 +39,7 @@ contract PositionDescriptorTest is Test, PosmTestSetup { function test_setup_succeeds() public view { assertEq(address(positionDescriptor.poolManager()), address(manager)); assertEq(positionDescriptor.WETH9(), WETH9); - assertEq(positionDescriptor.nativeCurrencyLabelBytes(), nativeCurrencyLabel); - } - - function test_nativeCurrencyLabel_succeeds() public view { - assertEq(positionDescriptor.nativeCurrencyLabel(), "ETH"); + assertEq(positionDescriptor.nativeCurrencyLabel(), nativeCurrencyLabel); } function test_currencyRatioPriority_mainnet_succeeds() public { From d0b81fe8bd0c206ce550a4ca1f1dcb30f26cfd86 Mon Sep 17 00:00:00 2001 From: dianakocsis Date: Mon, 23 Sep 2024 18:08:00 -0400 Subject: [PATCH 34/45] remove extra check --- src/libraries/SafeERC20Namer.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/SafeERC20Namer.sol b/src/libraries/SafeERC20Namer.sol index 04bd63a13..47764d2cf 100644 --- a/src/libraries/SafeERC20Namer.sol +++ b/src/libraries/SafeERC20Namer.sol @@ -38,7 +38,7 @@ library SafeERC20Namer { function callAndParseStringReturn(address token, bytes4 selector) private view returns (string memory) { (bool success, bytes memory data) = token.staticcall(abi.encodeWithSelector(selector)); // if not implemented, or returns empty data, return empty string - if (!success || data.length == 0) { + if (!success) { return ""; } // bytes32 data always has length 32 From 4d53d2a9c7f84d777d683d1efd4abe5d309eef3b Mon Sep 17 00:00:00 2001 From: dianakocsis Date: Tue, 24 Sep 2024 12:04:48 -0400 Subject: [PATCH 35/45] comments, selector, and reference --- src/PositionDescriptor.sol | 1 + src/libraries/AddressStringUtil.sol | 1 + src/libraries/SafeERC20Namer.sol | 7 ++++--- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/PositionDescriptor.sol b/src/PositionDescriptor.sol index 4b318e164..b7ee4d844 100644 --- a/src/PositionDescriptor.sol +++ b/src/PositionDescriptor.sol @@ -51,6 +51,7 @@ contract PositionDescriptor is IPositionDescriptor { (PoolKey memory poolKey, PositionInfo positionInfo) = positionManager.getPoolAndPositionInfo(tokenId); (, int24 tick,,) = poolManager.getSlot0(poolKey.toId()); + // If possible, flip currencies to get the larger currency as the base currency, so that the price (quote/base) is more readable // flip if currency0 priority is greater than currency1 priority bool _flipRatio = flipRatio(Currency.unwrap(poolKey.currency0), Currency.unwrap(poolKey.currency1)); diff --git a/src/libraries/AddressStringUtil.sol b/src/libraries/AddressStringUtil.sol index bb6aff303..b80404c83 100644 --- a/src/libraries/AddressStringUtil.sol +++ b/src/libraries/AddressStringUtil.sol @@ -3,6 +3,7 @@ pragma solidity ^0.8.20; /// @title AddressStringUtil /// @notice provides utility functions for converting addresses to strings +/// @dev Reference: https://github.com/Uniswap/solidity-lib/blob/master/contracts/libraries/AddressStringUtil.sol library AddressStringUtil { /// @notice Converts an address to the uppercase hex string, extracting only len bytes (up to 20, multiple of 2) /// @param addr the address to convert diff --git a/src/libraries/SafeERC20Namer.sol b/src/libraries/SafeERC20Namer.sol index 47764d2cf..c1b070704 100644 --- a/src/libraries/SafeERC20Namer.sol +++ b/src/libraries/SafeERC20Namer.sol @@ -1,11 +1,13 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.20; +import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import "./AddressStringUtil.sol"; /// @title SafeERC20Namer /// @notice produces token descriptors from inconsistent or absent ERC20 symbol implementations that can return string or bytes32 /// this library will always produce a string symbol to represent the token +/// @dev Reference: https://github.com/Uniswap/solidity-lib/blob/master/contracts/libraries/SafeERC20Namer.sol library SafeERC20Namer { function bytes32ToString(bytes32 x) private pure returns (string memory) { bytes memory bytesString = new bytes(32); @@ -37,7 +39,7 @@ library SafeERC20Namer { /// @return the token symbol function callAndParseStringReturn(address token, bytes4 selector) private view returns (string memory) { (bool success, bytes memory data) = token.staticcall(abi.encodeWithSelector(selector)); - // if not implemented, or returns empty data, return empty string + // if not implemented, return empty string if (!success) { return ""; } @@ -55,8 +57,7 @@ library SafeERC20Namer { /// @param token the token address /// @return the token symbol function tokenSymbol(address token) internal view returns (string memory) { - // 0x95d89b41 = bytes4(keccak256("symbol()")) - string memory symbol = callAndParseStringReturn(token, 0x95d89b41); + string memory symbol = callAndParseStringReturn(token, IERC20Metadata.symbol.selector); if (bytes(symbol).length == 0) { // fallback to 6 uppercase hex of address return addressToSymbol(token); From a4892e3e1033cbe47b3352cfafb72017657644ac Mon Sep 17 00:00:00 2001 From: dianakocsis Date: Thu, 26 Sep 2024 15:15:51 -0400 Subject: [PATCH 36/45] some changes --- src/libraries/AddressStringUtil.sol | 8 ++++++-- src/libraries/CurrencyRatioSortOrder.sol | 2 +- src/libraries/Descriptor.sol | 15 ++++++++------- src/libraries/HexStrings.sol | 3 ++- src/libraries/SVG.sol | 5 +++-- src/libraries/SafeERC20Namer.sol | 2 +- 6 files changed, 21 insertions(+), 14 deletions(-) diff --git a/src/libraries/AddressStringUtil.sol b/src/libraries/AddressStringUtil.sol index b80404c83..999bc2d95 100644 --- a/src/libraries/AddressStringUtil.sol +++ b/src/libraries/AddressStringUtil.sol @@ -1,16 +1,20 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.20; +pragma solidity ^0.8.0; /// @title AddressStringUtil /// @notice provides utility functions for converting addresses to strings /// @dev Reference: https://github.com/Uniswap/solidity-lib/blob/master/contracts/libraries/AddressStringUtil.sol library AddressStringUtil { + error InvalidAddressLength(uint256 len); + /// @notice Converts an address to the uppercase hex string, extracting only len bytes (up to 20, multiple of 2) /// @param addr the address to convert /// @param len the number of bytes to extract /// @return the hex string function toAsciiString(address addr, uint256 len) internal pure returns (string memory) { - require(len % 2 == 0 && len > 0 && len <= 40, "AddressStringUtil: INVALID_LEN"); + if (!(len % 2 == 0 && len > 0 && len <= 40)) { + revert InvalidAddressLength(len); + } bytes memory s = new bytes(len); uint256 addrNum = uint256(uint160(addr)); diff --git a/src/libraries/CurrencyRatioSortOrder.sol b/src/libraries/CurrencyRatioSortOrder.sol index 857d74484..6114c1f96 100644 --- a/src/libraries/CurrencyRatioSortOrder.sol +++ b/src/libraries/CurrencyRatioSortOrder.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.20; +pragma solidity ^0.8.0; library CurrencyRatioSortOrder { int256 constant NUMERATOR_MOST = 300; diff --git a/src/libraries/Descriptor.sol b/src/libraries/Descriptor.sol index 0f04eb2c5..9af532188 100644 --- a/src/libraries/Descriptor.sol +++ b/src/libraries/Descriptor.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.20; +pragma solidity ^0.8.0; import {Currency, CurrencyLibrary} from "@uniswap/v4-core/src/types/Currency.sol"; import {IHooks} from "@uniswap/v4-core/src/interfaces/IHooks.sol"; @@ -12,6 +12,7 @@ import {HexStrings} from "./HexStrings.sol"; /// @title Descriptor /// @notice Describes NFT token positions +/// @dev Reference: https://github.com/Uniswap/v3-periphery/blob/main/contracts/libraries/NFTDescriptor.sol library Descriptor { using TickMath for int24; using Strings for uint256; @@ -37,7 +38,7 @@ library Descriptor { address hooks; } - /// @notice Constructs the token URI for a Uniswap V4 NFT + /// @notice Constructs the token URI for a Uniswap v4 NFT /// @param params Parameters needed to construct the token URI /// @return The token URI as a string function constructTokenURI(ConstructTokenURIParams calldata params) public pure returns (string memory) { @@ -106,7 +107,7 @@ library Descriptor { return symbol; } - /// @notice Generates the first part of the description for a Uniswap V4 NFT + /// @notice Generates the first part of the description for a Uniswap v4 NFT /// @param quoteCurrencySymbol The symbol of the quote currency /// @param baseCurrencySymbol The symbol of the base currency /// @param poolManager The address of the pool manager @@ -138,7 +139,7 @@ library Descriptor { ); } - /// @notice Generates the second part of the description for a Uniswap V4 NFTs + /// @notice Generates the second part of the description for a Uniswap v4 NFTs /// @param tokenId The token ID /// @param baseCurrencySymbol The symbol of the base currency /// @param baseCurrency The address of the base currency @@ -169,7 +170,7 @@ library Descriptor { ); } - /// @notice Generates the name for a Uniswap V4 NFT + /// @notice Generates the name for a Uniswap v4 NFT /// @param params Parameters needed to generate the name /// @param feeTier The fee tier of the pool /// @return The name of the NFT @@ -452,7 +453,7 @@ library Descriptor { return (uint256(uint160(addr))).toHexString(20); } - /// @notice Generates the SVG image for a Uniswap V4 NFT + /// @notice Generates the SVG image for a Uniswap v4 NFT /// @param params Parameters needed to generate the SVG image /// @return svg The SVG image as a string function generateSVGImage(ConstructTokenURIParams memory params) internal pure returns (string memory svg) { @@ -503,7 +504,7 @@ library Descriptor { pure returns (string memory) { - return (n - inMn * (outMx - outMn) / (inMx - inMn) + outMn).toString(); + return ((n - inMn) * (outMx - outMn) / (inMx - inMn) + outMn).toString(); } function currencyToColorHex(uint256 currency, uint256 offset) internal pure returns (string memory str) { diff --git a/src/libraries/HexStrings.sol b/src/libraries/HexStrings.sol index 0be747d30..d5a323875 100644 --- a/src/libraries/HexStrings.sol +++ b/src/libraries/HexStrings.sol @@ -1,8 +1,9 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.20; +pragma solidity ^0.8.0; /// @title HexStrings /// @notice Provides function for converting numbers to hexadecimal strings +/// @dev Reference: https://github.com/Uniswap/v3-periphery/blob/main/contracts/libraries/HexStrings.sol library HexStrings { bytes16 internal constant ALPHABET = "0123456789abcdef"; diff --git a/src/libraries/SVG.sol b/src/libraries/SVG.sol index b12bd2855..9e860ea23 100644 --- a/src/libraries/SVG.sol +++ b/src/libraries/SVG.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.20; +pragma solidity ^0.8.0; import {Currency, CurrencyLibrary} from "@uniswap/v4-core/src/types/Currency.sol"; import {IHooks} from "@uniswap/v4-core/src/interfaces/IHooks.sol"; @@ -9,6 +9,7 @@ import {Base64} from "@openzeppelin/contracts/utils/Base64.sol"; /// @title SVG /// @notice Provides a function for generating an SVG associated with a Uniswap NFT +/// @dev Reference: https://github.com/Uniswap/v3-periphery/blob/main/contracts/libraries/NFTSVG.sol library SVG { using Strings for uint256; @@ -48,7 +49,7 @@ library SVG { string y3; } - /// @notice Generate the SVG associated with a Uniswap V4 NFT + /// @notice Generate the SVG associated with a Uniswap v4 NFT /// @param params The SVGParams struct containing the parameters for the SVG /// @return svg The SVG string associated with the NFT function generateSVG(SVGParams memory params) internal pure returns (string memory svg) { diff --git a/src/libraries/SafeERC20Namer.sol b/src/libraries/SafeERC20Namer.sol index c1b070704..0e8417a7f 100644 --- a/src/libraries/SafeERC20Namer.sol +++ b/src/libraries/SafeERC20Namer.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.20; +pragma solidity ^0.8.0; import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import "./AddressStringUtil.sol"; From c08ccd934d09e4112b092c93ac646851d8d1b8f3 Mon Sep 17 00:00:00 2001 From: dianakocsis Date: Thu, 26 Sep 2024 18:02:34 -0400 Subject: [PATCH 37/45] more fixes --- .forge-snapshots/PositionManager_permit.snap | 2 +- src/PositionManager.sol | 2 +- src/V4Router.sol | 2 +- src/libraries/CurrencyRatioSortOrder.sol | 4 ++++ src/libraries/Descriptor.sol | 24 ++++++++++++-------- src/libraries/SVG.sol | 6 ----- test/position-managers/Permit.t.sol | 2 +- 7 files changed, 22 insertions(+), 20 deletions(-) diff --git a/.forge-snapshots/PositionManager_permit.snap b/.forge-snapshots/PositionManager_permit.snap index ef9de9e8f..227e327e4 100644 --- a/.forge-snapshots/PositionManager_permit.snap +++ b/.forge-snapshots/PositionManager_permit.snap @@ -1 +1 @@ -79064 \ No newline at end of file +79076 \ No newline at end of file diff --git a/src/PositionManager.sol b/src/PositionManager.sol index de5a2f76d..d5721e346 100644 --- a/src/PositionManager.sol +++ b/src/PositionManager.sol @@ -130,7 +130,7 @@ contract PositionManager is ) BaseActionsRouter(_poolManager) Permit2Forwarder(_permit2) - ERC721Permit_v4("Uniswap V4 Positions NFT", "UNI-V4-POSM") + ERC721Permit_v4("Uniswap v4 Positions NFT", "UNI-V4-POSM") Notifier(_unsubscribeGasLimit) { tokenDescriptor = _tokenDescriptor; diff --git a/src/V4Router.sol b/src/V4Router.sol index b33d8d904..0aaf77155 100644 --- a/src/V4Router.sol +++ b/src/V4Router.sol @@ -18,7 +18,7 @@ import {Actions} from "./libraries/Actions.sol"; import {ActionConstants} from "./libraries/ActionConstants.sol"; /// @title UniswapV4Router -/// @notice Abstract contract that contains all internal logic needed for routing through Uniswap V4 pools +/// @notice Abstract contract that contains all internal logic needed for routing through Uniswap v4 pools /// @dev the entry point to executing actions in this contract is calling `BaseActionsRouter._executeActions` /// An inheriting contract should call _executeActions at the point that they wish actions to be executed abstract contract V4Router is IV4Router, BaseActionsRouter, DeltaResolver { diff --git a/src/libraries/CurrencyRatioSortOrder.sol b/src/libraries/CurrencyRatioSortOrder.sol index 6114c1f96..d9d9d4426 100644 --- a/src/libraries/CurrencyRatioSortOrder.sol +++ b/src/libraries/CurrencyRatioSortOrder.sol @@ -1,6 +1,10 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.0; +/// @title CurrencyRatioSortOrder +/// @notice Provides constants for sorting currencies when displaying price ratios +/// Currencies given larger values will be in the numerator of the price ratio +/// @dev Reference: https://github.com/Uniswap/v3-periphery/blob/main/contracts/libraries/TokenRatioSortOrder.sol library CurrencyRatioSortOrder { int256 constant NUMERATOR_MOST = 300; int256 constant NUMERATOR_MORE = 200; diff --git a/src/libraries/Descriptor.sol b/src/libraries/Descriptor.sol index 9af532188..831fd81aa 100644 --- a/src/libraries/Descriptor.sol +++ b/src/libraries/Descriptor.sol @@ -5,6 +5,7 @@ import {Currency, CurrencyLibrary} from "@uniswap/v4-core/src/types/Currency.sol import {IHooks} from "@uniswap/v4-core/src/interfaces/IHooks.sol"; import {TickMath} from "@uniswap/v4-core/src/libraries/TickMath.sol"; import {FullMath} from "@uniswap/v4-core/src/libraries/FullMath.sol"; +import {LPFeeLibrary} from "@uniswap/v4-core/src/libraries/LPFeeLibrary.sol"; import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; import {Base64} from "@openzeppelin/contracts/utils/Base64.sol"; import {SVG} from "./SVG.sol"; @@ -46,12 +47,12 @@ library Descriptor { string memory descriptionPartOne = generateDescriptionPartOne( escapeQuotes(params.quoteCurrencySymbol), escapeQuotes(params.baseCurrencySymbol), - addressToString(params.poolManager), - addressToString(Currency.unwrap(params.quoteCurrency)) + addressToString(params.poolManager) ); string memory descriptionPartTwo = generateDescriptionPartTwo( params.tokenId.toString(), escapeQuotes(params.baseCurrencySymbol), + addressToString(Currency.unwrap(params.quoteCurrency)), addressToString(Currency.unwrap(params.baseCurrency)), addressToString(params.hooks), feeToPercentString(params.fee) @@ -111,18 +112,16 @@ library Descriptor { /// @param quoteCurrencySymbol The symbol of the quote currency /// @param baseCurrencySymbol The symbol of the base currency /// @param poolManager The address of the pool manager - /// @param quoteCurrency The address of the quote currency /// @return The first part of the description function generateDescriptionPartOne( string memory quoteCurrencySymbol, string memory baseCurrencySymbol, - string memory poolManager, - string memory quoteCurrency + string memory poolManager ) private pure returns (string memory) { // displays quote currency first, then base currency return string( abi.encodePacked( - "This NFT represents a liquidity position in a Uniswap V4 ", + "This NFT represents a liquidity position in a Uniswap v4 ", quoteCurrencySymbol, "-", baseCurrencySymbol, @@ -131,10 +130,7 @@ library Descriptor { "\\nPool Manager Address: ", poolManager, "\\n", - quoteCurrencySymbol, - " Address: ", - quoteCurrency, - "\\n" + quoteCurrencySymbol ) ); } @@ -142,6 +138,7 @@ library Descriptor { /// @notice Generates the second part of the description for a Uniswap v4 NFTs /// @param tokenId The token ID /// @param baseCurrencySymbol The symbol of the base currency + /// @param quoteCurrency The address of the quote currency /// @param baseCurrency The address of the base currency /// @param hooks The address of the hooks contract /// @param feeTier The fee tier of the pool @@ -149,12 +146,16 @@ library Descriptor { function generateDescriptionPartTwo( string memory tokenId, string memory baseCurrencySymbol, + string memory quoteCurrency, string memory baseCurrency, string memory hooks, string memory feeTier ) private pure returns (string memory) { return string( abi.encodePacked( + ' Address: ', + quoteCurrency, + "\\n", baseCurrencySymbol, " Address: ", baseCurrency, @@ -402,6 +403,9 @@ library Descriptor { /// @param fee fee amount /// @return fee as a decimal string with percent sign function feeToPercentString(uint24 fee) internal pure returns (string memory) { + if (fee == LPFeeLibrary.DYNAMIC_FEE_FLAG) { + return "Dynamic"; + } if (fee == 0) { return "0%"; } diff --git a/src/libraries/SVG.sol b/src/libraries/SVG.sol index 9e860ea23..24d712828 100644 --- a/src/libraries/SVG.sol +++ b/src/libraries/SVG.sol @@ -53,12 +53,6 @@ library SVG { /// @param params The SVGParams struct containing the parameters for the SVG /// @return svg The SVG string associated with the NFT function generateSVG(SVGParams memory params) internal pure returns (string memory svg) { - /* - address: "0xe8ab59d3bcde16a29912de83a90eb39628cfc163", - msg: "Forged in SVG for Uniswap in 2021 by 0xe8ab59d3bcde16a29912de83a90eb39628cfc163", - sig: "0x2df0e99d9cbfec33a705d83f75666d98b22dea7c1af412c584f7d626d83f02875993df740dc87563b9c73378f8462426da572d7989de88079a382ad96c57b68d1b", - version: "2" - */ return string( abi.encodePacked( generateSVGDefs(params), diff --git a/test/position-managers/Permit.t.sol b/test/position-managers/Permit.t.sol index ea3a9c6d9..392fc17e6 100644 --- a/test/position-managers/Permit.t.sol +++ b/test/position-managers/Permit.t.sol @@ -64,7 +64,7 @@ contract PermitTest is Test, PosmTestSetup { keccak256( abi.encode( keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)"), - keccak256("Uniswap V4 Positions NFT"), // storage is private on EIP712.sol so we need to hardcode these + keccak256("Uniswap v4 Positions NFT"), // storage is private on EIP712.sol so we need to hardcode these block.chainid, address(lpm) ) From 40904b06506edd15cb2e4e3d20689fa69cfb3205 Mon Sep 17 00:00:00 2001 From: dianakocsis Date: Thu, 26 Sep 2024 18:03:39 -0400 Subject: [PATCH 38/45] format --- src/libraries/Descriptor.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/Descriptor.sol b/src/libraries/Descriptor.sol index 831fd81aa..43149f516 100644 --- a/src/libraries/Descriptor.sol +++ b/src/libraries/Descriptor.sol @@ -153,7 +153,7 @@ library Descriptor { ) private pure returns (string memory) { return string( abi.encodePacked( - ' Address: ', + " Address: ", quoteCurrency, "\\n", baseCurrencySymbol, From 8a6033ed1026864cbf3278b47ce09102bd2d4551 Mon Sep 17 00:00:00 2001 From: dianakocsis Date: Mon, 30 Sep 2024 22:25:34 -0400 Subject: [PATCH 39/45] more review changes --- script/DeployPositionDescriptor.s.sol | 24 ---------- script/DeployPosm.s.sol | 17 +++++-- src/PositionDescriptor.sol | 10 ++-- src/libraries/Descriptor.sol | 3 +- src/libraries/SafeERC20Namer.sol | 67 --------------------------- 5 files changed, 19 insertions(+), 102 deletions(-) delete mode 100644 script/DeployPositionDescriptor.s.sol delete mode 100644 src/libraries/SafeERC20Namer.sol diff --git a/script/DeployPositionDescriptor.s.sol b/script/DeployPositionDescriptor.s.sol deleted file mode 100644 index 9599d7eae..000000000 --- a/script/DeployPositionDescriptor.s.sol +++ /dev/null @@ -1,24 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.20; - -import "forge-std/console2.sol"; -import "forge-std/Script.sol"; - -import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol"; -import {PositionDescriptor} from "../src/PositionDescriptor.sol"; - -contract DeployPositionDescriptorTest is Script { - function setUp() public {} - - function run(address poolManager, address weth, string memory nativeCurrencyLabel) - public - returns (PositionDescriptor positionDescriptor) - { - vm.startBroadcast(); - - positionDescriptor = new PositionDescriptor(IPoolManager(poolManager), weth, nativeCurrencyLabel); - console2.log("PositionDescriptor", address(positionDescriptor)); - - vm.stopBroadcast(); - } -} diff --git a/script/DeployPosm.s.sol b/script/DeployPosm.s.sol index e3f15bf44..4233208ce 100644 --- a/script/DeployPosm.s.sol +++ b/script/DeployPosm.s.sol @@ -9,21 +9,28 @@ import {StateView} from "../src/lens/StateView.sol"; 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"; contract DeployPosmTest is Script { function setUp() public {} - function run(address poolManager, address permit2, uint256 unsubscribeGasLimit, address positionDescriptor) - public - returns (PositionManager posm) - { + function run( + address poolManager, + address permit2, + uint256 unsubscribeGasLimit, + address weth, + string memory nativeCurrencyLabel + ) public returns (PositionDescriptor positionDescriptor, PositionManager posm) { vm.startBroadcast(); + positionDescriptor = new PositionDescriptor(IPoolManager(poolManager), weth, nativeCurrencyLabel); + console2.log("PositionDescriptor", address(positionDescriptor)); + posm = new PositionManager{salt: hex"03"}( IPoolManager(poolManager), IAllowanceTransfer(permit2), unsubscribeGasLimit, - IPositionDescriptor(positionDescriptor) + IPositionDescriptor(address(positionDescriptor)) ); console2.log("PositionManager", address(posm)); diff --git a/src/PositionDescriptor.sol b/src/PositionDescriptor.sol index b7ee4d844..c3dc3a6c6 100644 --- a/src/PositionDescriptor.sol +++ b/src/PositionDescriptor.sol @@ -13,7 +13,7 @@ import {IPositionDescriptor} from "./interfaces/IPositionDescriptor.sol"; import {PositionInfo, PositionInfoLibrary} from "./libraries/PositionInfoLibrary.sol"; import {Descriptor} from "./libraries/Descriptor.sol"; import {CurrencyRatioSortOrder} from "./libraries/CurrencyRatioSortOrder.sol"; -import {SafeERC20Namer} from "./libraries/SafeERC20Namer.sol"; +import {SafeERC20Metadata} from "./libraries/SafeERC20Metadata.sol"; /// @title Describes NFT token positions /// @notice Produces a string containing the data URI for a JSON metadata string @@ -67,16 +67,16 @@ contract PositionDescriptor is IPositionDescriptor { baseCurrency: baseCurrency, quoteCurrencySymbol: quoteCurrency.isAddressZero() ? nativeCurrencyLabel - : SafeERC20Namer.tokenSymbol(Currency.unwrap(quoteCurrency)), + : SafeERC20Metadata.tokenSymbol(Currency.unwrap(quoteCurrency)), baseCurrencySymbol: baseCurrency.isAddressZero() ? nativeCurrencyLabel - : SafeERC20Namer.tokenSymbol(Currency.unwrap(baseCurrency)), + : SafeERC20Metadata.tokenSymbol(Currency.unwrap(baseCurrency)), quoteCurrencyDecimals: quoteCurrency.isAddressZero() ? 18 - : IERC20Metadata(Currency.unwrap(quoteCurrency)).decimals(), + : SafeERC20Metadata.tokenDecimals(Currency.unwrap(quoteCurrency)), baseCurrencyDecimals: baseCurrency.isAddressZero() ? 18 - : IERC20Metadata(Currency.unwrap(baseCurrency)).decimals(), + : SafeERC20Metadata.tokenDecimals(Currency.unwrap(baseCurrency)), flipRatio: _flipRatio, tickLower: positionInfo.tickLower(), tickUpper: positionInfo.tickUpper(), diff --git a/src/libraries/Descriptor.sol b/src/libraries/Descriptor.sol index 43149f516..b5da50ee5 100644 --- a/src/libraries/Descriptor.sol +++ b/src/libraries/Descriptor.sol @@ -18,6 +18,7 @@ library Descriptor { using TickMath for int24; using Strings for uint256; using HexStrings for uint256; + using LPFeeLibrary for uint24; uint256 constant sqrt10X128 = 1076067327063303206878105757264492625226; @@ -403,7 +404,7 @@ library Descriptor { /// @param fee fee amount /// @return fee as a decimal string with percent sign function feeToPercentString(uint24 fee) internal pure returns (string memory) { - if (fee == LPFeeLibrary.DYNAMIC_FEE_FLAG) { + if (fee.isDynamicFee()) { return "Dynamic"; } if (fee == 0) { diff --git a/src/libraries/SafeERC20Namer.sol b/src/libraries/SafeERC20Namer.sol deleted file mode 100644 index 0e8417a7f..000000000 --- a/src/libraries/SafeERC20Namer.sol +++ /dev/null @@ -1,67 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.0; - -import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; -import "./AddressStringUtil.sol"; - -/// @title SafeERC20Namer -/// @notice produces token descriptors from inconsistent or absent ERC20 symbol implementations that can return string or bytes32 -/// this library will always produce a string symbol to represent the token -/// @dev Reference: https://github.com/Uniswap/solidity-lib/blob/master/contracts/libraries/SafeERC20Namer.sol -library SafeERC20Namer { - function bytes32ToString(bytes32 x) private pure returns (string memory) { - bytes memory bytesString = new bytes(32); - uint256 charCount = 0; - for (uint256 j = 0; j < 32; j++) { - bytes1 char = x[j]; - if (char != 0) { - bytesString[charCount] = char; - charCount++; - } - } - bytes memory bytesStringTrimmed = new bytes(charCount); - for (uint256 j = 0; j < charCount; j++) { - bytesStringTrimmed[j] = bytesString[j]; - } - return string(bytesStringTrimmed); - } - - /// @notice produces a token symbol from the address - the first 6 hex of the address string in upper case - /// @param token the token address - /// @return the token symbol - function addressToSymbol(address token) private pure returns (string memory) { - return AddressStringUtil.toAsciiString(token, 6); - } - - /// @notice calls an external view token contract method that returns a symbol, and parses the output into a string - /// @param token the token address - /// @param selector the selector of the symbol method - /// @return the token symbol - function callAndParseStringReturn(address token, bytes4 selector) private view returns (string memory) { - (bool success, bytes memory data) = token.staticcall(abi.encodeWithSelector(selector)); - // if not implemented, return empty string - if (!success) { - return ""; - } - // bytes32 data always has length 32 - if (data.length == 32) { - bytes32 decoded = abi.decode(data, (bytes32)); - return bytes32ToString(decoded); - } else if (data.length > 64) { - return abi.decode(data, (string)); - } - return ""; - } - - /// @notice attempts to extract the token symbol. if it does not implement symbol, returns a symbol derived from the address - /// @param token the token address - /// @return the token symbol - function tokenSymbol(address token) internal view returns (string memory) { - string memory symbol = callAndParseStringReturn(token, IERC20Metadata.symbol.selector); - if (bytes(symbol).length == 0) { - // fallback to 6 uppercase hex of address - return addressToSymbol(token); - } - return symbol; - } -} From 6ff9887183103e062d6ab6a0b291bf162a1376d8 Mon Sep 17 00:00:00 2001 From: dianakocsis Date: Mon, 30 Sep 2024 22:46:23 -0400 Subject: [PATCH 40/45] add safeerc20metadata file --- src/libraries/SafeERC20Metadata.sol | 80 +++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 src/libraries/SafeERC20Metadata.sol diff --git a/src/libraries/SafeERC20Metadata.sol b/src/libraries/SafeERC20Metadata.sol new file mode 100644 index 000000000..5dc976f60 --- /dev/null +++ b/src/libraries/SafeERC20Metadata.sol @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.0; + +import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; +import "./AddressStringUtil.sol"; + +/// @title SafeERC20Metadata +/// @notice can produce symbols and decimals from inconsistent or absent ERC20 implementations +/// @dev Reference: https://github.com/Uniswap/solidity-lib/blob/master/contracts/libraries/SafeERC20Namer.sol +library SafeERC20Metadata { + function bytes32ToString(bytes32 x) private pure returns (string memory) { + bytes memory bytesString = new bytes(32); + uint256 charCount = 0; + for (uint256 j = 0; j < 32; j++) { + bytes1 char = x[j]; + if (char != 0) { + bytesString[charCount] = char; + charCount++; + } + } + bytes memory bytesStringTrimmed = new bytes(charCount); + for (uint256 j = 0; j < charCount; j++) { + bytesStringTrimmed[j] = bytesString[j]; + } + return string(bytesStringTrimmed); + } + + /// @notice produces a token symbol from the address - the first 6 hex of the address string in upper case + /// @param token the token address + /// @return the token symbol + function addressToSymbol(address token) private pure returns (string memory) { + return AddressStringUtil.toAsciiString(token, 6); + } + + /// @notice calls an external view token contract method that returns a symbol, and parses the output into a string + /// @param token the token address + /// @param selector the selector of the symbol method + /// @return the token symbol + function callAndParseStringReturn(address token, bytes4 selector) private view returns (string memory) { + (bool success, bytes memory data) = token.staticcall(abi.encodeWithSelector(selector)); + // if not implemented, return empty string + if (!success) { + return ""; + } + // bytes32 data always has length 32 + if (data.length == 32) { + bytes32 decoded = abi.decode(data, (bytes32)); + return bytes32ToString(decoded); + } else if (data.length > 64) { + return abi.decode(data, (string)); + } + return ""; + } + + /// @notice attempts to extract the token symbol. if it does not implement symbol, returns a symbol derived from the address + /// @param token the token address + /// @return the token symbol + function tokenSymbol(address token) internal view returns (string memory) { + string memory symbol = callAndParseStringReturn(token, IERC20Metadata.symbol.selector); + if (bytes(symbol).length == 0) { + // fallback to 6 uppercase hex of address + return addressToSymbol(token); + } + return symbol; + } + + /// @notice attempts to extract the token decimals, returns 0 if not implemented or not a uint8 + /// @param token the token address + /// @return the token decimals + function tokenDecimals(address token) internal view returns (uint8) { + (bool success, bytes memory data) = token.staticcall(abi.encodeCall(IERC20Metadata.decimals, ())); + if (!success) { + return 0; + } + if (data.length == 32) { + return abi.decode(data, (uint8)); + } + return 0; + } +} From 8ea6b670703d0bccba14e38cc6328698dd163807 Mon Sep 17 00:00:00 2001 From: dianakocsis Date: Wed, 2 Oct 2024 15:36:22 -0400 Subject: [PATCH 41/45] updates --- src/PositionDescriptor.sol | 39 ++++++++---------------- src/libraries/CurrencyRatioSortOrder.sol | 1 - src/libraries/Descriptor.sol | 1 - src/libraries/SafeERC20Metadata.sol | 29 ++++++++++++------ test/PositionDescriptor.t.sol | 8 +++-- 5 files changed, 38 insertions(+), 40 deletions(-) diff --git a/src/PositionDescriptor.sol b/src/PositionDescriptor.sol index c3dc3a6c6..4e26346cd 100644 --- a/src/PositionDescriptor.sol +++ b/src/PositionDescriptor.sol @@ -3,11 +3,9 @@ pragma solidity 0.8.26; import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol"; import {Currency, CurrencyLibrary} from "@uniswap/v4-core/src/types/Currency.sol"; -import {IHooks} from "@uniswap/v4-core/src/interfaces/IHooks.sol"; import {StateLibrary} from "@uniswap/v4-core/src/libraries/StateLibrary.sol"; import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol"; import {PoolId, PoolIdLibrary} from "@uniswap/v4-core/src/types/PoolId.sol"; -import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import {IPositionManager} from "./interfaces/IPositionManager.sol"; import {IPositionDescriptor} from "./interfaces/IPositionDescriptor.sol"; import {PositionInfo, PositionInfoLibrary} from "./libraries/PositionInfoLibrary.sol"; @@ -30,14 +28,14 @@ contract PositionDescriptor is IPositionDescriptor { address private constant TBTC = 0x8dAEBADE922dF735c38C80C7eBD708Af50815fAa; address private constant WBTC = 0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599; - address public immutable WETH9; + address public immutable wrappedNative; string public nativeCurrencyLabel; IPoolManager public immutable poolManager; - constructor(IPoolManager _poolManager, address _WETH9, string memory _nativeCurrencyLabel) { + constructor(IPoolManager _poolManager, address _wrappedNative, string memory _nativeCurrencyLabel) { poolManager = _poolManager; - WETH9 = _WETH9; + wrappedNative = _wrappedNative; nativeCurrencyLabel = _nativeCurrencyLabel; } @@ -65,18 +63,10 @@ contract PositionDescriptor is IPositionDescriptor { tokenId: tokenId, quoteCurrency: quoteCurrency, baseCurrency: baseCurrency, - quoteCurrencySymbol: quoteCurrency.isAddressZero() - ? nativeCurrencyLabel - : SafeERC20Metadata.tokenSymbol(Currency.unwrap(quoteCurrency)), - baseCurrencySymbol: baseCurrency.isAddressZero() - ? nativeCurrencyLabel - : SafeERC20Metadata.tokenSymbol(Currency.unwrap(baseCurrency)), - quoteCurrencyDecimals: quoteCurrency.isAddressZero() - ? 18 - : SafeERC20Metadata.tokenDecimals(Currency.unwrap(quoteCurrency)), - baseCurrencyDecimals: baseCurrency.isAddressZero() - ? 18 - : SafeERC20Metadata.tokenDecimals(Currency.unwrap(baseCurrency)), + quoteCurrencySymbol: SafeERC20Metadata.currencySymbol(quoteCurrency, nativeCurrencyLabel), + baseCurrencySymbol: SafeERC20Metadata.currencySymbol(baseCurrency, nativeCurrencyLabel), + quoteCurrencyDecimals: SafeERC20Metadata.currencyDecimals(quoteCurrency), + baseCurrencyDecimals: SafeERC20Metadata.currencyDecimals(baseCurrency), flipRatio: _flipRatio, tickLower: positionInfo.tickLower(), tickUpper: positionInfo.tickUpper(), @@ -90,8 +80,8 @@ contract PositionDescriptor is IPositionDescriptor { } /// @notice Returns true if currency0 has higher priority than currency1 - /// @param currency0 The first currency - /// @param currency1 The second currency + /// @param currency0 The first currency address + /// @param currency1 The second currency address /// @return flipRatio True if currency0 has higher priority than currency1 function flipRatio(address currency0, address currency1) public view returns (bool) { return currencyRatioPriority(currency0) > currencyRatioPriority(currency1); @@ -99,19 +89,16 @@ contract PositionDescriptor is IPositionDescriptor { /// @notice Returns the priority of a currency. /// For certain currencies on mainnet, the smaller the currency, the higher the priority - /// @param currency The currency + /// @param currency The currency address /// @return priority The priority of the currency function currencyRatioPriority(address currency) public view returns (int256) { - // Currencies in order of priority on mainnet: USDC, USDT, DAI, ETH, WETH, TBTC, WBTC - // weth is different address on different chains. passed in constructor + // Currencies in order of priority on mainnet: USDC, USDT, DAI, (ETH, WETH), TBTC, WBTC + // wrapped native is different address on different chains. passed in constructor // native currency - if (currency == address(0)) { + if (currency == address(0) || currency == wrappedNative) { return CurrencyRatioSortOrder.DENOMINATOR; } - if (currency == WETH9) { - return CurrencyRatioSortOrder.DENOMINATOR_2; - } if (block.chainid == 1) { if (currency == USDC) { return CurrencyRatioSortOrder.NUMERATOR_MOST; diff --git a/src/libraries/CurrencyRatioSortOrder.sol b/src/libraries/CurrencyRatioSortOrder.sol index d9d9d4426..1f3a719a4 100644 --- a/src/libraries/CurrencyRatioSortOrder.sol +++ b/src/libraries/CurrencyRatioSortOrder.sol @@ -12,6 +12,5 @@ library CurrencyRatioSortOrder { int256 constant DENOMINATOR_MOST = -300; int256 constant DENOMINATOR_MORE = -200; - int256 constant DENOMINATOR_2 = -150; int256 constant DENOMINATOR = -100; } diff --git a/src/libraries/Descriptor.sol b/src/libraries/Descriptor.sol index b5da50ee5..8e32a35d5 100644 --- a/src/libraries/Descriptor.sol +++ b/src/libraries/Descriptor.sol @@ -2,7 +2,6 @@ pragma solidity ^0.8.0; import {Currency, CurrencyLibrary} from "@uniswap/v4-core/src/types/Currency.sol"; -import {IHooks} from "@uniswap/v4-core/src/interfaces/IHooks.sol"; import {TickMath} from "@uniswap/v4-core/src/libraries/TickMath.sol"; import {FullMath} from "@uniswap/v4-core/src/libraries/FullMath.sol"; import {LPFeeLibrary} from "@uniswap/v4-core/src/libraries/LPFeeLibrary.sol"; diff --git a/src/libraries/SafeERC20Metadata.sol b/src/libraries/SafeERC20Metadata.sol index 5dc976f60..3d1e1cb9c 100644 --- a/src/libraries/SafeERC20Metadata.sol +++ b/src/libraries/SafeERC20Metadata.sol @@ -1,13 +1,16 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.0; -import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; -import "./AddressStringUtil.sol"; +import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; +import {Currency, CurrencyLibrary} from "@uniswap/v4-core/src/types/Currency.sol"; +import {AddressStringUtil} from "./AddressStringUtil.sol"; /// @title SafeERC20Metadata /// @notice can produce symbols and decimals from inconsistent or absent ERC20 implementations /// @dev Reference: https://github.com/Uniswap/solidity-lib/blob/master/contracts/libraries/SafeERC20Namer.sol library SafeERC20Metadata { + using CurrencyLibrary for Currency; + function bytes32ToString(bytes32 x) private pure returns (string memory) { bytes memory bytesString = new bytes(32); uint256 charCount = 0; @@ -53,22 +56,30 @@ library SafeERC20Metadata { } /// @notice attempts to extract the token symbol. if it does not implement symbol, returns a symbol derived from the address - /// @param token the token address + /// @param currency The currency + /// @param nativeLabel The native label /// @return the token symbol - function tokenSymbol(address token) internal view returns (string memory) { - string memory symbol = callAndParseStringReturn(token, IERC20Metadata.symbol.selector); + function currencySymbol(Currency currency, string memory nativeLabel) internal view returns (string memory) { + if (currency.isAddressZero()) { + return nativeLabel; + } + address currencyAddress = Currency.unwrap(currency); + string memory symbol = callAndParseStringReturn(currencyAddress, IERC20Metadata.symbol.selector); if (bytes(symbol).length == 0) { // fallback to 6 uppercase hex of address - return addressToSymbol(token); + return addressToSymbol(currencyAddress); } return symbol; } /// @notice attempts to extract the token decimals, returns 0 if not implemented or not a uint8 - /// @param token the token address + /// @param currency The currency /// @return the token decimals - function tokenDecimals(address token) internal view returns (uint8) { - (bool success, bytes memory data) = token.staticcall(abi.encodeCall(IERC20Metadata.decimals, ())); + function currencyDecimals(Currency currency) internal view returns (uint8) { + if (currency.isAddressZero()) { + return 18; + } + (bool success, bytes memory data) = Currency.unwrap(currency).staticcall(abi.encodeCall(IERC20Metadata.decimals, ())); if (!success) { return 0; } diff --git a/test/PositionDescriptor.t.sol b/test/PositionDescriptor.t.sol index 9ba6f54a9..782b798db 100644 --- a/test/PositionDescriptor.t.sol +++ b/test/PositionDescriptor.t.sol @@ -38,13 +38,14 @@ contract PositionDescriptorTest is Test, PosmTestSetup { function test_setup_succeeds() public view { assertEq(address(positionDescriptor.poolManager()), address(manager)); - assertEq(positionDescriptor.WETH9(), WETH9); + assertEq(positionDescriptor.wrappedNative(), WETH9); assertEq(positionDescriptor.nativeCurrencyLabel(), nativeCurrencyLabel); } function test_currencyRatioPriority_mainnet_succeeds() public { vm.chainId(1); - assertEq(positionDescriptor.currencyRatioPriority(WETH9), CurrencyRatioSortOrder.DENOMINATOR_2); + assertEq(positionDescriptor.currencyRatioPriority(WETH9), CurrencyRatioSortOrder.DENOMINATOR); + assertEq(positionDescriptor.currencyRatioPriority(address(0)), CurrencyRatioSortOrder.DENOMINATOR); assertEq(positionDescriptor.currencyRatioPriority(USDC), CurrencyRatioSortOrder.NUMERATOR_MOST); assertEq(positionDescriptor.currencyRatioPriority(USDT), CurrencyRatioSortOrder.NUMERATOR_MORE); assertEq(positionDescriptor.currencyRatioPriority(DAI), CurrencyRatioSortOrder.NUMERATOR); @@ -54,7 +55,8 @@ contract PositionDescriptorTest is Test, PosmTestSetup { } function test_currencyRatioPriority_notMainnet_succeeds() public { - assertEq(positionDescriptor.currencyRatioPriority(WETH9), CurrencyRatioSortOrder.DENOMINATOR_2); + assertEq(positionDescriptor.currencyRatioPriority(WETH9), CurrencyRatioSortOrder.DENOMINATOR); + assertEq(positionDescriptor.currencyRatioPriority(address(0)), CurrencyRatioSortOrder.DENOMINATOR); assertEq(positionDescriptor.currencyRatioPriority(USDC), 0); assertEq(positionDescriptor.currencyRatioPriority(USDT), 0); assertEq(positionDescriptor.currencyRatioPriority(DAI), 0); From 072102f94d1f5ef652645d2b9dd148930b86d3bd Mon Sep 17 00:00:00 2001 From: dianakocsis Date: Wed, 2 Oct 2024 15:38:53 -0400 Subject: [PATCH 42/45] format --- src/libraries/SafeERC20Metadata.sol | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libraries/SafeERC20Metadata.sol b/src/libraries/SafeERC20Metadata.sol index 3d1e1cb9c..0e4b9e86c 100644 --- a/src/libraries/SafeERC20Metadata.sol +++ b/src/libraries/SafeERC20Metadata.sol @@ -79,7 +79,8 @@ library SafeERC20Metadata { if (currency.isAddressZero()) { return 18; } - (bool success, bytes memory data) = Currency.unwrap(currency).staticcall(abi.encodeCall(IERC20Metadata.decimals, ())); + (bool success, bytes memory data) = + Currency.unwrap(currency).staticcall(abi.encodeCall(IERC20Metadata.decimals, ())); if (!success) { return 0; } From bd90cee89ef9fd178e76d424b738eb74bd994a05 Mon Sep 17 00:00:00 2001 From: dianakocsis Date: Thu, 3 Oct 2024 22:58:03 -0400 Subject: [PATCH 43/45] currency name plus more tests --- script/DeployPosm.s.sol | 4 +- src/PositionDescriptor.sol | 10 +- src/libraries/Descriptor.sol | 14 +-- ...0Metadata.sol => SafeCurrencyMetadata.sol} | 92 +++++++++---------- test/libraries/Descriptor.t.sol | 32 +++++++ 5 files changed, 92 insertions(+), 60 deletions(-) rename src/libraries/{SafeERC20Metadata.sol => SafeCurrencyMetadata.sol} (78%) diff --git a/script/DeployPosm.s.sol b/script/DeployPosm.s.sol index 4233208ce..5bbb6184c 100644 --- a/script/DeployPosm.s.sol +++ b/script/DeployPosm.s.sol @@ -18,12 +18,12 @@ contract DeployPosmTest is Script { address poolManager, address permit2, uint256 unsubscribeGasLimit, - address weth, + address wrappedNative, string memory nativeCurrencyLabel ) public returns (PositionDescriptor positionDescriptor, PositionManager posm) { vm.startBroadcast(); - positionDescriptor = new PositionDescriptor(IPoolManager(poolManager), weth, nativeCurrencyLabel); + positionDescriptor = new PositionDescriptor(IPoolManager(poolManager), wrappedNative, nativeCurrencyLabel); console2.log("PositionDescriptor", address(positionDescriptor)); posm = new PositionManager{salt: hex"03"}( diff --git a/src/PositionDescriptor.sol b/src/PositionDescriptor.sol index 4e26346cd..572146d06 100644 --- a/src/PositionDescriptor.sol +++ b/src/PositionDescriptor.sol @@ -11,7 +11,7 @@ import {IPositionDescriptor} from "./interfaces/IPositionDescriptor.sol"; import {PositionInfo, PositionInfoLibrary} from "./libraries/PositionInfoLibrary.sol"; import {Descriptor} from "./libraries/Descriptor.sol"; import {CurrencyRatioSortOrder} from "./libraries/CurrencyRatioSortOrder.sol"; -import {SafeERC20Metadata} from "./libraries/SafeERC20Metadata.sol"; +import {SafeCurrencyMetadata} from "./libraries/SafeCurrencyMetadata.sol"; /// @title Describes NFT token positions /// @notice Produces a string containing the data URI for a JSON metadata string @@ -63,10 +63,10 @@ contract PositionDescriptor is IPositionDescriptor { tokenId: tokenId, quoteCurrency: quoteCurrency, baseCurrency: baseCurrency, - quoteCurrencySymbol: SafeERC20Metadata.currencySymbol(quoteCurrency, nativeCurrencyLabel), - baseCurrencySymbol: SafeERC20Metadata.currencySymbol(baseCurrency, nativeCurrencyLabel), - quoteCurrencyDecimals: SafeERC20Metadata.currencyDecimals(quoteCurrency), - baseCurrencyDecimals: SafeERC20Metadata.currencyDecimals(baseCurrency), + quoteCurrencySymbol: SafeCurrencyMetadata.currencySymbol(quoteCurrency, nativeCurrencyLabel), + baseCurrencySymbol: SafeCurrencyMetadata.currencySymbol(baseCurrency, nativeCurrencyLabel), + quoteCurrencyDecimals: SafeCurrencyMetadata.currencyDecimals(quoteCurrency), + baseCurrencyDecimals: SafeCurrencyMetadata.currencyDecimals(baseCurrency), flipRatio: _flipRatio, tickLower: positionInfo.tickLower(), tickUpper: positionInfo.tickUpper(), diff --git a/src/libraries/Descriptor.sol b/src/libraries/Descriptor.sol index 8e32a35d5..09a3cf5cc 100644 --- a/src/libraries/Descriptor.sol +++ b/src/libraries/Descriptor.sol @@ -439,16 +439,16 @@ library Descriptor { } else { // represents fee less than 1% // else if decimal < 1 - nZeros = 5 - digits; // number of zeros, inlcuding the zero before decimal - params.zerosStartIndex = 2; // leading zeros will start after the decimal point - params.zerosEndIndex = uint8(nZeros + params.zerosStartIndex - 1); // end index for leading zeros - params.bufferLength = uint8(nZeros + numSigfigs + 2); // total length of string buffer, including "0." and "%" - params.sigfigIndex = uint8(params.bufferLength - 2); // index of starting signficant figure + nZeros = 5 - digits; + params.zerosStartIndex = 2; + params.zerosEndIndex = uint8(nZeros + params.zerosStartIndex - 1); + params.bufferLength = uint8(nZeros + numSigfigs + 2); + params.sigfigIndex = uint8(params.bufferLength - 2); params.isLessThanOne = true; } - params.sigfigs = uint256(fee) / (10 ** (digits - numSigfigs)); // the signficant figures of the fee + params.sigfigs = uint256(fee) / (10 ** (digits - numSigfigs)); params.isPercent = true; - params.decimalIndex = digits > 4 ? uint8(digits - 4) : 0; // based on total number of digits in the fee + params.decimalIndex = digits > 4 ? uint8(digits - 4) : 0; return generateDecimalString(params); } diff --git a/src/libraries/SafeERC20Metadata.sol b/src/libraries/SafeCurrencyMetadata.sol similarity index 78% rename from src/libraries/SafeERC20Metadata.sol rename to src/libraries/SafeCurrencyMetadata.sol index 0e4b9e86c..84aa28813 100644 --- a/src/libraries/SafeERC20Metadata.sol +++ b/src/libraries/SafeCurrencyMetadata.sol @@ -5,56 +5,12 @@ import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IER import {Currency, CurrencyLibrary} from "@uniswap/v4-core/src/types/Currency.sol"; import {AddressStringUtil} from "./AddressStringUtil.sol"; -/// @title SafeERC20Metadata +/// @title SafeCurrencyMetadata /// @notice can produce symbols and decimals from inconsistent or absent ERC20 implementations /// @dev Reference: https://github.com/Uniswap/solidity-lib/blob/master/contracts/libraries/SafeERC20Namer.sol -library SafeERC20Metadata { +library SafeCurrencyMetadata { using CurrencyLibrary for Currency; - function bytes32ToString(bytes32 x) private pure returns (string memory) { - bytes memory bytesString = new bytes(32); - uint256 charCount = 0; - for (uint256 j = 0; j < 32; j++) { - bytes1 char = x[j]; - if (char != 0) { - bytesString[charCount] = char; - charCount++; - } - } - bytes memory bytesStringTrimmed = new bytes(charCount); - for (uint256 j = 0; j < charCount; j++) { - bytesStringTrimmed[j] = bytesString[j]; - } - return string(bytesStringTrimmed); - } - - /// @notice produces a token symbol from the address - the first 6 hex of the address string in upper case - /// @param token the token address - /// @return the token symbol - function addressToSymbol(address token) private pure returns (string memory) { - return AddressStringUtil.toAsciiString(token, 6); - } - - /// @notice calls an external view token contract method that returns a symbol, and parses the output into a string - /// @param token the token address - /// @param selector the selector of the symbol method - /// @return the token symbol - function callAndParseStringReturn(address token, bytes4 selector) private view returns (string memory) { - (bool success, bytes memory data) = token.staticcall(abi.encodeWithSelector(selector)); - // if not implemented, return empty string - if (!success) { - return ""; - } - // bytes32 data always has length 32 - if (data.length == 32) { - bytes32 decoded = abi.decode(data, (bytes32)); - return bytes32ToString(decoded); - } else if (data.length > 64) { - return abi.decode(data, (string)); - } - return ""; - } - /// @notice attempts to extract the token symbol. if it does not implement symbol, returns a symbol derived from the address /// @param currency The currency /// @param nativeLabel The native label @@ -89,4 +45,48 @@ library SafeERC20Metadata { } return 0; } + + function bytes32ToString(bytes32 x) private pure returns (string memory) { + bytes memory bytesString = new bytes(32); + uint256 charCount = 0; + for (uint256 j = 0; j < 32; j++) { + bytes1 char = x[j]; + if (char != 0) { + bytesString[charCount] = char; + charCount++; + } + } + bytes memory bytesStringTrimmed = new bytes(charCount); + for (uint256 j = 0; j < charCount; j++) { + bytesStringTrimmed[j] = bytesString[j]; + } + return string(bytesStringTrimmed); + } + + /// @notice produces a symbol from the address - the first 6 hex of the address string in upper case + /// @param currencyAddress the address of the currency + /// @return the symbol + function addressToSymbol(address currencyAddress) private pure returns (string memory) { + return AddressStringUtil.toAsciiString(currencyAddress, 6); + } + + /// @notice calls an external view contract method that returns a symbol, and parses the output into a string + /// @param currencyAddress the address of the currency + /// @param selector the selector of the symbol method + /// @return the symbol + function callAndParseStringReturn(address currencyAddress, bytes4 selector) private view returns (string memory) { + (bool success, bytes memory data) = currencyAddress.staticcall(abi.encodeWithSelector(selector)); + // if not implemented, return empty string + if (!success) { + return ""; + } + // bytes32 data always has length 32 + if (data.length == 32) { + bytes32 decoded = abi.decode(data, (bytes32)); + return bytes32ToString(decoded); + } else if (data.length > 64) { + return abi.decode(data, (string)); + } + return ""; + } } diff --git a/test/libraries/Descriptor.t.sol b/test/libraries/Descriptor.t.sol index 2dd6339aa..e191c5a5e 100644 --- a/test/libraries/Descriptor.t.sol +++ b/test/libraries/Descriptor.t.sol @@ -7,6 +7,7 @@ import {TickMath} from "@uniswap/v4-core/src/libraries/TickMath.sol"; contract DescriptorTest is Test { function test_feeToPercentString_succeeds() public pure { + assertEq(Descriptor.feeToPercentString(0x800000), "Dynamic"); assertEq(Descriptor.feeToPercentString(0), "0%"); assertEq(Descriptor.feeToPercentString(1), "0.0001%"); assertEq(Descriptor.feeToPercentString(30), "0.003%"); @@ -115,4 +116,35 @@ contract DescriptorTest is Test { assertEq(Descriptor.tickToDecimalString(1000, tickSpacing, 18, 10, true), "90484000"); assertEq(Descriptor.tickToDecimalString(1000, tickSpacing, 10, 18, true), "0.0000000090484"); } + + function test_fixedPointToDecimalString() public pure { + assertEq( + Descriptor.fixedPointToDecimalString(1457647476727839560029885420909913413788472405159, 18, 18), + "338490000000000000000000000000000000000" + ); + assertEq( + Descriptor.fixedPointToDecimalString(4025149349925610116743993887520032712, 18, 18), "2581100000000000" + ); + assertEq(Descriptor.fixedPointToDecimalString(3329657202331788924044422905302854, 18, 18), "1766200000"); + assertEq(Descriptor.fixedPointToDecimalString(16241966553695418990605751641065, 18, 18), "42026"); + assertEq(Descriptor.fixedPointToDecimalString(2754475062069337566441091812235, 18, 18), "1208.7"); + assertEq(Descriptor.fixedPointToDecimalString(871041495427277622831427623669, 18, 18), "120.87"); + assertEq(Descriptor.fixedPointToDecimalString(275447506206933756644109181223, 18, 18), "12.087"); + + assertEq(Descriptor.fixedPointToDecimalString(88028870788706913884596530851, 18, 18), "1.2345"); + assertEq(Descriptor.fixedPointToDecimalString(79228162514264337593543950336, 18, 18), "1.0000"); + assertEq(Descriptor.fixedPointToDecimalString(27837173154497669652482281089, 18, 18), "0.12345"); + assertEq(Descriptor.fixedPointToDecimalString(1559426812423768092342, 18, 18), "0.00000000000000038741"); + assertEq(Descriptor.fixedPointToDecimalString(74532606916587, 18, 18), "0.00000000000000000000000000000088498"); + assertEq( + Descriptor.fixedPointToDecimalString(4947797163, 18, 18), "0.0000000000000000000000000000000000000029387" + ); + + assertEq(Descriptor.fixedPointToDecimalString(79228162514264337593543950336, 18, 16), "100.00"); + assertEq(Descriptor.fixedPointToDecimalString(250541448375047931186413801569, 18, 17), "100.00"); + assertEq(Descriptor.fixedPointToDecimalString(79228162514264337593543950336, 24, 5), "1.0000"); + + assertEq(Descriptor.fixedPointToDecimalString(79228162514264337593543950336, 10, 18), "0.000000010000"); + assertEq(Descriptor.fixedPointToDecimalString(79228162514264337593543950336, 7, 18), "0.000000000010000"); + } } From f72f1954f1e241811a064e61f34eb7143a5b0e1e Mon Sep 17 00:00:00 2001 From: dianakocsis Date: Mon, 7 Oct 2024 10:38:35 -0400 Subject: [PATCH 44/45] invalid token id --- src/PositionDescriptor.sol | 5 +++++ test/PositionDescriptor.t.sol | 22 ++++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/src/PositionDescriptor.sol b/src/PositionDescriptor.sol index 572146d06..7cf39d943 100644 --- a/src/PositionDescriptor.sol +++ b/src/PositionDescriptor.sol @@ -21,6 +21,8 @@ contract PositionDescriptor is IPositionDescriptor { using CurrencyLibrary for Currency; using PositionInfoLibrary for PositionInfo; + error InvalidTokenId(uint256 tokenId); + // mainnet addresses address private constant DAI = 0x6B175474E89094C44Da98b954EedeAC495271d0F; address private constant USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48; @@ -47,6 +49,9 @@ contract PositionDescriptor is IPositionDescriptor { returns (string memory) { (PoolKey memory poolKey, PositionInfo positionInfo) = positionManager.getPoolAndPositionInfo(tokenId); + if (positionInfo.poolId() == 0) { + revert InvalidTokenId(tokenId); + } (, int24 tick,,) = poolManager.getSlot0(poolKey.toId()); // If possible, flip currencies to get the larger currency as the base currency, so that the price (quote/base) is more readable diff --git a/test/PositionDescriptor.t.sol b/test/PositionDescriptor.t.sol index 782b798db..6658a7356 100644 --- a/test/PositionDescriptor.t.sol +++ b/test/PositionDescriptor.t.sol @@ -114,4 +114,26 @@ contract PositionDescriptorTest is Test, PosmTestSetup { bytes memory data = vm.parseJson(json); Token memory token = abi.decode(data, (Token)); } + + function test_tokenURI_revertsWithInvalidTokenId() public { + int24 tickLower = int24(key.tickSpacing); + int24 tickUpper = int24(key.tickSpacing * 2); + uint256 amount0Desired = 100e18; + uint256 amount1Desired = 100e18; + uint256 liquidityToAdd = LiquidityAmounts.getLiquidityForAmounts( + SQRT_PRICE_1_1, + TickMath.getSqrtPriceAtTick(tickLower), + TickMath.getSqrtPriceAtTick(tickUpper), + amount0Desired, + amount1Desired + ); + + PositionConfig memory config = PositionConfig({poolKey: key, tickLower: tickLower, tickUpper: tickUpper}); + uint256 tokenId = lpm.nextTokenId(); + mint(config, liquidityToAdd, ActionConstants.MSG_SENDER, ZERO_BYTES); + + vm.expectRevert(abi.encodeWithSelector(PositionDescriptor.InvalidTokenId.selector, tokenId + 1)); + + positionDescriptor.tokenURI(lpm, tokenId + 1); + } } From c10da46d0752d63d0e0d2a8d41c2780f7f78ef2e Mon Sep 17 00:00:00 2001 From: dianakocsis Date: Mon, 7 Oct 2024 16:17:16 -0400 Subject: [PATCH 45/45] re-add comments --- src/libraries/Descriptor.sol | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/libraries/Descriptor.sol b/src/libraries/Descriptor.sol index 09a3cf5cc..8e32a35d5 100644 --- a/src/libraries/Descriptor.sol +++ b/src/libraries/Descriptor.sol @@ -439,16 +439,16 @@ library Descriptor { } else { // represents fee less than 1% // else if decimal < 1 - nZeros = 5 - digits; - params.zerosStartIndex = 2; - params.zerosEndIndex = uint8(nZeros + params.zerosStartIndex - 1); - params.bufferLength = uint8(nZeros + numSigfigs + 2); - params.sigfigIndex = uint8(params.bufferLength - 2); + nZeros = 5 - digits; // number of zeros, inlcuding the zero before decimal + params.zerosStartIndex = 2; // leading zeros will start after the decimal point + params.zerosEndIndex = uint8(nZeros + params.zerosStartIndex - 1); // end index for leading zeros + params.bufferLength = uint8(nZeros + numSigfigs + 2); // total length of string buffer, including "0." and "%" + params.sigfigIndex = uint8(params.bufferLength - 2); // index of starting signficant figure params.isLessThanOne = true; } - params.sigfigs = uint256(fee) / (10 ** (digits - numSigfigs)); + params.sigfigs = uint256(fee) / (10 ** (digits - numSigfigs)); // the signficant figures of the fee params.isPercent = true; - params.decimalIndex = digits > 4 ? uint8(digits - 4) : 0; + params.decimalIndex = digits > 4 ? uint8(digits - 4) : 0; // based on total number of digits in the fee return generateDecimalString(params); }