diff --git a/.forge-snapshots/positionDescriptor bytecode size.snap b/.forge-snapshots/positionDescriptor bytecode size.snap index d2ccf486..a23dd234 100644 --- a/.forge-snapshots/positionDescriptor bytecode size.snap +++ b/.forge-snapshots/positionDescriptor bytecode size.snap @@ -1 +1 @@ -31236 \ No newline at end of file +31443 \ No newline at end of file diff --git a/src/libraries/Descriptor.sol b/src/libraries/Descriptor.sol index 918ca5a8..b3d5ef29 100644 --- a/src/libraries/Descriptor.sol +++ b/src/libraries/Descriptor.sol @@ -45,13 +45,13 @@ 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), + escapeSpecialCharacters(params.quoteCurrencySymbol), + escapeSpecialCharacters(params.baseCurrencySymbol), addressToString(params.poolManager) ); string memory descriptionPartTwo = generateDescriptionPartTwo( params.tokenId.toString(), - escapeQuotes(params.baseCurrencySymbol), + escapeSpecialCharacters(params.baseCurrencySymbol), addressToString(Currency.unwrap(params.quoteCurrency)), addressToString(Currency.unwrap(params.baseCurrency)), params.hooks == address(0) ? "No Hook" : addressToString(params.hooks), @@ -81,23 +81,23 @@ library Descriptor { ); } - /// @notice Escapes double quotes in a string if they are present - function escapeQuotes(string memory symbol) internal pure returns (string memory) { + /// @notice Escapes special characters in a string if they are present + function escapeSpecialCharacters(string memory symbol) internal pure returns (string memory) { bytes memory symbolBytes = bytes(symbol); - uint8 quotesCount = 0; - // count the amount of double quotes (") in the symbol + uint8 specialCharCount = 0; + // count the amount of double quotes, form feeds, new lines, carriage returns, or tabs in the symbol for (uint8 i = 0; i < symbolBytes.length; i++) { - if (symbolBytes[i] == '"') { - quotesCount++; + if (isSpecialCharacter(symbolBytes[i])) { + specialCharCount++; } } - if (quotesCount > 0) { - // create a new bytes array with enough space to hold the original bytes plus space for the backslashes to escape the quotes - bytes memory escapedBytes = new bytes(symbolBytes.length + quotesCount); + if (specialCharCount > 0) { + // create a new bytes array with enough space to hold the original bytes plus space for the backslashes to escape the special characters + bytes memory escapedBytes = new bytes(symbolBytes.length + specialCharCount); uint256 index; for (uint8 i = 0; i < symbolBytes.length; i++) { - // add a '\' before any double quotes - if (symbolBytes[i] == '"') { + // add a '\' before any double quotes, form feeds, new lines, carriage returns, or tabs + if (isSpecialCharacter(symbolBytes[i])) { escapedBytes[index++] = "\\"; } // copy each byte from original string to the new array @@ -186,9 +186,9 @@ library Descriptor { "Uniswap - ", feeTier, " - ", - escapeQuotes(params.quoteCurrencySymbol), + escapeSpecialCharacters(params.quoteCurrencySymbol), "/", - escapeQuotes(params.baseCurrencySymbol), + escapeSpecialCharacters(params.baseCurrencySymbol), " - ", tickToDecimalString( !params.flipRatio ? params.tickLower : params.tickUpper, @@ -503,6 +503,10 @@ library Descriptor { } } + function isSpecialCharacter(bytes1 b) private pure returns (bool) { + return b == '"' || b == "\u000c" || b == "\n" || b == "\r" || b == "\t"; + } + function scale(uint256 n, uint256 inMn, uint256 inMx, uint256 outMn, uint256 outMx) private pure diff --git a/test/libraries/Descriptor.t.sol b/test/libraries/Descriptor.t.sol index e191c5a5..2f3d5fd8 100644 --- a/test/libraries/Descriptor.t.sol +++ b/test/libraries/Descriptor.t.sol @@ -39,15 +39,20 @@ contract DescriptorTest is Test { ); } - function test_escapeQuotes_succeeds() public pure { - assertEq(Descriptor.escapeQuotes(""), ""); - assertEq(Descriptor.escapeQuotes("a"), "a"); - assertEq(Descriptor.escapeQuotes("abc"), "abc"); - assertEq(Descriptor.escapeQuotes("a\"bc"), "a\\\"bc"); - assertEq(Descriptor.escapeQuotes("a\"b\"c"), "a\\\"b\\\"c"); - assertEq(Descriptor.escapeQuotes("a\"b\"c\""), "a\\\"b\\\"c\\\""); - assertEq(Descriptor.escapeQuotes("\"a\"b\"c\""), "\\\"a\\\"b\\\"c\\\""); - assertEq(Descriptor.escapeQuotes("\"a\"b\"c\"\""), "\\\"a\\\"b\\\"c\\\"\\\""); + function test_escapeSpecialCharacters_succeeds() public pure { + assertEq(Descriptor.escapeSpecialCharacters(""), ""); + assertEq(Descriptor.escapeSpecialCharacters("a"), "a"); + assertEq(Descriptor.escapeSpecialCharacters("abc"), "abc"); + assertEq(Descriptor.escapeSpecialCharacters("a\"bc"), "a\\\"bc"); + assertEq(Descriptor.escapeSpecialCharacters("a\"b\"c"), "a\\\"b\\\"c"); + assertEq(Descriptor.escapeSpecialCharacters("a\"b\"c\""), "a\\\"b\\\"c\\\""); + assertEq(Descriptor.escapeSpecialCharacters("\"a\"b\"c\""), "\\\"a\\\"b\\\"c\\\""); + assertEq(Descriptor.escapeSpecialCharacters("\"a\"b\"c\"\""), "\\\"a\\\"b\\\"c\\\"\\\""); + + assertEq(Descriptor.escapeSpecialCharacters("a\rbc"), "a\\\rbc"); + assertEq(Descriptor.escapeSpecialCharacters("a\nbc"), "a\\\nbc"); + assertEq(Descriptor.escapeSpecialCharacters("a\tbc"), "a\\\tbc"); + assertEq(Descriptor.escapeSpecialCharacters("a\u000cbc"), "a\\\u000cbc"); } function test_tickToDecimalString_withTickSpacing10() public pure {