From 76af62e1e79631611ddc68b40e7e7d997d800f8a Mon Sep 17 00:00:00 2001 From: dianakocsis Date: Thu, 17 Oct 2024 14:56:18 -0400 Subject: [PATCH 1/8] Pashov L-04 display Native instead of the zero address --- .../positionDescriptor bytecode size.snap | 2 +- src/PositionDescriptor.sol | 89 ++++++------ ...ortOrder.sol => AddressRatioSortOrder.sol} | 8 +- src/libraries/Descriptor.sol | 137 +++++++++--------- src/libraries/SVG.sol | 61 ++++---- ...encyMetadata.sol => SafeERC20Metadata.sol} | 39 +++-- test/PositionDescriptor.t.sol | 42 +++--- 7 files changed, 187 insertions(+), 191 deletions(-) rename src/libraries/{CurrencyRatioSortOrder.sol => AddressRatioSortOrder.sol} (65%) rename src/libraries/{SafeCurrencyMetadata.sol => SafeERC20Metadata.sol} (64%) diff --git a/.forge-snapshots/positionDescriptor bytecode size.snap b/.forge-snapshots/positionDescriptor bytecode size.snap index ec121d73..fcd5df5e 100644 --- a/.forge-snapshots/positionDescriptor bytecode size.snap +++ b/.forge-snapshots/positionDescriptor bytecode size.snap @@ -1 +1 @@ -31065 \ No newline at end of file +31144 \ No newline at end of file diff --git a/src/PositionDescriptor.sol b/src/PositionDescriptor.sol index 7cf39d94..716f7497 100644 --- a/src/PositionDescriptor.sol +++ b/src/PositionDescriptor.sol @@ -10,8 +10,8 @@ import {IPositionManager} from "./interfaces/IPositionManager.sol"; 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 {SafeCurrencyMetadata} from "./libraries/SafeCurrencyMetadata.sol"; +import {AddressRatioSortOrder} from "./libraries/AddressRatioSortOrder.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 @@ -31,14 +31,14 @@ contract PositionDescriptor is IPositionDescriptor { address private constant WBTC = 0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599; address public immutable wrappedNative; - string public nativeCurrencyLabel; + string public nativeAddressLabel; IPoolManager public immutable poolManager; - constructor(IPoolManager _poolManager, address _wrappedNative, string memory _nativeCurrencyLabel) { + constructor(IPoolManager _poolManager, address _wrappedNative, string memory _nativeAddressLabel) { poolManager = _poolManager; wrappedNative = _wrappedNative; - nativeCurrencyLabel = _nativeCurrencyLabel; + nativeAddressLabel = _nativeAddressLabel; } /// @inheritdoc IPositionDescriptor @@ -54,24 +54,27 @@ contract PositionDescriptor is IPositionDescriptor { } (, 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)); + address address0 = Currency.unwrap(poolKey.currency0); + address address1 = Currency.unwrap(poolKey.currency1); - // If not flipped, quote currency is currency1, base currency is currency0 - // If flipped, quote currency is currency0, base currency is currency1 - Currency quoteCurrency = !_flipRatio ? poolKey.currency1 : poolKey.currency0; - Currency baseCurrency = !_flipRatio ? poolKey.currency0 : poolKey.currency1; + // If possible, flip addresses to get the larger one as the base, so that the price (quote/base) is more readable + // flip if address0 priority is greater than address1 priority + bool _flipRatio = flipRatio(address0, address1); + + // If not flipped, quote address is address1, base address is address0 + // If flipped, quote address is address0, base address is address1 + address quoteAddress = !_flipRatio ? address1 : address0; + address baseAddress = !_flipRatio ? address0 : address1; return Descriptor.constructTokenURI( Descriptor.ConstructTokenURIParams({ tokenId: tokenId, - quoteCurrency: quoteCurrency, - baseCurrency: baseCurrency, - quoteCurrencySymbol: SafeCurrencyMetadata.currencySymbol(quoteCurrency, nativeCurrencyLabel), - baseCurrencySymbol: SafeCurrencyMetadata.currencySymbol(baseCurrency, nativeCurrencyLabel), - quoteCurrencyDecimals: SafeCurrencyMetadata.currencyDecimals(quoteCurrency), - baseCurrencyDecimals: SafeCurrencyMetadata.currencyDecimals(baseCurrency), + quoteAddress: quoteAddress, + baseAddress: baseAddress, + quoteAddressSymbol: SafeERC20Metadata.addressSymbol(quoteAddress, nativeAddressLabel), + baseAddressSymbol: SafeERC20Metadata.addressSymbol(baseAddress, nativeAddressLabel), + quoteAddressDecimals: SafeERC20Metadata.addressDecimals(quoteAddress), + baseAddressDecimals: SafeERC20Metadata.addressDecimals(baseAddress), flipRatio: _flipRatio, tickLower: positionInfo.tickLower(), tickUpper: positionInfo.tickUpper(), @@ -84,37 +87,37 @@ contract PositionDescriptor is IPositionDescriptor { ); } - /// @notice Returns true if currency0 has higher priority than currency1 - /// @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); + /// @notice Returns true if address0 has higher priority than address1 + /// @param address0 The first address + /// @param address1 The second address + /// @return flipRatio True if address0 has higher priority than address1 + function flipRatio(address address0, address address1) public view returns (bool) { + return addressRatioPriority(address0) > addressRatioPriority(address1); } - /// @notice Returns the priority of a currency. - /// For certain currencies on mainnet, the smaller the currency, the higher the priority - /// @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 + /// @notice Returns the priority of an address. + /// For certain addresses on mainnet, the smaller the address, the higher the priority + /// @param addr The address + /// @return priority The priority of the address + function addressRatioPriority(address addr) public view returns (int256) { + // Addresses 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) || currency == wrappedNative) { - return CurrencyRatioSortOrder.DENOMINATOR; + // native address + if (addr == address(0) || addr == wrappedNative) { + return AddressRatioSortOrder.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; + if (addr == USDC) { + return AddressRatioSortOrder.NUMERATOR_MOST; + } else if (addr == USDT) { + return AddressRatioSortOrder.NUMERATOR_MORE; + } else if (addr == DAI) { + return AddressRatioSortOrder.NUMERATOR; + } else if (addr == TBTC) { + return AddressRatioSortOrder.DENOMINATOR_MORE; + } else if (addr == WBTC) { + return AddressRatioSortOrder.DENOMINATOR_MOST; } else { return 0; } diff --git a/src/libraries/CurrencyRatioSortOrder.sol b/src/libraries/AddressRatioSortOrder.sol similarity index 65% rename from src/libraries/CurrencyRatioSortOrder.sol rename to src/libraries/AddressRatioSortOrder.sol index 1f3a719a..71fdfcf9 100644 --- a/src/libraries/CurrencyRatioSortOrder.sol +++ b/src/libraries/AddressRatioSortOrder.sol @@ -1,11 +1,11 @@ // 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 +/// @title AddressRatioSortOrder +/// @notice Provides constants for sorting addresses when displaying price ratios +/// Addresses 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 { +library AddressRatioSortOrder { int256 constant NUMERATOR_MOST = 300; int256 constant NUMERATOR_MORE = 200; int256 constant NUMERATOR = 100; diff --git a/src/libraries/Descriptor.sol b/src/libraries/Descriptor.sol index 527e7bd8..56f7cb39 100644 --- a/src/libraries/Descriptor.sol +++ b/src/libraries/Descriptor.sol @@ -1,7 +1,6 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.0; -import {Currency, CurrencyLibrary} from "@uniswap/v4-core/src/types/Currency.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"; @@ -23,12 +22,12 @@ library Descriptor { struct ConstructTokenURIParams { uint256 tokenId; - Currency quoteCurrency; - Currency baseCurrency; - string quoteCurrencySymbol; - string baseCurrencySymbol; - uint8 quoteCurrencyDecimals; - uint8 baseCurrencyDecimals; + address quoteAddress; + address baseAddress; + string quoteAddressSymbol; + string baseAddressSymbol; + uint8 quoteAddressDecimals; + uint8 baseAddressDecimals; bool flipRatio; int24 tickLower; int24 tickUpper; @@ -45,15 +44,15 @@ library Descriptor { function constructTokenURI(ConstructTokenURIParams memory params) internal pure returns (string memory) { string memory name = generateName(params, feeToPercentString(params.fee)); string memory descriptionPartOne = generateDescriptionPartOne( - escapeQuotes(params.quoteCurrencySymbol), - escapeQuotes(params.baseCurrencySymbol), + escapeQuotes(params.quoteAddressSymbol), + escapeQuotes(params.baseAddressSymbol), addressToString(params.poolManager) ); string memory descriptionPartTwo = generateDescriptionPartTwo( params.tokenId.toString(), - escapeQuotes(params.baseCurrencySymbol), - addressToString(Currency.unwrap(params.quoteCurrency)), - addressToString(Currency.unwrap(params.baseCurrency)), + escapeQuotes(params.baseAddressSymbol), + params.quoteAddress == address(0) ? "Native" : addressToString(params.quoteAddress), + params.baseAddress == address(0) ? "Native" : addressToString(params.baseAddress), addressToString(params.hooks), feeToPercentString(params.fee) ); @@ -109,56 +108,56 @@ library Descriptor { } /// @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 quoteAddressSymbol The symbol of the quote address + /// @param baseAddressSymbol The symbol of the base address /// @param poolManager The address of the pool manager /// @return The first part of the description function generateDescriptionPartOne( - string memory quoteCurrencySymbol, - string memory baseCurrencySymbol, + string memory quoteAddressSymbol, + string memory baseAddressSymbol, string memory poolManager ) private pure returns (string memory) { - // displays quote currency first, then base currency + // displays quote address first, then base address return string( abi.encodePacked( "This NFT represents a liquidity position in a Uniswap v4 ", - quoteCurrencySymbol, + quoteAddressSymbol, "-", - baseCurrencySymbol, + baseAddressSymbol, " pool. ", "The owner of this NFT can modify or redeem the position.\\n", "\\nPool Manager Address: ", poolManager, "\\n", - quoteCurrencySymbol + quoteAddressSymbol ) ); } /// @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 baseAddressSymbol The symbol of the base address + /// @param quoteAddress The address of the quote address + /// @param baseAddress The address of the base address /// @param hooks The address of the hooks contract /// @param feeTier The fee tier of the pool /// @return The second part of the description function generateDescriptionPartTwo( string memory tokenId, - string memory baseCurrencySymbol, - string memory quoteCurrency, - string memory baseCurrency, + string memory baseAddressSymbol, + string memory quoteAddress, + string memory baseAddress, string memory hooks, string memory feeTier ) private pure returns (string memory) { return string( abi.encodePacked( " Address: ", - quoteCurrency, + quoteAddress, "\\n", - baseCurrencySymbol, + baseAddressSymbol, " Address: ", - baseCurrency, + baseAddress, "\\nHook Address: ", hooks, "\\nFee Tier: ", @@ -166,7 +165,7 @@ library Descriptor { "\\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." + unicode"⚠️ DISCLAIMER: Due diligence is imperative when assessing this NFT. Make sure addresses match the expected addresses, as symbols may be imitated." ) ); } @@ -180,29 +179,29 @@ library Descriptor { pure returns (string memory) { - // image shows in terms of price, ie quoteCurrency/baseCurrency + // image shows in terms of price, ie quoteAddress/baseAddress return string( abi.encodePacked( "Uniswap - ", feeTier, " - ", - escapeQuotes(params.quoteCurrencySymbol), + escapeQuotes(params.quoteAddressSymbol), "/", - escapeQuotes(params.baseCurrencySymbol), + escapeQuotes(params.baseAddressSymbol), " - ", tickToDecimalString( !params.flipRatio ? params.tickLower : params.tickUpper, params.tickSpacing, - params.baseCurrencyDecimals, - params.quoteCurrencyDecimals, + params.baseAddressDecimals, + params.quoteAddressDecimals, params.flipRatio ), "<>", tickToDecimalString( !params.flipRatio ? params.tickUpper : params.tickLower, params.tickSpacing, - params.baseCurrencyDecimals, - params.quoteCurrencyDecimals, + params.baseAddressDecimals, + params.quoteAddressDecimals, params.flipRatio ) ) @@ -262,15 +261,15 @@ library Descriptor { /// MIN or MAX are returned if tick is at the bottom or top of the price curve /// @param tick The tick (either tickLower or tickUpper) /// @param tickSpacing The tick spacing of the pool - /// @param baseCurrencyDecimals The decimals of the base currency - /// @param quoteCurrencyDecimals The decimals of the quote currency + /// @param baseAddressDecimals The decimals of the base address + /// @param quoteAddressDecimals The decimals of the quote address /// @param flipRatio True if the ratio was flipped /// @return The ratio value as a string function tickToDecimalString( int24 tick, int24 tickSpacing, - uint8 baseCurrencyDecimals, - uint8 quoteCurrencyDecimals, + uint8 baseAddressDecimals, + uint8 quoteAddressDecimals, bool flipRatio ) internal pure returns (string memory) { if (tick == (TickMath.MIN_TICK / tickSpacing) * tickSpacing) { @@ -282,7 +281,7 @@ library Descriptor { if (flipRatio) { sqrtRatioX96 = uint160(uint256(1 << 192) / sqrtRatioX96); } - return fixedPointToDecimalString(sqrtRatioX96, baseCurrencyDecimals, quoteCurrencyDecimals); + return fixedPointToDecimalString(sqrtRatioX96, baseAddressDecimals, quoteAddressDecimals); } } @@ -306,17 +305,17 @@ library Descriptor { /// @notice Adjusts the sqrt price for different currencies with different decimals /// @param sqrtRatioX96 The sqrt price at a specific tick - /// @param baseCurrencyDecimals The decimals of the base currency - /// @param quoteCurrencyDecimals The decimals of the quote currency + /// @param baseAddressDecimals The decimals of the base address + /// @param quoteAddressDecimals The decimals of the quote address /// @return adjustedSqrtRatioX96 The adjusted sqrt price - function adjustForDecimalPrecision(uint160 sqrtRatioX96, uint8 baseCurrencyDecimals, uint8 quoteCurrencyDecimals) + function adjustForDecimalPrecision(uint160 sqrtRatioX96, uint8 baseAddressDecimals, uint8 quoteAddressDecimals) private pure returns (uint256 adjustedSqrtRatioX96) { - uint256 difference = abs(int256(uint256(baseCurrencyDecimals)) - (int256(uint256(quoteCurrencyDecimals)))); + uint256 difference = abs(int256(uint256(baseAddressDecimals)) - (int256(uint256(quoteAddressDecimals)))); if (difference > 0 && difference <= 18) { - if (baseCurrencyDecimals > quoteCurrencyDecimals) { + if (baseAddressDecimals > quoteAddressDecimals) { adjustedSqrtRatioX96 = sqrtRatioX96 * (10 ** (difference / 2)); if (difference % 2 == 1) { adjustedSqrtRatioX96 = FullMath.mulDiv(adjustedSqrtRatioX96, sqrt10X128, 1 << 128); @@ -339,13 +338,13 @@ library Descriptor { return uint256(x >= 0 ? x : -x); } - function fixedPointToDecimalString(uint160 sqrtRatioX96, uint8 baseCurrencyDecimals, uint8 quoteCurrencyDecimals) + function fixedPointToDecimalString(uint160 sqrtRatioX96, uint8 baseAddressDecimals, uint8 quoteAddressDecimals) internal pure returns (string memory) { uint256 adjustedSqrtRatioX96 = - adjustForDecimalPrecision(sqrtRatioX96, baseCurrencyDecimals, quoteCurrencyDecimals); + adjustForDecimalPrecision(sqrtRatioX96, baseAddressDecimals, quoteAddressDecimals); uint256 value = FullMath.mulDiv(adjustedSqrtRatioX96, adjustedSqrtRatioX96, 1 << 64); bool priceBelow1 = adjustedSqrtRatioX96 < 2 ** 96; @@ -462,27 +461,27 @@ library Descriptor { /// @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)), + quoteAddress: addressToString(params.quoteAddress), + baseAddress: addressToString(params.baseAddress), hooks: params.hooks, - quoteCurrencySymbol: params.quoteCurrencySymbol, - baseCurrencySymbol: params.baseCurrencySymbol, + quoteAddressSymbol: params.quoteAddressSymbol, + baseAddressSymbol: params.baseAddressSymbol, 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) + color0: addressToColorHex(uint256(uint160(params.quoteAddress)), 136), + color1: addressToColorHex(uint256(uint160(params.baseAddress)), 136), + color2: addressToColorHex(uint256(uint160(params.quoteAddress)), 0), + color3: addressToColorHex(uint256(uint160(params.baseAddress)), 0), + x1: scale(getCircleCoord(uint256(uint160(params.quoteAddress)), 16, params.tokenId), 0, 255, 16, 274), + y1: scale(getCircleCoord(uint256(uint160(params.baseAddress)), 16, params.tokenId), 0, 255, 100, 484), + x2: scale(getCircleCoord(uint256(uint160(params.quoteAddress)), 32, params.tokenId), 0, 255, 16, 274), + y2: scale(getCircleCoord(uint256(uint160(params.baseAddress)), 32, params.tokenId), 0, 255, 100, 484), + x3: scale(getCircleCoord(uint256(uint160(params.quoteAddress)), 48, params.tokenId), 0, 255, 16, 274), + y3: scale(getCircleCoord(uint256(uint160(params.baseAddress)), 48, params.tokenId), 0, 255, 100, 484) }); return SVG.generateSVG(svgParams); @@ -511,15 +510,15 @@ library Descriptor { 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 addressToColorHex(uint256 addr, uint256 offset) internal pure returns (string memory str) { + return string((addr >> offset).toHexStringNoPrefix(3)); } - function getCircleCoord(uint256 currency, uint256 offset, uint256 tokenId) internal pure returns (uint256) { - return (sliceCurrencyHex(currency, offset) * tokenId) % 255; + function getCircleCoord(uint256 addr, uint256 offset, uint256 tokenId) internal pure returns (uint256) { + return (sliceAddressHex(addr, offset) * tokenId) % 255; } - function sliceCurrencyHex(uint256 currency, uint256 offset) internal pure returns (uint256) { - return uint256(uint8(currency >> offset)); + function sliceAddressHex(uint256 addr, uint256 offset) internal pure returns (uint256) { + return uint256(uint8(addr >> offset)); } } diff --git a/src/libraries/SVG.sol b/src/libraries/SVG.sol index dea00e66..0da00a50 100644 --- a/src/libraries/SVG.sol +++ b/src/libraries/SVG.sol @@ -1,7 +1,6 @@ // SPDX-License-Identifier: UNLICENSED 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 {BitMath} from "@uniswap/v4-core/src/libraries/BitMath.sol"; import {Strings} from "openzeppelin-contracts/contracts/utils/Strings.sol"; @@ -26,11 +25,11 @@ library SVG { string constant curve8 = "M1 1C1 97 49 145 145 145"; struct SVGParams { - string quoteCurrency; - string baseCurrency; + string quoteAddress; + string baseAddress; address hooks; - string quoteCurrencySymbol; - string baseCurrencySymbol; + string quoteAddressSymbol; + string baseAddressSymbol; string feeTier; int24 tickLower; int24 tickUpper; @@ -57,9 +56,9 @@ library SVG { abi.encodePacked( generateSVGDefs(params), generateSVGBorderText( - params.quoteCurrency, params.baseCurrency, params.quoteCurrencySymbol, params.baseCurrencySymbol + params.quoteAddress, params.baseAddress, params.quoteAddressSymbol, params.baseAddressSymbol ), - generateSVGCardMantle(params.quoteCurrencySymbol, params.baseCurrencySymbol, params.feeTier), + generateSVGCardMantle(params.quoteAddressSymbol, params.baseAddressSymbol, params.feeTier), generageSvgCurve(params.tickLower, params.tickUpper, params.tickSpacing, params.overRange), generateSVGPositionDataAndLocationCurve( params.tokenId.toString(), params.hooks, params.tickLower, params.tickUpper @@ -158,61 +157,61 @@ library SVG { ); } - /// @notice Generate the SVG for the moving border text displaying the quote and base currency addresses with their symbols - /// @param quoteCurrency The quote currency - /// @param baseCurrency The base currency - /// @param quoteCurrencySymbol The quote currency symbol - /// @param baseCurrencySymbol The base currency symbol + /// @notice Generate the SVG for the moving border text displaying the quote and base addresses with their symbols + /// @param quoteAddress The quote address + /// @param baseAddress The base address + /// @param quoteAddressSymbol The quote address symbol + /// @param baseAddressSymbol The base address symbol /// @return svg The SVG for the border NFT's border text function generateSVGBorderText( - string memory quoteCurrency, - string memory baseCurrency, - string memory quoteCurrencySymbol, - string memory baseCurrencySymbol + string memory quoteAddress, + string memory baseAddress, + string memory quoteAddressSymbol, + string memory baseAddressSymbol ) private pure returns (string memory svg) { svg = string( abi.encodePacked( '', '', - baseCurrency, + baseAddress, unicode" • ", - baseCurrencySymbol, + baseAddressSymbol, ' ', ' ', - baseCurrency, + baseAddress, unicode" • ", - baseCurrencySymbol, + baseAddressSymbol, ' ', '', - quoteCurrency, + quoteAddress, unicode" • ", - quoteCurrencySymbol, + quoteAddressSymbol, ' ', - quoteCurrency, + quoteAddress, unicode" • ", - quoteCurrencySymbol, + quoteAddressSymbol, ' ' ) ); } - /// @notice Generate the SVG for the card mantle displaying the quote and base currency symbols and fee tier - /// @param quoteCurrencySymbol The quote currency symbol - /// @param baseCurrencySymbol The base currency symbol + /// @notice Generate the SVG for the card mantle displaying the quote and base address symbols and fee tier + /// @param quoteAddressSymbol The quote address symbol + /// @param baseAddressSymbol The base address symbol /// @param feeTier The fee tier /// @return svg The SVG for the card mantle function generateSVGCardMantle( - string memory quoteCurrencySymbol, - string memory baseCurrencySymbol, + string memory quoteAddressSymbol, + string memory baseAddressSymbol, string memory feeTier ) private pure returns (string memory svg) { svg = string( abi.encodePacked( ' ', - quoteCurrencySymbol, + quoteAddressSymbol, "/", - baseCurrencySymbol, + baseAddressSymbol, '', feeTier, "", diff --git a/src/libraries/SafeCurrencyMetadata.sol b/src/libraries/SafeERC20Metadata.sol similarity index 64% rename from src/libraries/SafeCurrencyMetadata.sol rename to src/libraries/SafeERC20Metadata.sol index f4d7cf27..1346a3f7 100644 --- a/src/libraries/SafeCurrencyMetadata.sol +++ b/src/libraries/SafeERC20Metadata.sol @@ -2,41 +2,36 @@ pragma solidity ^0.8.0; import {IERC20Metadata} from "openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol"; -import {Currency, CurrencyLibrary} from "@uniswap/v4-core/src/types/Currency.sol"; import {AddressStringUtil} from "./AddressStringUtil.sol"; -/// @title SafeCurrencyMetadata +/// @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 SafeCurrencyMetadata { - using CurrencyLibrary for Currency; - +library SafeERC20Metadata { /// @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 addr The address /// @param nativeLabel The native label /// @return the token symbol - function currencySymbol(Currency currency, string memory nativeLabel) internal view returns (string memory) { - if (currency.isAddressZero()) { + function addressSymbol(address addr, string memory nativeLabel) internal view returns (string memory) { + if (addr == address(0)) { return nativeLabel; } - address currencyAddress = Currency.unwrap(currency); - string memory symbol = callAndParseStringReturn(currencyAddress, IERC20Metadata.symbol.selector); + string memory symbol = callAndParseStringReturn(addr, IERC20Metadata.symbol.selector); if (bytes(symbol).length == 0) { // fallback to 6 uppercase hex of address - return addressToSymbol(currencyAddress); + return addressToSymbol(addr); } return symbol; } /// @notice attempts to extract the token decimals, returns 0 if not implemented or not a uint8 - /// @param currency The currency + /// @param addr The address /// @return the token decimals - function currencyDecimals(Currency currency) internal view returns (uint8) { - if (currency.isAddressZero()) { + function addressDecimals(address addr) internal view returns (uint8) { + if (addr == address(0)) { return 18; } - (bool success, bytes memory data) = - Currency.unwrap(currency).staticcall(abi.encodeCall(IERC20Metadata.decimals, ())); + (bool success, bytes memory data) = addr.staticcall(abi.encodeCall(IERC20Metadata.decimals, ())); if (!success) { return 0; } @@ -64,18 +59,18 @@ library SafeCurrencyMetadata { } /// @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 + /// @param addr The address /// @return the symbol - function addressToSymbol(address currencyAddress) private pure returns (string memory) { - return AddressStringUtil.toAsciiString(currencyAddress, 6); + function addressToSymbol(address addr) private pure returns (string memory) { + return AddressStringUtil.toAsciiString(addr, 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 addr The address /// @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)); + function callAndParseStringReturn(address addr, bytes4 selector) private view returns (string memory) { + (bool success, bytes memory data) = addr.staticcall(abi.encodeWithSelector(selector)); // if not implemented, return empty string if (!success) { return ""; diff --git a/test/PositionDescriptor.t.sol b/test/PositionDescriptor.t.sol index dabe8393..381a4cdb 100644 --- a/test/PositionDescriptor.t.sol +++ b/test/PositionDescriptor.t.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.24; import "forge-std/Test.sol"; import {PositionDescriptor} from "../src/PositionDescriptor.sol"; -import {CurrencyRatioSortOrder} from "../src/libraries/CurrencyRatioSortOrder.sol"; +import {AddressRatioSortOrder} from "../src/libraries/AddressRatioSortOrder.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"; @@ -44,30 +44,30 @@ contract PositionDescriptorTest is Test, PosmTestSetup, GasSnapshot { function test_setup_succeeds() public view { assertEq(address(positionDescriptor.poolManager()), address(manager)); assertEq(positionDescriptor.wrappedNative(), WETH9); - assertEq(positionDescriptor.nativeCurrencyLabel(), nativeCurrencyLabel); + assertEq(positionDescriptor.nativeAddressLabel(), nativeCurrencyLabel); } - function test_currencyRatioPriority_mainnet_succeeds() public { + function test_addressRatioPriority_mainnet_succeeds() public { vm.chainId(1); - 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); - assertEq(positionDescriptor.currencyRatioPriority(TBTC), CurrencyRatioSortOrder.DENOMINATOR_MORE); - assertEq(positionDescriptor.currencyRatioPriority(WBTC), CurrencyRatioSortOrder.DENOMINATOR_MOST); - assertEq(positionDescriptor.currencyRatioPriority(makeAddr("ALICE")), 0); + assertEq(positionDescriptor.addressRatioPriority(WETH9), AddressRatioSortOrder.DENOMINATOR); + assertEq(positionDescriptor.addressRatioPriority(address(0)), AddressRatioSortOrder.DENOMINATOR); + assertEq(positionDescriptor.addressRatioPriority(USDC), AddressRatioSortOrder.NUMERATOR_MOST); + assertEq(positionDescriptor.addressRatioPriority(USDT), AddressRatioSortOrder.NUMERATOR_MORE); + assertEq(positionDescriptor.addressRatioPriority(DAI), AddressRatioSortOrder.NUMERATOR); + assertEq(positionDescriptor.addressRatioPriority(TBTC), AddressRatioSortOrder.DENOMINATOR_MORE); + assertEq(positionDescriptor.addressRatioPriority(WBTC), AddressRatioSortOrder.DENOMINATOR_MOST); + assertEq(positionDescriptor.addressRatioPriority(makeAddr("ALICE")), 0); } - function test_currencyRatioPriority_notMainnet_succeeds() public { - 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); - assertEq(positionDescriptor.currencyRatioPriority(TBTC), 0); - assertEq(positionDescriptor.currencyRatioPriority(WBTC), 0); - assertEq(positionDescriptor.currencyRatioPriority(makeAddr("ALICE")), 0); + function test_addressRatioPriority_notMainnet_succeeds() public { + assertEq(positionDescriptor.addressRatioPriority(WETH9), AddressRatioSortOrder.DENOMINATOR); + assertEq(positionDescriptor.addressRatioPriority(address(0)), AddressRatioSortOrder.DENOMINATOR); + assertEq(positionDescriptor.addressRatioPriority(USDC), 0); + assertEq(positionDescriptor.addressRatioPriority(USDT), 0); + assertEq(positionDescriptor.addressRatioPriority(DAI), 0); + assertEq(positionDescriptor.addressRatioPriority(TBTC), 0); + assertEq(positionDescriptor.addressRatioPriority(WBTC), 0); + assertEq(positionDescriptor.addressRatioPriority(makeAddr("ALICE")), 0); } function test_flipRatio_succeeds() public { @@ -122,7 +122,7 @@ contract PositionDescriptorTest is Test, PosmTestSetup, GasSnapshot { assertEq(token.name, "Uniswap - 0.3% - TEST/TEST - 1.0060<>1.0121"); assertEq( token.description, - unicode"This NFT represents a liquidity position in a Uniswap v4 TEST-TEST pool. The owner of this NFT can modify or redeem the position.\n\nPool Manager Address: 0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f\nTEST Address: 0x5991a2df15a8f6a256d3ec51e99254cd3fb576a9\nTEST Address: 0x2e234dae75c793f67a35089c9d99245e1c58470b\nHook Address: 0x0000000000000000000000000000000000000000\nFee Tier: 0.3%\nToken ID: 1\n\n⚠️ DISCLAIMER: Due diligence is imperative when assessing this NFT. Make sure currency addresses match the expected currencies, as currency symbols may be imitated." + unicode"This NFT represents a liquidity position in a Uniswap v4 TEST-TEST pool. The owner of this NFT can modify or redeem the position.\n\nPool Manager Address: 0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f\nTEST Address: 0x5991a2df15a8f6a256d3ec51e99254cd3fb576a9\nTEST Address: 0x2e234dae75c793f67a35089c9d99245e1c58470b\nHook Address: 0x0000000000000000000000000000000000000000\nFee Tier: 0.3%\nToken ID: 1\n\n⚠️ DISCLAIMER: Due diligence is imperative when assessing this NFT. Make sure addresses match the expected addresses, as symbols may be imitated." ); } From 52dc6dd0ba8fab565c373ac5c2fbb0179e12435c Mon Sep 17 00:00:00 2001 From: dianakocsis Date: Mon, 28 Oct 2024 17:00:04 -0400 Subject: [PATCH 2/8] add test for native --- test/PositionDescriptor.t.sol | 48 +++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/test/PositionDescriptor.t.sol b/test/PositionDescriptor.t.sol index 381a4cdb..e56bfae0 100644 --- a/test/PositionDescriptor.t.sol +++ b/test/PositionDescriptor.t.sol @@ -12,6 +12,7 @@ import {PosmTestSetup} from "./shared/PosmTestSetup.sol"; import {ActionConstants} from "../src/libraries/ActionConstants.sol"; import {Base64} from "./base64.sol"; import {GasSnapshot} from "forge-gas-snapshot/GasSnapshot.sol"; +import {CurrencyLibrary} from "@uniswap/v4-core/src/types/Currency.sol"; contract PositionDescriptorTest is Test, PosmTestSetup, GasSnapshot { using Base64 for string; @@ -126,6 +127,53 @@ contract PositionDescriptorTest is Test, PosmTestSetup, GasSnapshot { ); } + function test_native_tokenURI_succeeds() public { + (nativeKey,) = initPool(CurrencyLibrary.ADDRESS_ZERO, currency1, IHooks(address(0)), 3000, SQRT_PRICE_1_1); + int24 tickLower = int24(nativeKey.tickSpacing); + int24 tickUpper = int24(nativeKey.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: nativeKey, tickLower: tickLower, tickUpper: tickUpper}); + uint256 tokenId = lpm.nextTokenId(); + mintWithNative(SQRT_PRICE_1_1, 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; + + string memory uri = positionDescriptor.tokenURI(lpm, tokenId); + // Convert the uri to bytes + bytes memory uriBytes = bytes(uri); + + // Slice the uri to get only the base64-encoded part + bytes memory base64Part = new bytes(uriBytes.length - prefixLength); + + for (uint256 i = 0; i < base64Part.length; i++) { + base64Part[i] = uriBytes[i + prefixLength]; + } + + // Decode the base64-encoded part + bytes memory decoded = Base64.decode(string(base64Part)); + string memory json = string(decoded); + + // decode json + bytes memory data = vm.parseJson(json); + Token memory token = abi.decode(data, (Token)); + + assertEq(token.name, "Uniswap - 0.3% - TEST/ETH - 1.0060<>1.0121"); + assertEq( + token.description, + unicode"This NFT represents a liquidity position in a Uniswap v4 TEST-ETH pool. The owner of this NFT can modify or redeem the position.\n\nPool Manager Address: 0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f\nTEST Address: 0x5991a2df15a8f6a256d3ec51e99254cd3fb576a9\nETH Address: Native\nHook Address: 0x0000000000000000000000000000000000000000\nFee Tier: 0.3%\nToken ID: 1\n\n⚠️ DISCLAIMER: Due diligence is imperative when assessing this NFT. Make sure addresses match the expected addresses, as symbols may be imitated." + ); + } + function test_tokenURI_revertsWithInvalidTokenId() public { int24 tickLower = int24(key.tickSpacing); int24 tickUpper = int24(key.tickSpacing * 2); From 1bb53676f846455a5c7c2f6c6790adbeae4221a1 Mon Sep 17 00:00:00 2001 From: dianakocsis Date: Tue, 29 Oct 2024 12:05:56 -0400 Subject: [PATCH 3/8] erc20 -> address --- .../{SafeERC20Metadata.sol => SafeAddressMetadata.sol} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename src/libraries/{SafeERC20Metadata.sol => SafeAddressMetadata.sol} (98%) diff --git a/src/libraries/SafeERC20Metadata.sol b/src/libraries/SafeAddressMetadata.sol similarity index 98% rename from src/libraries/SafeERC20Metadata.sol rename to src/libraries/SafeAddressMetadata.sol index 1346a3f7..24e2b653 100644 --- a/src/libraries/SafeERC20Metadata.sol +++ b/src/libraries/SafeAddressMetadata.sol @@ -4,10 +4,10 @@ pragma solidity ^0.8.0; import {IERC20Metadata} from "openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import {AddressStringUtil} from "./AddressStringUtil.sol"; -/// @title SafeERC20Metadata +/// @title SafeAddressMetadata /// @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 SafeAddressMetadata { /// @notice attempts to extract the token symbol. if it does not implement symbol, returns a symbol derived from the address /// @param addr The address /// @param nativeLabel The native label From ea860c0022648fe37b12ff601249554673ec1c7c Mon Sep 17 00:00:00 2001 From: dianakocsis Date: Tue, 29 Oct 2024 15:51:02 -0400 Subject: [PATCH 4/8] format --- test/PositionDescriptor.t.sol | 131 +++++++++++++++++++++++++++++----- 1 file changed, 114 insertions(+), 17 deletions(-) diff --git a/test/PositionDescriptor.t.sol b/test/PositionDescriptor.t.sol index 3084b1ad..495f280b 100644 --- a/test/PositionDescriptor.t.sol +++ b/test/PositionDescriptor.t.sol @@ -131,16 +131,62 @@ contract PositionDescriptorTest is Test, PosmTestSetup, GasSnapshot { string memory currency0Address = toHexString(Currency.unwrap(currency0)); string memory currency1Address = toHexString(Currency.unwrap(currency1)); string memory id = uintToString(tokenId); - string memory hookAddress = address(key.hooks) == address(0) ? "No Hook" : string(abi.encodePacked("0x", toHexString(address(key.hooks)))); + string memory hookAddress = address(key.hooks) == address(0) + ? "No Hook" + : string(abi.encodePacked("0x", toHexString(address(key.hooks)))); string memory fee = Descriptor.feeToPercentString(key.fee); - string memory tickToDecimal0 = Descriptor.tickToDecimalString(tickLower, key.tickSpacing, SafeAddressMetadata.addressDecimals(Currency.unwrap(currency0)), SafeAddressMetadata.addressDecimals(Currency.unwrap(currency1)), false); - string memory tickToDecimal1 = Descriptor.tickToDecimalString(tickUpper, key.tickSpacing, SafeAddressMetadata.addressDecimals(Currency.unwrap(currency0)), SafeAddressMetadata.addressDecimals(Currency.unwrap(currency1)), false); + string memory tickToDecimal0 = Descriptor.tickToDecimalString( + tickLower, + key.tickSpacing, + SafeAddressMetadata.addressDecimals(Currency.unwrap(currency0)), + SafeAddressMetadata.addressDecimals(Currency.unwrap(currency1)), + false + ); + string memory tickToDecimal1 = Descriptor.tickToDecimalString( + tickUpper, + key.tickSpacing, + SafeAddressMetadata.addressDecimals(Currency.unwrap(currency0)), + SafeAddressMetadata.addressDecimals(Currency.unwrap(currency1)), + false + ); - assertEq(token.name, string(abi.encodePacked("Uniswap - ", fee, " - ", symbol1, "/", symbol0, " - ", tickToDecimal0, "<>", tickToDecimal1))); + assertEq( + token.name, + string( + abi.encodePacked( + "Uniswap - ", fee, " - ", symbol1, "/", symbol0, " - ", tickToDecimal0, "<>", tickToDecimal1 + ) + ) + ); assertEq( token.description, - string(abi.encodePacked(unicode"This NFT represents a liquidity position in a Uniswap v4 ", symbol1, "-", symbol0, " pool. The owner of this NFT can modify or redeem the position.\n\nPool Manager Address: ", managerAddress, "\n", symbol1, " Address: ", currency1Address, "\n", symbol0, " Address: ", currency0Address, "\nHook Address: ", hookAddress, "\nFee Tier: ", fee, "\nToken ID: ", id, "\n\n", unicode"⚠️ DISCLAIMER: Due diligence is imperative when assessing this NFT. Make sure addresses match the expected addresses, as symbols may be imitated.") - )); + string( + abi.encodePacked( + unicode"This NFT represents a liquidity position in a Uniswap v4 ", + symbol1, + "-", + symbol0, + " pool. The owner of this NFT can modify or redeem the position.\n\nPool Manager Address: ", + managerAddress, + "\n", + symbol1, + " Address: ", + currency1Address, + "\n", + symbol0, + " Address: ", + currency0Address, + "\nHook Address: ", + hookAddress, + "\nFee Tier: ", + fee, + "\nToken ID: ", + id, + "\n\n", + unicode"⚠️ DISCLAIMER: Due diligence is imperative when assessing this NFT. Make sure addresses match the expected addresses, as symbols may be imitated." + ) + ) + ); } function test_native_tokenURI_succeeds() public { @@ -183,24 +229,75 @@ contract PositionDescriptorTest is Test, PosmTestSetup, GasSnapshot { bytes memory data = vm.parseJson(json); Token memory token = abi.decode(data, (Token)); - // quote is currency1, base is currency0 - string memory symbol0 = SafeAddressMetadata.addressSymbol(Currency.unwrap(nativeKey.currency0), nativeCurrencyLabel); - string memory symbol1 = SafeAddressMetadata.addressSymbol(Currency.unwrap(nativeKey.currency1), nativeCurrencyLabel); + string memory symbol0 = + SafeAddressMetadata.addressSymbol(Currency.unwrap(nativeKey.currency0), nativeCurrencyLabel); + string memory symbol1 = + SafeAddressMetadata.addressSymbol(Currency.unwrap(nativeKey.currency1), nativeCurrencyLabel); string memory managerAddress = toHexString(address(manager)); - string memory currency0Address = Currency.unwrap(nativeKey.currency0) == address(0) ? "Native" : toHexString(Currency.unwrap(nativeKey.currency0)); - string memory currency1Address = Currency.unwrap(nativeKey.currency1) == address(0) ? "Native" : toHexString(Currency.unwrap(nativeKey.currency1)); + string memory currency0Address = Currency.unwrap(nativeKey.currency0) == address(0) + ? "Native" + : toHexString(Currency.unwrap(nativeKey.currency0)); + string memory currency1Address = Currency.unwrap(nativeKey.currency1) == address(0) + ? "Native" + : toHexString(Currency.unwrap(nativeKey.currency1)); string memory id = uintToString(tokenId); - string memory hookAddress = address(nativeKey.hooks) == address(0) ? "No Hook" : string(abi.encodePacked("0x", toHexString(address(nativeKey.hooks)))); + string memory hookAddress = address(nativeKey.hooks) == address(0) + ? "No Hook" + : string(abi.encodePacked("0x", toHexString(address(nativeKey.hooks)))); string memory fee = Descriptor.feeToPercentString(nativeKey.fee); - string memory tickToDecimal0 = Descriptor.tickToDecimalString(tickLower, nativeKey.tickSpacing, SafeAddressMetadata.addressDecimals(Currency.unwrap(currency0)), SafeAddressMetadata.addressDecimals(Currency.unwrap(currency1)), false); - string memory tickToDecimal1 = Descriptor.tickToDecimalString(tickUpper, nativeKey.tickSpacing, SafeAddressMetadata.addressDecimals(Currency.unwrap(currency0)), SafeAddressMetadata.addressDecimals(Currency.unwrap(currency1)), false); + string memory tickToDecimal0 = Descriptor.tickToDecimalString( + tickLower, + nativeKey.tickSpacing, + SafeAddressMetadata.addressDecimals(Currency.unwrap(currency0)), + SafeAddressMetadata.addressDecimals(Currency.unwrap(currency1)), + false + ); + string memory tickToDecimal1 = Descriptor.tickToDecimalString( + tickUpper, + nativeKey.tickSpacing, + SafeAddressMetadata.addressDecimals(Currency.unwrap(currency0)), + SafeAddressMetadata.addressDecimals(Currency.unwrap(currency1)), + false + ); - assertEq(token.name, string(abi.encodePacked("Uniswap - ", fee, " - ", symbol1, "/", symbol0, " - ", tickToDecimal0, "<>", tickToDecimal1))); + assertEq( + token.name, + string( + abi.encodePacked( + "Uniswap - ", fee, " - ", symbol1, "/", symbol0, " - ", tickToDecimal0, "<>", tickToDecimal1 + ) + ) + ); assertEq( token.description, - string(abi.encodePacked(unicode"This NFT represents a liquidity position in a Uniswap v4 ", symbol1, "-", symbol0, " pool. The owner of this NFT can modify or redeem the position.\n\nPool Manager Address: ", managerAddress, "\n", symbol1, " Address: ", currency1Address, "\n", symbol0, " Address: ", currency0Address, "\nHook Address: ", hookAddress, "\nFee Tier: ", fee, "\nToken ID: ", id, "\n\n", unicode"⚠️ DISCLAIMER: Due diligence is imperative when assessing this NFT. Make sure addresses match the expected addresses, as symbols may be imitated.") - )); + string( + abi.encodePacked( + unicode"This NFT represents a liquidity position in a Uniswap v4 ", + symbol1, + "-", + symbol0, + " pool. The owner of this NFT can modify or redeem the position.\n\nPool Manager Address: ", + managerAddress, + "\n", + symbol1, + " Address: ", + currency1Address, + "\n", + symbol0, + " Address: ", + currency0Address, + "\nHook Address: ", + hookAddress, + "\nFee Tier: ", + fee, + "\nToken ID: ", + id, + "\n\n", + unicode"⚠️ DISCLAIMER: Due diligence is imperative when assessing this NFT. Make sure addresses match the expected addresses, as symbols may be imitated." + ) + ) + ); } function test_tokenURI_revertsWithInvalidTokenId() public { From c85228e7dc5b0f6b574ed75db7903c11b6c1847f Mon Sep 17 00:00:00 2001 From: dianakocsis Date: Tue, 29 Oct 2024 16:02:39 -0400 Subject: [PATCH 5/8] another test --- test/PositionDescriptor.t.sol | 6 ++++++ test/libraries/SVG.t.sol | 7 +++++++ 2 files changed, 13 insertions(+) diff --git a/test/PositionDescriptor.t.sol b/test/PositionDescriptor.t.sol index 495f280b..6c24a4af 100644 --- a/test/PositionDescriptor.t.sol +++ b/test/PositionDescriptor.t.sol @@ -125,6 +125,8 @@ contract PositionDescriptorTest is Test, PosmTestSetup, GasSnapshot { Token memory token = abi.decode(data, (Token)); // quote is currency1, base is currency0 + assertFalse(positionDescriptor.flipRatio(Currency.unwrap(key.currency0), Currency.unwrap(key.currency1))); + string memory symbol0 = SafeAddressMetadata.addressSymbol(Currency.unwrap(currency0), nativeCurrencyLabel); string memory symbol1 = SafeAddressMetadata.addressSymbol(Currency.unwrap(currency1), nativeCurrencyLabel); string memory managerAddress = toHexString(address(manager)); @@ -230,6 +232,10 @@ contract PositionDescriptorTest is Test, PosmTestSetup, GasSnapshot { Token memory token = abi.decode(data, (Token)); // quote is currency1, base is currency0 + assertFalse( + positionDescriptor.flipRatio(Currency.unwrap(nativeKey.currency0), Currency.unwrap(nativeKey.currency1)) + ); + string memory symbol0 = SafeAddressMetadata.addressSymbol(Currency.unwrap(nativeKey.currency0), nativeCurrencyLabel); string memory symbol1 = diff --git a/test/libraries/SVG.t.sol b/test/libraries/SVG.t.sol index c322483e..915557cb 100644 --- a/test/libraries/SVG.t.sol +++ b/test/libraries/SVG.t.sol @@ -47,4 +47,11 @@ contract DescriptorTest is Test { result = SVG.isRare(2, 0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB); assertFalse(result); } + + function test_substring_succeeds() public pure { + string memory result = SVG.substring("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", 0, 5); + assertEq(result, "0xC02"); + result = SVG.substring("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", 39, 42); + assertEq(result, "Cc2"); + } } From d7cb3eebad6f9daad90eb2de0fa062800c0a5225 Mon Sep 17 00:00:00 2001 From: dianakocsis Date: Thu, 31 Oct 2024 12:09:10 -0400 Subject: [PATCH 6/8] revert name change --- src/PositionDescriptor.sol | 56 ++++---- ...rtOrder.sol => CurrencyRatioSortOrder.sol} | 8 +- src/libraries/Descriptor.sol | 128 +++++++++--------- src/libraries/SVG.sol | 60 ++++---- ...sMetadata.sol => SafeCurrencyMetadata.sol} | 40 +++--- test/PositionDescriptor.t.sol | 70 +++++----- test/libraries/SafeAddressMetadata.t.sol | 16 --- test/libraries/SafeCurrencyMetadata.t.sol | 16 +++ 8 files changed, 197 insertions(+), 197 deletions(-) rename src/libraries/{AddressRatioSortOrder.sol => CurrencyRatioSortOrder.sol} (65%) rename src/libraries/{SafeAddressMetadata.sol => SafeCurrencyMetadata.sol} (71%) delete mode 100644 test/libraries/SafeAddressMetadata.t.sol create mode 100644 test/libraries/SafeCurrencyMetadata.t.sol diff --git a/src/PositionDescriptor.sol b/src/PositionDescriptor.sol index 1ae83856..40a0313f 100644 --- a/src/PositionDescriptor.sol +++ b/src/PositionDescriptor.sol @@ -10,8 +10,8 @@ import {IPositionManager} from "./interfaces/IPositionManager.sol"; import {IPositionDescriptor} from "./interfaces/IPositionDescriptor.sol"; import {PositionInfo, PositionInfoLibrary} from "./libraries/PositionInfoLibrary.sol"; import {Descriptor} from "./libraries/Descriptor.sol"; -import {AddressRatioSortOrder} from "./libraries/AddressRatioSortOrder.sol"; -import {SafeAddressMetadata} from "./libraries/SafeAddressMetadata.sol"; +import {CurrencyRatioSortOrder} from "./libraries/CurrencyRatioSortOrder.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 @@ -31,14 +31,14 @@ contract PositionDescriptor is IPositionDescriptor { address private constant WBTC = 0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599; address public immutable wrappedNative; - string public nativeAddressLabel; + string public nativeCurrencyLabel; IPoolManager public immutable poolManager; - constructor(IPoolManager _poolManager, address _wrappedNative, string memory _nativeAddressLabel) { + constructor(IPoolManager _poolManager, address _wrappedNative, string memory _nativeCurrencyLabel) { poolManager = _poolManager; wrappedNative = _wrappedNative; - nativeAddressLabel = _nativeAddressLabel; + nativeCurrencyLabel = _nativeCurrencyLabel; } /// @inheritdoc IPositionDescriptor @@ -54,27 +54,27 @@ contract PositionDescriptor is IPositionDescriptor { } (, int24 tick,,) = poolManager.getSlot0(poolKey.toId()); - address address0 = Currency.unwrap(poolKey.currency0); - address address1 = Currency.unwrap(poolKey.currency1); + address currency0 = Currency.unwrap(poolKey.currency0); + address currency1 = Currency.unwrap(poolKey.currency1); - // If possible, flip addresses to get the larger one as the base, so that the price (quote/base) is more readable - // flip if address0 priority is greater than address1 priority - bool _flipRatio = flipRatio(address0, address1); + // If possible, flip currencies to get the larger one as the base, so that the price (quote/base) is more readable + // flip if currency0 priority is greater than currency1 priority + bool _flipRatio = flipRatio(currency0, currency1); - // If not flipped, quote address is address1, base address is address0 - // If flipped, quote address is address0, base address is address1 - address quoteAddress = !_flipRatio ? address1 : address0; - address baseAddress = !_flipRatio ? address0 : address1; + // If not flipped, quote currency is currency1, base currency is currency0 + // If flipped, quote currency is currency0, base currency is currency1 + address quoteCurrency = !_flipRatio ? currency1 : currency0; + address baseCurrency = !_flipRatio ? currency0 : currency1; return Descriptor.constructTokenURI( Descriptor.ConstructTokenURIParams({ tokenId: tokenId, - quoteAddress: quoteAddress, - baseAddress: baseAddress, - quoteAddressSymbol: SafeAddressMetadata.addressSymbol(quoteAddress, nativeAddressLabel), - baseAddressSymbol: SafeAddressMetadata.addressSymbol(baseAddress, nativeAddressLabel), - quoteAddressDecimals: SafeAddressMetadata.addressDecimals(quoteAddress), - baseAddressDecimals: SafeAddressMetadata.addressDecimals(baseAddress), + quoteCurrency: quoteCurrency, + baseCurrency: 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(), @@ -92,32 +92,32 @@ contract PositionDescriptor is IPositionDescriptor { /// @param address1 The second address /// @return flipRatio True if address0 has higher priority than address1 function flipRatio(address address0, address address1) public view returns (bool) { - return addressRatioPriority(address0) > addressRatioPriority(address1); + return currencyRatioPriority(address0) > currencyRatioPriority(address1); } /// @notice Returns the priority of an address. /// For certain addresses on mainnet, the smaller the address, the higher the priority /// @param addr The address /// @return priority The priority of the address - function addressRatioPriority(address addr) public view returns (int256) { + function currencyRatioPriority(address addr) public view returns (int256) { // Addresses 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 address if (addr == address(0) || addr == wrappedNative) { - return AddressRatioSortOrder.DENOMINATOR; + return CurrencyRatioSortOrder.DENOMINATOR; } if (block.chainid == 1) { if (addr == USDC) { - return AddressRatioSortOrder.NUMERATOR_MOST; + return CurrencyRatioSortOrder.NUMERATOR_MOST; } else if (addr == USDT) { - return AddressRatioSortOrder.NUMERATOR_MORE; + return CurrencyRatioSortOrder.NUMERATOR_MORE; } else if (addr == DAI) { - return AddressRatioSortOrder.NUMERATOR; + return CurrencyRatioSortOrder.NUMERATOR; } else if (addr == TBTC) { - return AddressRatioSortOrder.DENOMINATOR_MORE; + return CurrencyRatioSortOrder.DENOMINATOR_MORE; } else if (addr == WBTC) { - return AddressRatioSortOrder.DENOMINATOR_MOST; + return CurrencyRatioSortOrder.DENOMINATOR_MOST; } else { return 0; } diff --git a/src/libraries/AddressRatioSortOrder.sol b/src/libraries/CurrencyRatioSortOrder.sol similarity index 65% rename from src/libraries/AddressRatioSortOrder.sol rename to src/libraries/CurrencyRatioSortOrder.sol index 71fdfcf9..1f3a719a 100644 --- a/src/libraries/AddressRatioSortOrder.sol +++ b/src/libraries/CurrencyRatioSortOrder.sol @@ -1,11 +1,11 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.0; -/// @title AddressRatioSortOrder -/// @notice Provides constants for sorting addresses when displaying price ratios -/// Addresses given larger values will be in the numerator of the price ratio +/// @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 AddressRatioSortOrder { +library CurrencyRatioSortOrder { int256 constant NUMERATOR_MOST = 300; int256 constant NUMERATOR_MORE = 200; int256 constant NUMERATOR = 100; diff --git a/src/libraries/Descriptor.sol b/src/libraries/Descriptor.sol index 01057eb0..76176a4c 100644 --- a/src/libraries/Descriptor.sol +++ b/src/libraries/Descriptor.sol @@ -22,12 +22,12 @@ library Descriptor { struct ConstructTokenURIParams { uint256 tokenId; - address quoteAddress; - address baseAddress; - string quoteAddressSymbol; - string baseAddressSymbol; - uint8 quoteAddressDecimals; - uint8 baseAddressDecimals; + address quoteCurrency; + address baseCurrency; + string quoteCurrencySymbol; + string baseCurrencySymbol; + uint8 quoteCurrencyDecimals; + uint8 baseCurrencyDecimals; bool flipRatio; int24 tickLower; int24 tickUpper; @@ -44,15 +44,15 @@ library Descriptor { function constructTokenURI(ConstructTokenURIParams memory params) internal pure returns (string memory) { string memory name = generateName(params, feeToPercentString(params.fee)); string memory descriptionPartOne = generateDescriptionPartOne( - escapeSpecialCharacters(params.quoteAddressSymbol), - escapeSpecialCharacters(params.baseAddressSymbol), + escapeSpecialCharacters(params.quoteCurrencySymbol), + escapeSpecialCharacters(params.baseCurrencySymbol), addressToString(params.poolManager) ); string memory descriptionPartTwo = generateDescriptionPartTwo( params.tokenId.toString(), - escapeSpecialCharacters(params.baseAddressSymbol), - params.quoteAddress == address(0) ? "Native" : addressToString(params.quoteAddress), - params.baseAddress == address(0) ? "Native" : addressToString(params.baseAddress), + escapeSpecialCharacters(params.baseCurrencySymbol), + params.quoteCurrency == address(0) ? "Native" : addressToString(params.quoteCurrency), + params.baseCurrency == address(0) ? "Native" : addressToString(params.baseCurrency), params.hooks == address(0) ? "No Hook" : addressToString(params.hooks), feeToPercentString(params.fee) ); @@ -108,56 +108,56 @@ library Descriptor { } /// @notice Generates the first part of the description for a Uniswap v4 NFT - /// @param quoteAddressSymbol The symbol of the quote address - /// @param baseAddressSymbol The symbol of the base address + /// @param quoteCurrencySymbol The symbol of the quote currency + /// @param baseCurrencySymbol The symbol of the base currency /// @param poolManager The address of the pool manager /// @return The first part of the description function generateDescriptionPartOne( - string memory quoteAddressSymbol, - string memory baseAddressSymbol, + string memory quoteCurrencySymbol, + string memory baseCurrencySymbol, string memory poolManager ) private pure returns (string memory) { - // displays quote address first, then base address + // displays quote currency first, then base currency return string( abi.encodePacked( "This NFT represents a liquidity position in a Uniswap v4 ", - quoteAddressSymbol, + quoteCurrencySymbol, "-", - baseAddressSymbol, + baseCurrencySymbol, " pool. ", "The owner of this NFT can modify or redeem the position.\\n", "\\nPool Manager Address: ", poolManager, "\\n", - quoteAddressSymbol + quoteCurrencySymbol ) ); } /// @notice Generates the second part of the description for a Uniswap v4 NFTs /// @param tokenId The token ID - /// @param baseAddressSymbol The symbol of the base address - /// @param quoteAddress The address of the quote address - /// @param baseAddress The address of the base address + /// @param baseCurrencySymbol The symbol of the base address + /// @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 /// @return The second part of the description function generateDescriptionPartTwo( string memory tokenId, - string memory baseAddressSymbol, - string memory quoteAddress, - string memory baseAddress, + 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: ", - quoteAddress, + quoteCurrency, "\\n", - baseAddressSymbol, + baseCurrencySymbol, " Address: ", - baseAddress, + baseCurrency, "\\nHook Address: ", hooks, "\\nFee Tier: ", @@ -165,7 +165,7 @@ library Descriptor { "\\nToken ID: ", tokenId, "\\n\\n", - unicode"⚠️ DISCLAIMER: Due diligence is imperative when assessing this NFT. Make sure addresses match the expected addresses, as symbols may be imitated." + unicode"⚠️ DISCLAIMER: Due diligence is imperative when assessing this NFT. Make sure currencies match the expected currencies, as currency symbols may be imitated." ) ); } @@ -179,29 +179,29 @@ library Descriptor { pure returns (string memory) { - // image shows in terms of price, ie quoteAddress/baseAddress + // image shows in terms of price, ie quoteCurrency/baseCurrency return string( abi.encodePacked( "Uniswap - ", feeTier, " - ", - escapeSpecialCharacters(params.quoteAddressSymbol), + escapeSpecialCharacters(params.quoteCurrencySymbol), "/", - escapeSpecialCharacters(params.baseAddressSymbol), + escapeSpecialCharacters(params.baseCurrencySymbol), " - ", tickToDecimalString( !params.flipRatio ? params.tickLower : params.tickUpper, params.tickSpacing, - params.baseAddressDecimals, - params.quoteAddressDecimals, + params.baseCurrencyDecimals, + params.quoteCurrencyDecimals, params.flipRatio ), "<>", tickToDecimalString( !params.flipRatio ? params.tickUpper : params.tickLower, params.tickSpacing, - params.baseAddressDecimals, - params.quoteAddressDecimals, + params.baseCurrencyDecimals, + params.quoteCurrencyDecimals, params.flipRatio ) ) @@ -261,15 +261,15 @@ library Descriptor { /// MIN or MAX are returned if tick is at the bottom or top of the price curve /// @param tick The tick (either tickLower or tickUpper) /// @param tickSpacing The tick spacing of the pool - /// @param baseAddressDecimals The decimals of the base address - /// @param quoteAddressDecimals The decimals of the quote address + /// @param baseCurrencyDecimals The decimals of the base address + /// @param quoteCurrencyDecimals The decimals of the quote address /// @param flipRatio True if the ratio was flipped /// @return The ratio value as a string function tickToDecimalString( int24 tick, int24 tickSpacing, - uint8 baseAddressDecimals, - uint8 quoteAddressDecimals, + uint8 baseCurrencyDecimals, + uint8 quoteCurrencyDecimals, bool flipRatio ) internal pure returns (string memory) { if (tick == (TickMath.MIN_TICK / tickSpacing) * tickSpacing) { @@ -281,7 +281,7 @@ library Descriptor { if (flipRatio) { sqrtRatioX96 = uint160(uint256(1 << 192) / sqrtRatioX96); } - return fixedPointToDecimalString(sqrtRatioX96, baseAddressDecimals, quoteAddressDecimals); + return fixedPointToDecimalString(sqrtRatioX96, baseCurrencyDecimals, quoteCurrencyDecimals); } } @@ -305,17 +305,17 @@ library Descriptor { /// @notice Adjusts the sqrt price for different currencies with different decimals /// @param sqrtRatioX96 The sqrt price at a specific tick - /// @param baseAddressDecimals The decimals of the base address - /// @param quoteAddressDecimals The decimals of the quote address + /// @param baseCurrencyDecimals The decimals of the base address + /// @param quoteCurrencyDecimals The decimals of the quote address /// @return adjustedSqrtRatioX96 The adjusted sqrt price - function adjustForDecimalPrecision(uint160 sqrtRatioX96, uint8 baseAddressDecimals, uint8 quoteAddressDecimals) + function adjustForDecimalPrecision(uint160 sqrtRatioX96, uint8 baseCurrencyDecimals, uint8 quoteCurrencyDecimals) private pure returns (uint256 adjustedSqrtRatioX96) { - uint256 difference = abs(int256(uint256(baseAddressDecimals)) - (int256(uint256(quoteAddressDecimals)))); + uint256 difference = abs(int256(uint256(baseCurrencyDecimals)) - (int256(uint256(quoteCurrencyDecimals)))); if (difference > 0 && difference <= 18) { - if (baseAddressDecimals > quoteAddressDecimals) { + if (baseCurrencyDecimals > quoteCurrencyDecimals) { adjustedSqrtRatioX96 = sqrtRatioX96 * (10 ** (difference / 2)); if (difference % 2 == 1) { adjustedSqrtRatioX96 = FullMath.mulDiv(adjustedSqrtRatioX96, sqrt10X128, 1 << 128); @@ -338,13 +338,13 @@ library Descriptor { return uint256(x >= 0 ? x : -x); } - function fixedPointToDecimalString(uint160 sqrtRatioX96, uint8 baseAddressDecimals, uint8 quoteAddressDecimals) + function fixedPointToDecimalString(uint160 sqrtRatioX96, uint8 baseCurrencyDecimals, uint8 quoteCurrencyDecimals) internal pure returns (string memory) { uint256 adjustedSqrtRatioX96 = - adjustForDecimalPrecision(sqrtRatioX96, baseAddressDecimals, quoteAddressDecimals); + adjustForDecimalPrecision(sqrtRatioX96, baseCurrencyDecimals, quoteCurrencyDecimals); uint256 value = FullMath.mulDiv(adjustedSqrtRatioX96, adjustedSqrtRatioX96, 1 << 64); bool priceBelow1 = adjustedSqrtRatioX96 < 2 ** 96; @@ -461,27 +461,27 @@ library Descriptor { /// @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({ - quoteAddress: addressToString(params.quoteAddress), - baseAddress: addressToString(params.baseAddress), + quoteCurrency: addressToString(params.quoteCurrency), + baseCurrency: addressToString(params.baseCurrency), hooks: params.hooks, - quoteAddressSymbol: params.quoteAddressSymbol, - baseAddressSymbol: params.baseAddressSymbol, + 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: addressToColorHex(uint256(uint160(params.quoteAddress)), 136), - color1: addressToColorHex(uint256(uint160(params.baseAddress)), 136), - color2: addressToColorHex(uint256(uint160(params.quoteAddress)), 0), - color3: addressToColorHex(uint256(uint160(params.baseAddress)), 0), - x1: scale(getCircleCoord(uint256(uint160(params.quoteAddress)), 16, params.tokenId), 0, 255, 16, 274), - y1: scale(getCircleCoord(uint256(uint160(params.baseAddress)), 16, params.tokenId), 0, 255, 100, 484), - x2: scale(getCircleCoord(uint256(uint160(params.quoteAddress)), 32, params.tokenId), 0, 255, 16, 274), - y2: scale(getCircleCoord(uint256(uint160(params.baseAddress)), 32, params.tokenId), 0, 255, 100, 484), - x3: scale(getCircleCoord(uint256(uint160(params.quoteAddress)), 48, params.tokenId), 0, 255, 16, 274), - y3: scale(getCircleCoord(uint256(uint160(params.baseAddress)), 48, params.tokenId), 0, 255, 100, 484) + color0: currencyToColorHex(uint256(uint160(params.quoteCurrency)), 136), + color1: currencyToColorHex(uint256(uint160(params.baseCurrency)), 136), + color2: currencyToColorHex(uint256(uint160(params.quoteCurrency)), 0), + color3: currencyToColorHex(uint256(uint160(params.baseCurrency)), 0), + x1: scale(getCircleCoord(uint256(uint160(params.quoteCurrency)), 16, params.tokenId), 0, 255, 16, 274), + y1: scale(getCircleCoord(uint256(uint160(params.baseCurrency)), 16, params.tokenId), 0, 255, 100, 484), + x2: scale(getCircleCoord(uint256(uint160(params.quoteCurrency)), 32, params.tokenId), 0, 255, 16, 274), + y2: scale(getCircleCoord(uint256(uint160(params.baseCurrency)), 32, params.tokenId), 0, 255, 100, 484), + x3: scale(getCircleCoord(uint256(uint160(params.quoteCurrency)), 48, params.tokenId), 0, 255, 16, 274), + y3: scale(getCircleCoord(uint256(uint160(params.baseCurrency)), 48, params.tokenId), 0, 255, 100, 484) }); return SVG.generateSVG(svgParams); @@ -514,8 +514,8 @@ library Descriptor { return ((n - inMn) * (outMx - outMn) / (inMx - inMn) + outMn).toString(); } - function addressToColorHex(uint256 addr, uint256 offset) internal pure returns (string memory str) { - return string((addr >> offset).toHexStringNoPrefix(3)); + function currencyToColorHex(uint256 currency, uint256 offset) internal pure returns (string memory str) { + return string((currency >> offset).toHexStringNoPrefix(3)); } function getCircleCoord(uint256 addr, uint256 offset, uint256 tokenId) internal pure returns (uint256) { diff --git a/src/libraries/SVG.sol b/src/libraries/SVG.sol index 6bbaf90a..89360e07 100644 --- a/src/libraries/SVG.sol +++ b/src/libraries/SVG.sol @@ -25,11 +25,11 @@ library SVG { string constant curve8 = "M1 1C1 97 49 145 145 145"; struct SVGParams { - string quoteAddress; - string baseAddress; + string quoteCurrency; + string baseCurrency; address hooks; - string quoteAddressSymbol; - string baseAddressSymbol; + string quoteCurrencySymbol; + string baseCurrencySymbol; string feeTier; int24 tickLower; int24 tickUpper; @@ -56,9 +56,9 @@ library SVG { abi.encodePacked( generateSVGDefs(params), generateSVGBorderText( - params.quoteAddress, params.baseAddress, params.quoteAddressSymbol, params.baseAddressSymbol + params.quoteCurrency, params.baseCurrency, params.quoteCurrencySymbol, params.baseCurrencySymbol ), - generateSVGCardMantle(params.quoteAddressSymbol, params.baseAddressSymbol, params.feeTier), + 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 @@ -157,61 +157,61 @@ library SVG { ); } - /// @notice Generate the SVG for the moving border text displaying the quote and base addresses with their symbols - /// @param quoteAddress The quote address - /// @param baseAddress The base address - /// @param quoteAddressSymbol The quote address symbol - /// @param baseAddressSymbol The base address symbol + /// @notice Generate the SVG for the moving border text displaying the quote and base currencies with their symbols + /// @param quoteCurrency The quote currency + /// @param baseCurrency The base currency + /// @param quoteCurrencySymbol The quote currency symbol + /// @param baseCurrencySymbol The base currency symbol /// @return svg The SVG for the border NFT's border text function generateSVGBorderText( - string memory quoteAddress, - string memory baseAddress, - string memory quoteAddressSymbol, - string memory baseAddressSymbol + string memory quoteCurrency, + string memory baseCurrency, + string memory quoteCurrencySymbol, + string memory baseCurrencySymbol ) private pure returns (string memory svg) { svg = string( abi.encodePacked( '', '', - baseAddress, + baseCurrency, unicode" • ", - baseAddressSymbol, + baseCurrencySymbol, ' ', ' ', - baseAddress, + baseCurrency, unicode" • ", - baseAddressSymbol, + baseCurrencySymbol, ' ', '', - quoteAddress, + quoteCurrency, unicode" • ", - quoteAddressSymbol, + quoteCurrencySymbol, ' ', - quoteAddress, + quoteCurrency, unicode" • ", - quoteAddressSymbol, + quoteCurrencySymbol, ' ' ) ); } - /// @notice Generate the SVG for the card mantle displaying the quote and base address symbols and fee tier - /// @param quoteAddressSymbol The quote address symbol - /// @param baseAddressSymbol The base address symbol + /// @notice Generate the SVG for the card mantle displaying the quote and base currency symbols and fee tier + /// @param quoteCurrencySymbol The quote currency symbol + /// @param baseCurrencySymbol The base currency symbol /// @param feeTier The fee tier /// @return svg The SVG for the card mantle function generateSVGCardMantle( - string memory quoteAddressSymbol, - string memory baseAddressSymbol, + string memory quoteCurrencySymbol, + string memory baseCurrencySymbol, string memory feeTier ) private pure returns (string memory svg) { svg = string( abi.encodePacked( ' ', - quoteAddressSymbol, + quoteCurrencySymbol, "/", - baseAddressSymbol, + baseCurrencySymbol, '', feeTier, "", diff --git a/src/libraries/SafeAddressMetadata.sol b/src/libraries/SafeCurrencyMetadata.sol similarity index 71% rename from src/libraries/SafeAddressMetadata.sol rename to src/libraries/SafeCurrencyMetadata.sol index ea97bfd7..b9115d55 100644 --- a/src/libraries/SafeAddressMetadata.sol +++ b/src/libraries/SafeCurrencyMetadata.sol @@ -4,24 +4,24 @@ pragma solidity ^0.8.0; import {IERC20Metadata} from "openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import {AddressStringUtil} from "./AddressStringUtil.sol"; -/// @title SafeAddressMetadata +/// @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 SafeAddressMetadata { +library SafeCurrencyMetadata { uint8 constant MAX_SYMBOL_LENGTH = 12; - /// @notice attempts to extract the token symbol. if it does not implement symbol, returns a symbol derived from the address - /// @param addr The address + /// @notice attempts to extract the token symbol. if it does not implement symbol, returns a symbol derived from the currency address + /// @param currency The currency address /// @param nativeLabel The native label - /// @return the token symbol - function addressSymbol(address addr, string memory nativeLabel) internal view returns (string memory) { - if (addr == address(0)) { + /// @return the currency symbol + function currencySymbol(address currency, string memory nativeLabel) internal view returns (string memory) { + if (currency == address(0)) { return nativeLabel; } - string memory symbol = callAndParseStringReturn(addr, IERC20Metadata.symbol.selector); + string memory symbol = callAndParseStringReturn(currency, IERC20Metadata.symbol.selector); if (bytes(symbol).length == 0) { // fallback to 6 uppercase hex of address - return addressToSymbol(addr); + return currencyToSymbol(currency); } if (bytes(symbol).length > MAX_SYMBOL_LENGTH) { return truncateSymbol(symbol); @@ -30,13 +30,13 @@ library SafeAddressMetadata { } /// @notice attempts to extract the token decimals, returns 0 if not implemented or not a uint8 - /// @param addr The address - /// @return the token decimals - function addressDecimals(address addr) internal view returns (uint8) { - if (addr == address(0)) { + /// @param currency The currency address + /// @return the currency decimals + function currencyDecimals(address currency) internal view returns (uint8) { + if (currency == address(0)) { return 18; } - (bool success, bytes memory data) = addr.staticcall(abi.encodeCall(IERC20Metadata.decimals, ())); + (bool success, bytes memory data) = currency.staticcall(abi.encodeCall(IERC20Metadata.decimals, ())); if (!success) { return 0; } @@ -67,18 +67,18 @@ library SafeAddressMetadata { } /// @notice produces a symbol from the address - the first 6 hex of the address string in upper case - /// @param addr The address + /// @param currency The currency address /// @return the symbol - function addressToSymbol(address addr) private pure returns (string memory) { - return AddressStringUtil.toAsciiString(addr, 6); + function currencyToSymbol(address currency) private pure returns (string memory) { + return AddressStringUtil.toAsciiString(currency, 6); } /// @notice calls an external view contract method that returns a symbol, and parses the output into a string - /// @param addr The address + /// @param currency The currency address /// @param selector the selector of the symbol method /// @return the symbol - function callAndParseStringReturn(address addr, bytes4 selector) private view returns (string memory) { - (bool success, bytes memory data) = addr.staticcall(abi.encodeWithSelector(selector)); + function callAndParseStringReturn(address currency, bytes4 selector) private view returns (string memory) { + (bool success, bytes memory data) = currency.staticcall(abi.encodeWithSelector(selector)); // if not implemented, return empty string if (!success) { return ""; diff --git a/test/PositionDescriptor.t.sol b/test/PositionDescriptor.t.sol index 6c24a4af..be3c909c 100644 --- a/test/PositionDescriptor.t.sol +++ b/test/PositionDescriptor.t.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.24; import "forge-std/Test.sol"; import {PositionDescriptor} from "../src/PositionDescriptor.sol"; -import {AddressRatioSortOrder} from "../src/libraries/AddressRatioSortOrder.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"; @@ -13,7 +13,7 @@ import {ActionConstants} from "../src/libraries/ActionConstants.sol"; import {Base64} from "./base64.sol"; import {GasSnapshot} from "forge-gas-snapshot/GasSnapshot.sol"; import {Currency, CurrencyLibrary} from "@uniswap/v4-core/src/types/Currency.sol"; -import {SafeAddressMetadata} from "../src/libraries/SafeAddressMetadata.sol"; +import {SafeCurrencyMetadata} from "../src/libraries/SafeCurrencyMetadata.sol"; import {AddressStringUtil} from "../src/libraries/AddressStringUtil.sol"; import {Descriptor} from "../src/libraries/Descriptor.sol"; @@ -49,30 +49,30 @@ contract PositionDescriptorTest is Test, PosmTestSetup, GasSnapshot { function test_setup_succeeds() public view { assertEq(address(positionDescriptor.poolManager()), address(manager)); assertEq(positionDescriptor.wrappedNative(), WETH9); - assertEq(positionDescriptor.nativeAddressLabel(), nativeCurrencyLabel); + assertEq(positionDescriptor.nativeCurrencyLabel(), nativeCurrencyLabel); } - function test_addressRatioPriority_mainnet_succeeds() public { + function test_currencyRatioPriority_mainnet_succeeds() public { vm.chainId(1); - assertEq(positionDescriptor.addressRatioPriority(WETH9), AddressRatioSortOrder.DENOMINATOR); - assertEq(positionDescriptor.addressRatioPriority(address(0)), AddressRatioSortOrder.DENOMINATOR); - assertEq(positionDescriptor.addressRatioPriority(USDC), AddressRatioSortOrder.NUMERATOR_MOST); - assertEq(positionDescriptor.addressRatioPriority(USDT), AddressRatioSortOrder.NUMERATOR_MORE); - assertEq(positionDescriptor.addressRatioPriority(DAI), AddressRatioSortOrder.NUMERATOR); - assertEq(positionDescriptor.addressRatioPriority(TBTC), AddressRatioSortOrder.DENOMINATOR_MORE); - assertEq(positionDescriptor.addressRatioPriority(WBTC), AddressRatioSortOrder.DENOMINATOR_MOST); - assertEq(positionDescriptor.addressRatioPriority(makeAddr("ALICE")), 0); + 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); + assertEq(positionDescriptor.currencyRatioPriority(TBTC), CurrencyRatioSortOrder.DENOMINATOR_MORE); + assertEq(positionDescriptor.currencyRatioPriority(WBTC), CurrencyRatioSortOrder.DENOMINATOR_MOST); + assertEq(positionDescriptor.currencyRatioPriority(makeAddr("ALICE")), 0); } - function test_addressRatioPriority_notMainnet_succeeds() public { - assertEq(positionDescriptor.addressRatioPriority(WETH9), AddressRatioSortOrder.DENOMINATOR); - assertEq(positionDescriptor.addressRatioPriority(address(0)), AddressRatioSortOrder.DENOMINATOR); - assertEq(positionDescriptor.addressRatioPriority(USDC), 0); - assertEq(positionDescriptor.addressRatioPriority(USDT), 0); - assertEq(positionDescriptor.addressRatioPriority(DAI), 0); - assertEq(positionDescriptor.addressRatioPriority(TBTC), 0); - assertEq(positionDescriptor.addressRatioPriority(WBTC), 0); - assertEq(positionDescriptor.addressRatioPriority(makeAddr("ALICE")), 0); + function test_currencyRatioPriority_notMainnet_succeeds() public { + 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); + assertEq(positionDescriptor.currencyRatioPriority(TBTC), 0); + assertEq(positionDescriptor.currencyRatioPriority(WBTC), 0); + assertEq(positionDescriptor.currencyRatioPriority(makeAddr("ALICE")), 0); } function test_flipRatio_succeeds() public { @@ -127,8 +127,8 @@ contract PositionDescriptorTest is Test, PosmTestSetup, GasSnapshot { // quote is currency1, base is currency0 assertFalse(positionDescriptor.flipRatio(Currency.unwrap(key.currency0), Currency.unwrap(key.currency1))); - string memory symbol0 = SafeAddressMetadata.addressSymbol(Currency.unwrap(currency0), nativeCurrencyLabel); - string memory symbol1 = SafeAddressMetadata.addressSymbol(Currency.unwrap(currency1), nativeCurrencyLabel); + string memory symbol0 = SafeCurrencyMetadata.currencySymbol(Currency.unwrap(currency0), nativeCurrencyLabel); + string memory symbol1 = SafeCurrencyMetadata.currencySymbol(Currency.unwrap(currency1), nativeCurrencyLabel); string memory managerAddress = toHexString(address(manager)); string memory currency0Address = toHexString(Currency.unwrap(currency0)); string memory currency1Address = toHexString(Currency.unwrap(currency1)); @@ -140,15 +140,15 @@ contract PositionDescriptorTest is Test, PosmTestSetup, GasSnapshot { string memory tickToDecimal0 = Descriptor.tickToDecimalString( tickLower, key.tickSpacing, - SafeAddressMetadata.addressDecimals(Currency.unwrap(currency0)), - SafeAddressMetadata.addressDecimals(Currency.unwrap(currency1)), + SafeCurrencyMetadata.currencyDecimals(Currency.unwrap(currency0)), + SafeCurrencyMetadata.currencyDecimals(Currency.unwrap(currency1)), false ); string memory tickToDecimal1 = Descriptor.tickToDecimalString( tickUpper, key.tickSpacing, - SafeAddressMetadata.addressDecimals(Currency.unwrap(currency0)), - SafeAddressMetadata.addressDecimals(Currency.unwrap(currency1)), + SafeCurrencyMetadata.currencyDecimals(Currency.unwrap(currency0)), + SafeCurrencyMetadata.currencyDecimals(Currency.unwrap(currency1)), false ); @@ -185,7 +185,7 @@ contract PositionDescriptorTest is Test, PosmTestSetup, GasSnapshot { "\nToken ID: ", id, "\n\n", - unicode"⚠️ DISCLAIMER: Due diligence is imperative when assessing this NFT. Make sure addresses match the expected addresses, as symbols may be imitated." + unicode"⚠️ DISCLAIMER: Due diligence is imperative when assessing this NFT. Make sure currencies match the expected currencies, as currency symbols may be imitated." ) ) ); @@ -237,9 +237,9 @@ contract PositionDescriptorTest is Test, PosmTestSetup, GasSnapshot { ); string memory symbol0 = - SafeAddressMetadata.addressSymbol(Currency.unwrap(nativeKey.currency0), nativeCurrencyLabel); + SafeCurrencyMetadata.currencySymbol(Currency.unwrap(nativeKey.currency0), nativeCurrencyLabel); string memory symbol1 = - SafeAddressMetadata.addressSymbol(Currency.unwrap(nativeKey.currency1), nativeCurrencyLabel); + SafeCurrencyMetadata.currencySymbol(Currency.unwrap(nativeKey.currency1), nativeCurrencyLabel); string memory managerAddress = toHexString(address(manager)); string memory currency0Address = Currency.unwrap(nativeKey.currency0) == address(0) ? "Native" @@ -255,15 +255,15 @@ contract PositionDescriptorTest is Test, PosmTestSetup, GasSnapshot { string memory tickToDecimal0 = Descriptor.tickToDecimalString( tickLower, nativeKey.tickSpacing, - SafeAddressMetadata.addressDecimals(Currency.unwrap(currency0)), - SafeAddressMetadata.addressDecimals(Currency.unwrap(currency1)), + SafeCurrencyMetadata.currencyDecimals(Currency.unwrap(currency0)), + SafeCurrencyMetadata.currencyDecimals(Currency.unwrap(currency1)), false ); string memory tickToDecimal1 = Descriptor.tickToDecimalString( tickUpper, nativeKey.tickSpacing, - SafeAddressMetadata.addressDecimals(Currency.unwrap(currency0)), - SafeAddressMetadata.addressDecimals(Currency.unwrap(currency1)), + SafeCurrencyMetadata.currencyDecimals(Currency.unwrap(currency0)), + SafeCurrencyMetadata.currencyDecimals(Currency.unwrap(currency1)), false ); @@ -300,7 +300,7 @@ contract PositionDescriptorTest is Test, PosmTestSetup, GasSnapshot { "\nToken ID: ", id, "\n\n", - unicode"⚠️ DISCLAIMER: Due diligence is imperative when assessing this NFT. Make sure addresses match the expected addresses, as symbols may be imitated." + unicode"⚠️ DISCLAIMER: Due diligence is imperative when assessing this NFT. Make sure currencies match the expected currencies, as currency symbols may be imitated." ) ) ); diff --git a/test/libraries/SafeAddressMetadata.t.sol b/test/libraries/SafeAddressMetadata.t.sol deleted file mode 100644 index 316a5978..00000000 --- a/test/libraries/SafeAddressMetadata.t.sol +++ /dev/null @@ -1,16 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.24; - -import "forge-std/Test.sol"; -import {SafeAddressMetadata} from "../../src/libraries/SafeAddressMetadata.sol"; - -contract SafeAddressMetadataTest is Test { - function test_truncateSymbol_succeeds() public pure { - // 12 characters - assertEq(SafeAddressMetadata.truncateSymbol("123456789012"), "123456789012"); - // 13 characters - assertEq(SafeAddressMetadata.truncateSymbol("1234567890123"), "123456789012"); - // 14 characters - assertEq(SafeAddressMetadata.truncateSymbol("12345678901234"), "123456789012"); - } -} diff --git a/test/libraries/SafeCurrencyMetadata.t.sol b/test/libraries/SafeCurrencyMetadata.t.sol new file mode 100644 index 00000000..15f6e0f5 --- /dev/null +++ b/test/libraries/SafeCurrencyMetadata.t.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.24; + +import "forge-std/Test.sol"; +import {SafeCurrencyMetadata} from "../../src/libraries/SafeCurrencyMetadata.sol"; + +contract SafeCurrencyMetadataTest is Test { + function test_truncateSymbol_succeeds() public pure { + // 12 characters + assertEq(SafeCurrencyMetadata.truncateSymbol("123456789012"), "123456789012"); + // 13 characters + assertEq(SafeCurrencyMetadata.truncateSymbol("1234567890123"), "123456789012"); + // 14 characters + assertEq(SafeCurrencyMetadata.truncateSymbol("12345678901234"), "123456789012"); + } +} From 81ab148c1a69ed978cb1fe3dfd12ff0d91963ce2 Mon Sep 17 00:00:00 2001 From: dianakocsis Date: Thu, 31 Oct 2024 12:29:40 -0400 Subject: [PATCH 7/8] more reversions --- .../positionDescriptor bytecode size.snap | 2 +- src/PositionDescriptor.sol | 38 +++++++++---------- src/libraries/Descriptor.sol | 20 +++++----- src/libraries/SVG.sol | 2 +- src/libraries/SafeCurrencyMetadata.sol | 16 ++++---- test/PositionDescriptor.t.sol | 4 +- 6 files changed, 41 insertions(+), 41 deletions(-) diff --git a/.forge-snapshots/positionDescriptor bytecode size.snap b/.forge-snapshots/positionDescriptor bytecode size.snap index 361cdd47..4babbfb1 100644 --- a/.forge-snapshots/positionDescriptor bytecode size.snap +++ b/.forge-snapshots/positionDescriptor bytecode size.snap @@ -1 +1 @@ -31690 \ No newline at end of file +31728 \ No newline at end of file diff --git a/src/PositionDescriptor.sol b/src/PositionDescriptor.sol index 40a0313f..06e164e6 100644 --- a/src/PositionDescriptor.sol +++ b/src/PositionDescriptor.sol @@ -57,7 +57,7 @@ contract PositionDescriptor is IPositionDescriptor { address currency0 = Currency.unwrap(poolKey.currency0); address currency1 = Currency.unwrap(poolKey.currency1); - // If possible, flip currencies to get the larger one as the base, so that the price (quote/base) is more readable + // If possible, flip currencies to get the larger currency as the base, so that the price (quote/base) is more readable // flip if currency0 priority is greater than currency1 priority bool _flipRatio = flipRatio(currency0, currency1); @@ -87,36 +87,36 @@ contract PositionDescriptor is IPositionDescriptor { ); } - /// @notice Returns true if address0 has higher priority than address1 - /// @param address0 The first address - /// @param address1 The second address - /// @return flipRatio True if address0 has higher priority than address1 - function flipRatio(address address0, address address1) public view returns (bool) { - return currencyRatioPriority(address0) > currencyRatioPriority(address1); + /// @notice Returns true if currency0 has higher priority than currency1 + /// @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); } - /// @notice Returns the priority of an address. - /// For certain addresses on mainnet, the smaller the address, the higher the priority - /// @param addr The address - /// @return priority The priority of the address - function currencyRatioPriority(address addr) public view returns (int256) { - // Addresses in order of priority on mainnet: USDC, USDT, DAI, (ETH, WETH), TBTC, WBTC + /// @notice Returns the priority of a currency. + /// For certain currencies on mainnet, the smaller the currency, the higher the priority + /// @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 // wrapped native is different address on different chains. passed in constructor // native address - if (addr == address(0) || addr == wrappedNative) { + if (currency == address(0) || currency == wrappedNative) { return CurrencyRatioSortOrder.DENOMINATOR; } if (block.chainid == 1) { - if (addr == USDC) { + if (currency == USDC) { return CurrencyRatioSortOrder.NUMERATOR_MOST; - } else if (addr == USDT) { + } else if (currency == USDT) { return CurrencyRatioSortOrder.NUMERATOR_MORE; - } else if (addr == DAI) { + } else if (currency == DAI) { return CurrencyRatioSortOrder.NUMERATOR; - } else if (addr == TBTC) { + } else if (currency == TBTC) { return CurrencyRatioSortOrder.DENOMINATOR_MORE; - } else if (addr == WBTC) { + } else if (currency == WBTC) { return CurrencyRatioSortOrder.DENOMINATOR_MOST; } else { return 0; diff --git a/src/libraries/Descriptor.sol b/src/libraries/Descriptor.sol index 76176a4c..ba261f85 100644 --- a/src/libraries/Descriptor.sol +++ b/src/libraries/Descriptor.sol @@ -136,7 +136,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 address + /// @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 @@ -165,7 +165,7 @@ library Descriptor { "\\nToken ID: ", tokenId, "\\n\\n", - unicode"⚠️ DISCLAIMER: Due diligence is imperative when assessing this NFT. Make sure currencies match the expected currencies, as currency symbols may be imitated." + unicode"⚠️ DISCLAIMER: Due diligence is imperative when assessing this NFT. Make sure currency addresses match the expected currencies, as currency symbols may be imitated." ) ); } @@ -261,8 +261,8 @@ library Descriptor { /// MIN or MAX are returned if tick is at the bottom or top of the price curve /// @param tick The tick (either tickLower or tickUpper) /// @param tickSpacing The tick spacing of the pool - /// @param baseCurrencyDecimals The decimals of the base address - /// @param quoteCurrencyDecimals The decimals of the quote address + /// @param baseCurrencyDecimals The decimals of the base currency + /// @param quoteCurrencyDecimals The decimals of the quote currency /// @param flipRatio True if the ratio was flipped /// @return The ratio value as a string function tickToDecimalString( @@ -305,8 +305,8 @@ library Descriptor { /// @notice Adjusts the sqrt price for different currencies with different decimals /// @param sqrtRatioX96 The sqrt price at a specific tick - /// @param baseCurrencyDecimals The decimals of the base address - /// @param quoteCurrencyDecimals The decimals of the quote address + /// @param baseCurrencyDecimals The decimals of the base currency + /// @param quoteCurrencyDecimals The decimals of the quote currency /// @return adjustedSqrtRatioX96 The adjusted sqrt price function adjustForDecimalPrecision(uint160 sqrtRatioX96, uint8 baseCurrencyDecimals, uint8 quoteCurrencyDecimals) private @@ -518,11 +518,11 @@ library Descriptor { return string((currency >> offset).toHexStringNoPrefix(3)); } - function getCircleCoord(uint256 addr, uint256 offset, uint256 tokenId) internal pure returns (uint256) { - return (sliceAddressHex(addr, offset) * tokenId) % 255; + function getCircleCoord(uint256 currency, uint256 offset, uint256 tokenId) internal pure returns (uint256) { + return (sliceCurrencyHex(currency, offset) * tokenId) % 255; } - function sliceAddressHex(uint256 addr, uint256 offset) internal pure returns (uint256) { - return uint256(uint8(addr >> offset)); + function sliceCurrencyHex(uint256 currency, uint256 offset) internal pure returns (uint256) { + return uint256(uint8(currency >> offset)); } } diff --git a/src/libraries/SVG.sol b/src/libraries/SVG.sol index 89360e07..2731a6ca 100644 --- a/src/libraries/SVG.sol +++ b/src/libraries/SVG.sol @@ -157,7 +157,7 @@ library SVG { ); } - /// @notice Generate the SVG for the moving border text displaying the quote and base currencies with their symbols + /// @notice Generate the SVG for the moving border text displaying the quote and base currency addresses with their symbols /// @param quoteCurrency The quote currency /// @param baseCurrency The base currency /// @param quoteCurrencySymbol The quote currency symbol diff --git a/src/libraries/SafeCurrencyMetadata.sol b/src/libraries/SafeCurrencyMetadata.sol index b9115d55..cc88ab84 100644 --- a/src/libraries/SafeCurrencyMetadata.sol +++ b/src/libraries/SafeCurrencyMetadata.sol @@ -10,7 +10,7 @@ import {AddressStringUtil} from "./AddressStringUtil.sol"; library SafeCurrencyMetadata { uint8 constant MAX_SYMBOL_LENGTH = 12; - /// @notice attempts to extract the token symbol. if it does not implement symbol, returns a symbol derived from the currency address + /// @notice attempts to extract the currency symbol. if it does not implement symbol, returns a symbol derived from the address /// @param currency The currency address /// @param nativeLabel The native label /// @return the currency symbol @@ -21,7 +21,7 @@ library SafeCurrencyMetadata { string memory symbol = callAndParseStringReturn(currency, IERC20Metadata.symbol.selector); if (bytes(symbol).length == 0) { // fallback to 6 uppercase hex of address - return currencyToSymbol(currency); + return addressToSymbol(currency); } if (bytes(symbol).length > MAX_SYMBOL_LENGTH) { return truncateSymbol(symbol); @@ -67,18 +67,18 @@ library SafeCurrencyMetadata { } /// @notice produces a symbol from the address - the first 6 hex of the address string in upper case - /// @param currency The currency address + /// @param currencyAddress the address of the currency /// @return the symbol - function currencyToSymbol(address currency) private pure returns (string memory) { - return AddressStringUtil.toAsciiString(currency, 6); + 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 currency The currency address + /// @param currencyAddress the address of the currency /// @param selector the selector of the symbol method /// @return the symbol - function callAndParseStringReturn(address currency, bytes4 selector) private view returns (string memory) { - (bool success, bytes memory data) = currency.staticcall(abi.encodeWithSelector(selector)); + 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 ""; diff --git a/test/PositionDescriptor.t.sol b/test/PositionDescriptor.t.sol index be3c909c..4b326814 100644 --- a/test/PositionDescriptor.t.sol +++ b/test/PositionDescriptor.t.sol @@ -185,7 +185,7 @@ contract PositionDescriptorTest is Test, PosmTestSetup, GasSnapshot { "\nToken ID: ", id, "\n\n", - unicode"⚠️ DISCLAIMER: Due diligence is imperative when assessing this NFT. Make sure currencies match the expected currencies, as currency symbols may be imitated." + unicode"⚠️ DISCLAIMER: Due diligence is imperative when assessing this NFT. Make sure currency addresses match the expected currencies, as currency symbols may be imitated." ) ) ); @@ -300,7 +300,7 @@ contract PositionDescriptorTest is Test, PosmTestSetup, GasSnapshot { "\nToken ID: ", id, "\n\n", - unicode"⚠️ DISCLAIMER: Due diligence is imperative when assessing this NFT. Make sure currencies match the expected currencies, as currency symbols may be imitated." + unicode"⚠️ DISCLAIMER: Due diligence is imperative when assessing this NFT. Make sure currency addresses match the expected currencies, as currency symbols may be imitated." ) ) ); From 1d1156dec4ff44996a82c431e5b2c25c25dd2555 Mon Sep 17 00:00:00 2001 From: dianakocsis Date: Thu, 31 Oct 2024 12:35:26 -0400 Subject: [PATCH 8/8] last one --- src/PositionDescriptor.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/PositionDescriptor.sol b/src/PositionDescriptor.sol index 06e164e6..7f1ac799 100644 --- a/src/PositionDescriptor.sol +++ b/src/PositionDescriptor.sol @@ -57,7 +57,7 @@ contract PositionDescriptor is IPositionDescriptor { address currency0 = Currency.unwrap(poolKey.currency0); address currency1 = Currency.unwrap(poolKey.currency1); - // If possible, flip currencies to get the larger currency as the base, so that the price (quote/base) is more readable + // 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(currency0, currency1); @@ -103,7 +103,7 @@ contract PositionDescriptor is IPositionDescriptor { // 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 address + // native currency if (currency == address(0) || currency == wrappedNative) { return CurrencyRatioSortOrder.DENOMINATOR; }