diff --git a/src/PositionDescriptor.sol b/src/PositionDescriptor.sol index c3dc3a6c..4e26346c 100644 --- a/src/PositionDescriptor.sol +++ b/src/PositionDescriptor.sol @@ -3,11 +3,9 @@ pragma solidity 0.8.26; import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol"; import {Currency, CurrencyLibrary} from "@uniswap/v4-core/src/types/Currency.sol"; -import {IHooks} from "@uniswap/v4-core/src/interfaces/IHooks.sol"; import {StateLibrary} from "@uniswap/v4-core/src/libraries/StateLibrary.sol"; import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol"; import {PoolId, PoolIdLibrary} from "@uniswap/v4-core/src/types/PoolId.sol"; -import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import {IPositionManager} from "./interfaces/IPositionManager.sol"; import {IPositionDescriptor} from "./interfaces/IPositionDescriptor.sol"; import {PositionInfo, PositionInfoLibrary} from "./libraries/PositionInfoLibrary.sol"; @@ -30,14 +28,14 @@ contract PositionDescriptor is IPositionDescriptor { address private constant TBTC = 0x8dAEBADE922dF735c38C80C7eBD708Af50815fAa; address private constant WBTC = 0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599; - address public immutable WETH9; + address public immutable wrappedNative; string public nativeCurrencyLabel; IPoolManager public immutable poolManager; - constructor(IPoolManager _poolManager, address _WETH9, string memory _nativeCurrencyLabel) { + constructor(IPoolManager _poolManager, address _wrappedNative, string memory _nativeCurrencyLabel) { poolManager = _poolManager; - WETH9 = _WETH9; + wrappedNative = _wrappedNative; nativeCurrencyLabel = _nativeCurrencyLabel; } @@ -65,18 +63,10 @@ contract PositionDescriptor is IPositionDescriptor { tokenId: tokenId, quoteCurrency: quoteCurrency, baseCurrency: baseCurrency, - quoteCurrencySymbol: quoteCurrency.isAddressZero() - ? nativeCurrencyLabel - : SafeERC20Metadata.tokenSymbol(Currency.unwrap(quoteCurrency)), - baseCurrencySymbol: baseCurrency.isAddressZero() - ? nativeCurrencyLabel - : SafeERC20Metadata.tokenSymbol(Currency.unwrap(baseCurrency)), - quoteCurrencyDecimals: quoteCurrency.isAddressZero() - ? 18 - : SafeERC20Metadata.tokenDecimals(Currency.unwrap(quoteCurrency)), - baseCurrencyDecimals: baseCurrency.isAddressZero() - ? 18 - : SafeERC20Metadata.tokenDecimals(Currency.unwrap(baseCurrency)), + quoteCurrencySymbol: SafeERC20Metadata.currencySymbol(quoteCurrency, nativeCurrencyLabel), + baseCurrencySymbol: SafeERC20Metadata.currencySymbol(baseCurrency, nativeCurrencyLabel), + quoteCurrencyDecimals: SafeERC20Metadata.currencyDecimals(quoteCurrency), + baseCurrencyDecimals: SafeERC20Metadata.currencyDecimals(baseCurrency), flipRatio: _flipRatio, tickLower: positionInfo.tickLower(), tickUpper: positionInfo.tickUpper(), @@ -90,8 +80,8 @@ contract PositionDescriptor is IPositionDescriptor { } /// @notice Returns true if currency0 has higher priority than currency1 - /// @param currency0 The first currency - /// @param currency1 The second currency + /// @param currency0 The first currency address + /// @param currency1 The second currency address /// @return flipRatio True if currency0 has higher priority than currency1 function flipRatio(address currency0, address currency1) public view returns (bool) { return currencyRatioPriority(currency0) > currencyRatioPriority(currency1); @@ -99,19 +89,16 @@ contract PositionDescriptor is IPositionDescriptor { /// @notice Returns the priority of a currency. /// For certain currencies on mainnet, the smaller the currency, the higher the priority - /// @param currency The currency + /// @param currency The currency address /// @return priority The priority of the currency function currencyRatioPriority(address currency) public view returns (int256) { - // Currencies in order of priority on mainnet: USDC, USDT, DAI, ETH, WETH, TBTC, WBTC - // weth is different address on different chains. passed in constructor + // Currencies in order of priority on mainnet: USDC, USDT, DAI, (ETH, WETH), TBTC, WBTC + // wrapped native is different address on different chains. passed in constructor // native currency - if (currency == address(0)) { + if (currency == address(0) || currency == wrappedNative) { return CurrencyRatioSortOrder.DENOMINATOR; } - if (currency == WETH9) { - return CurrencyRatioSortOrder.DENOMINATOR_2; - } if (block.chainid == 1) { if (currency == USDC) { return CurrencyRatioSortOrder.NUMERATOR_MOST; diff --git a/src/libraries/CurrencyRatioSortOrder.sol b/src/libraries/CurrencyRatioSortOrder.sol index d9d9d442..1f3a719a 100644 --- a/src/libraries/CurrencyRatioSortOrder.sol +++ b/src/libraries/CurrencyRatioSortOrder.sol @@ -12,6 +12,5 @@ library CurrencyRatioSortOrder { int256 constant DENOMINATOR_MOST = -300; int256 constant DENOMINATOR_MORE = -200; - int256 constant DENOMINATOR_2 = -150; int256 constant DENOMINATOR = -100; } diff --git a/src/libraries/Descriptor.sol b/src/libraries/Descriptor.sol index b5da50ee..8e32a35d 100644 --- a/src/libraries/Descriptor.sol +++ b/src/libraries/Descriptor.sol @@ -2,7 +2,6 @@ pragma solidity ^0.8.0; import {Currency, CurrencyLibrary} from "@uniswap/v4-core/src/types/Currency.sol"; -import {IHooks} from "@uniswap/v4-core/src/interfaces/IHooks.sol"; import {TickMath} from "@uniswap/v4-core/src/libraries/TickMath.sol"; import {FullMath} from "@uniswap/v4-core/src/libraries/FullMath.sol"; import {LPFeeLibrary} from "@uniswap/v4-core/src/libraries/LPFeeLibrary.sol"; diff --git a/src/libraries/SafeERC20Metadata.sol b/src/libraries/SafeERC20Metadata.sol index 5dc976f6..3d1e1cb9 100644 --- a/src/libraries/SafeERC20Metadata.sol +++ b/src/libraries/SafeERC20Metadata.sol @@ -1,13 +1,16 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.0; -import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; -import "./AddressStringUtil.sol"; +import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; +import {Currency, CurrencyLibrary} from "@uniswap/v4-core/src/types/Currency.sol"; +import {AddressStringUtil} from "./AddressStringUtil.sol"; /// @title SafeERC20Metadata /// @notice can produce symbols and decimals from inconsistent or absent ERC20 implementations /// @dev Reference: https://github.com/Uniswap/solidity-lib/blob/master/contracts/libraries/SafeERC20Namer.sol library SafeERC20Metadata { + using CurrencyLibrary for Currency; + function bytes32ToString(bytes32 x) private pure returns (string memory) { bytes memory bytesString = new bytes(32); uint256 charCount = 0; @@ -53,22 +56,30 @@ library SafeERC20Metadata { } /// @notice attempts to extract the token symbol. if it does not implement symbol, returns a symbol derived from the address - /// @param token the token address + /// @param currency The currency + /// @param nativeLabel The native label /// @return the token symbol - function tokenSymbol(address token) internal view returns (string memory) { - string memory symbol = callAndParseStringReturn(token, IERC20Metadata.symbol.selector); + function currencySymbol(Currency currency, string memory nativeLabel) internal view returns (string memory) { + if (currency.isAddressZero()) { + return nativeLabel; + } + address currencyAddress = Currency.unwrap(currency); + string memory symbol = callAndParseStringReturn(currencyAddress, IERC20Metadata.symbol.selector); if (bytes(symbol).length == 0) { // fallback to 6 uppercase hex of address - return addressToSymbol(token); + return addressToSymbol(currencyAddress); } return symbol; } /// @notice attempts to extract the token decimals, returns 0 if not implemented or not a uint8 - /// @param token the token address + /// @param currency The currency /// @return the token decimals - function tokenDecimals(address token) internal view returns (uint8) { - (bool success, bytes memory data) = token.staticcall(abi.encodeCall(IERC20Metadata.decimals, ())); + function currencyDecimals(Currency currency) internal view returns (uint8) { + if (currency.isAddressZero()) { + return 18; + } + (bool success, bytes memory data) = Currency.unwrap(currency).staticcall(abi.encodeCall(IERC20Metadata.decimals, ())); if (!success) { return 0; } diff --git a/test/PositionDescriptor.t.sol b/test/PositionDescriptor.t.sol index 9ba6f54a..782b798d 100644 --- a/test/PositionDescriptor.t.sol +++ b/test/PositionDescriptor.t.sol @@ -38,13 +38,14 @@ contract PositionDescriptorTest is Test, PosmTestSetup { function test_setup_succeeds() public view { assertEq(address(positionDescriptor.poolManager()), address(manager)); - assertEq(positionDescriptor.WETH9(), WETH9); + assertEq(positionDescriptor.wrappedNative(), WETH9); assertEq(positionDescriptor.nativeCurrencyLabel(), nativeCurrencyLabel); } function test_currencyRatioPriority_mainnet_succeeds() public { vm.chainId(1); - assertEq(positionDescriptor.currencyRatioPriority(WETH9), CurrencyRatioSortOrder.DENOMINATOR_2); + assertEq(positionDescriptor.currencyRatioPriority(WETH9), CurrencyRatioSortOrder.DENOMINATOR); + assertEq(positionDescriptor.currencyRatioPriority(address(0)), CurrencyRatioSortOrder.DENOMINATOR); assertEq(positionDescriptor.currencyRatioPriority(USDC), CurrencyRatioSortOrder.NUMERATOR_MOST); assertEq(positionDescriptor.currencyRatioPriority(USDT), CurrencyRatioSortOrder.NUMERATOR_MORE); assertEq(positionDescriptor.currencyRatioPriority(DAI), CurrencyRatioSortOrder.NUMERATOR); @@ -54,7 +55,8 @@ contract PositionDescriptorTest is Test, PosmTestSetup { } function test_currencyRatioPriority_notMainnet_succeeds() public { - assertEq(positionDescriptor.currencyRatioPriority(WETH9), CurrencyRatioSortOrder.DENOMINATOR_2); + assertEq(positionDescriptor.currencyRatioPriority(WETH9), CurrencyRatioSortOrder.DENOMINATOR); + assertEq(positionDescriptor.currencyRatioPriority(address(0)), CurrencyRatioSortOrder.DENOMINATOR); assertEq(positionDescriptor.currencyRatioPriority(USDC), 0); assertEq(positionDescriptor.currencyRatioPriority(USDT), 0); assertEq(positionDescriptor.currencyRatioPriority(DAI), 0);