// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.14; import "openzeppelin/contracts/utils/Base64.sol"; import "openzeppelin/contracts/utils/Strings.sol"; import "solmate/src/tokens/ERC20.sol"; import "../interfaces/IUniswapV3Pool.sol"; library NFTRenderer { struct RenderParams { address pool; address owner; int24 lowerTick; int24 upperTick; uint24 fee; } function render(RenderParams memory params) internal view returns (string memory) { IUniswapV3Pool pool = IUniswapV3Pool(params.pool); ERC20 token0 = ERC20(pool.token0()); ERC20 token1 = ERC20(pool.token1()); string memory symbol0 = token0.symbol(); string memory symbol1 = token1.symbol(); string memory image = string.concat( "", "", renderBackground(params.owner, params.lowerTick, params.upperTick), renderTop(symbol0, symbol1, params.fee), renderBottom(params.lowerTick, params.upperTick), "" ); string memory description = renderDescription( symbol0, symbol1, params.fee, params.lowerTick, params.upperTick ); string memory json = string.concat( '{"name":"Uniswap V3 Position",', '"description":"', description, '",', '"image":"data:image/svg+xml;base64,', Base64.encode(bytes(image)), '"}' ); return string.concat( "data:application/json;base64,", Base64.encode(bytes(json)) ); } //////////////////////////////////////////////////////////////////////////// // // INTERNAL // //////////////////////////////////////////////////////////////////////////// function renderBackground( address owner, int24 lowerTick, int24 upperTick ) internal pure returns (string memory background) { bytes32 key = keccak256(abi.encodePacked(owner, lowerTick, upperTick)); uint256 hue = uint256(key) % 360; background = string.concat( '', '' ); } function renderTop( string memory symbol0, string memory symbol1, uint24 fee ) internal pure returns (string memory top) { top = string.concat( '', '', symbol0, "/", symbol1, "" '', '', feeToText(fee), "" ); } function renderBottom(int24 lowerTick, int24 upperTick) internal pure returns (string memory bottom) { bottom = string.concat( '', 'Lower tick: ', tickToText(lowerTick), "", '', 'Upper tick: ', tickToText(upperTick), "" ); } function renderDescription( string memory symbol0, string memory symbol1, uint24 fee, int24 lowerTick, int24 upperTick ) internal pure returns (string memory description) { description = string.concat( symbol0, "/", symbol1, " ", feeToText(fee), ", Lower tick: ", tickToText(lowerTick), ", Upper text: ", tickToText(upperTick) ); } function feeToText(uint256 fee) internal pure returns (string memory feeString) { if (fee == 500) { feeString = "0.05%"; } else if (fee == 3000) { feeString = "0.3%"; } } function tickToText(int24 tick) internal pure returns (string memory tickString) { tickString = string.concat( tick < 0 ? "-" : "", tick < 0 ? Strings.toString(uint256(uint24(-tick))) : Strings.toString(uint256(uint24(tick))) ); } }