// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity =0.7.6; pragma abicoder v2; import '@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol'; import '@uniswap/lib/contracts/libraries/SafeERC20Namer.sol'; import './libraries/ChainId.sol'; import './interfaces/INonfungiblePositionManager.sol'; import './interfaces/INonfungibleTokenPositionDescriptor.sol'; import './interfaces/IERC20Metadata.sol'; import './libraries/PoolAddress.sol'; import './libraries/NFTDescriptor.sol'; import './libraries/TokenRatioSortOrder.sol'; /// @title Describes NFT token positions /// @notice Produces a string containing the data URI for a JSON metadata string contract NonfungibleTokenPositionDescriptor is INonfungibleTokenPositionDescriptor { address private constant DAI = 0x6B175474E89094C44Da98b954EedeAC495271d0F; address private constant USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48; address private constant USDT = 0xdAC17F958D2ee523a2206206994597C13D831ec7; address private constant TBTC = 0x8dAEBADE922dF735c38C80C7eBD708Af50815fAa; address private constant WBTC = 0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599; address public immutable WETH9; constructor(address _WETH9) { WETH9 = _WETH9; } /// @inheritdoc INonfungibleTokenPositionDescriptor function tokenURI(INonfungiblePositionManager positionManager, uint256 tokenId) external view override returns (string memory) { (, , address token0, address token1, uint24 fee, int24 tickLower, int24 tickUpper, , , , , ) = positionManager.positions(tokenId); IUniswapV3Pool pool = IUniswapV3Pool( PoolAddress.computeAddress( positionManager.factory(), PoolAddress.PoolKey({token0: token0, token1: token1, fee: fee}) ) ); bool _flipRatio = flipRatio(token0, token1, ChainId.get()); address quoteTokenAddress = !_flipRatio ? token1 : token0; address baseTokenAddress = !_flipRatio ? token0 : token1; (, int24 tick, , , , , ) = pool.slot0(); return NFTDescriptor.constructTokenURI( NFTDescriptor.ConstructTokenURIParams({ tokenId: tokenId, quoteTokenAddress: quoteTokenAddress, baseTokenAddress: baseTokenAddress, quoteTokenSymbol: quoteTokenAddress == WETH9 ? 'ETH' : SafeERC20Namer.tokenSymbol(quoteTokenAddress), baseTokenSymbol: baseTokenAddress == WETH9 ? 'ETH' : SafeERC20Namer.tokenSymbol(baseTokenAddress), quoteTokenDecimals: IERC20Metadata(quoteTokenAddress).decimals(), baseTokenDecimals: IERC20Metadata(baseTokenAddress).decimals(), flipRatio: _flipRatio, tickLower: tickLower, tickUpper: tickUpper, tickCurrent: tick, tickSpacing: pool.tickSpacing(), fee: fee, poolAddress: address(pool) }) ); } function flipRatio( address token0, address token1, uint256 chainId ) public view returns (bool) { return tokenRatioPriority(token0, chainId) > tokenRatioPriority(token1, chainId); } function tokenRatioPriority(address token, uint256 chainId) public view returns (int256) { if (token == WETH9) { return TokenRatioSortOrder.DENOMINATOR; } if (chainId == 1) { if (token == USDC) { return TokenRatioSortOrder.NUMERATOR_MOST; } else if (token == USDT) { return TokenRatioSortOrder.NUMERATOR_MORE; } else if (token == DAI) { return TokenRatioSortOrder.NUMERATOR; } else if (token == TBTC) { return TokenRatioSortOrder.DENOMINATOR_MORE; } else if (token == WBTC) { return TokenRatioSortOrder.DENOMINATOR_MOST; } else { return 0; } } return 0; } }