Talking about NFT image generation in UniswapV3

1. NFT and SVG

This year, I opened the peripheral contracts in UniswapV3 to learn, and suddenly found a NFTSVG.sol in it. Seeing that the name uses SVG to represent NFT, I happened to have studied the application connection between NFT and SVG before, so I opened the source code and took a general look, which is exactly the case.

We know that the popularity of NFT started with the encrypted cat on Ethereum. Each encrypted cat is actually an ERC721 token, and this token corresponds to a set of data structures, such as the cat's owner, the cat's eye color, etc. But when we display it on the front end, what does the cat's eyes look like? It is a combination of front-end images, that is, the image of your cat is actually stored on their website. There are URLs in the later stage, each token (cat) corresponds to a url address, and this address is an image of a cat. Therefore, the image here exists on their server.

There is a problem here. When the front-end and server of the encrypted cat are turned off, how can you still display this cat? The answer is no! So can we perpetuate this image on Ethereum? The answer is yes! Due to the storage limitations of Ethereum, it is not convenient to store ordinary encoded images directly on it, and it is not convenient to modify them. But SVG can. Although SVG is a vector image, it is more like a standardized code, and you can even add custom tags to it. For this reason, we put forward the EIP-2569 proposal to save the ERC20/721 token image directly on Ethereum earlier. The proposal was pulled on March 28, 2020. Here is the specific link https://github.com/ethereum/EIPs/pull/2569
and SVG is interactive and responds to some events like clicks, mouseovers, etc.

In UniswapV3, this method is also adopted (it cannot be said that our method is adopted). Write the SVG template directly in the code, and then use the abi.encodePackedfunction to combine the template and the parameters of the corresponding position, and finally convert it into svg source code (string) output. In this way, our NFT image can be obtained directly on Ethereum, even if Uniswap is closed, it does not matter, your token image has been permanent on Ethereum.

2. NFT in UniswapV3

Let's take a look at the specific NFT image of UniswapV3 (the NFT here actually represents the liquidity of a pool added by the user): From the above figure, we can see that the pool corresponding to this NFT
insert image description here
is DAI/WETH, and the handling fee is 1%.
The luck is still a little bit worse, only one digit is 6666. Of course, this is far too far, and even if the ID is all 6, it is not extra useful.

3. NFT generation code of UniswapV3

Ok, the image is finished, let's look at the two pieces of code intercepted on UniswapV3: the
first paragraph, the external interface, pass in the corresponding parameters to generate an NFT SVG image:

function generateSVG(SVGParams memory params) internal pure returns (string memory svg) {
    
    
    /*
    address: "0xe8ab59d3bcde16a29912de83a90eb39628cfc163",
    msg: "Forged in SVG for Uniswap in 2021 by 0xe8ab59d3bcde16a29912de83a90eb39628cfc163",
    sig: "0x2df0e99d9cbfec33a705d83f75666d98b22dea7c1af412c584f7d626d83f02875993df740dc87563b9c73378f8462426da572d7989de88079a382ad96c57b68d1b",
    version: "2"
    */
    return
        string(
            abi.encodePacked(
                generateSVGDefs(params),
                generateSVGBorderText(
                    params.quoteToken,
                    params.baseToken,
                    params.quoteTokenSymbol,
                    params.baseTokenSymbol
                ),
                generateSVGCardMantle(params.quoteTokenSymbol, params.baseTokenSymbol, params.feeTier),
                generageSvgCurve(params.tickLower, params.tickUpper, params.tickSpacing, params.overRange),
                generateSVGPositionDataAndLocationCurve(
                    params.tokenId.toString(),
                    params.tickLower,
                    params.tickUpper
                ),
                generateSVGRareSparkle(params.tokenId, params.poolAddress),
                '</svg>'
            )
        );
}

As you can see, this image is composed of multiple parts, such as definition, border text, middle content, and finally the SVG end tag. Let's take a look at the following code screenshot:
insert image description here
I'm just a simple screenshot of this code, you can see the source code on github for the specific code. We can see that the first line of the output string is <svg width="290" height="500" viewBox="0 0 290 500" xmlns="http://www.w3.org/2000/svg", this is the SVG definition. Then it's more complicated, and the Base64encoding is embedded in the SVG, see

Base64.encode(
    bytes(
        abi.encodePacked(
            "<svg width='290' height='500' viewBox='0 0 290 500' xmlns='http://www.w3.org/2000/svg'><rect width='290px' height='500px' fill='#",
            params.color0,
            "'/></svg>"
        )
    )
),

This code should draw a rectangle with a width of 290 pixels and a height of 500 pixels. This author is not a professional in SVG, so I will not study how to draw it.

We will not read the rest of the code for the time being, in a word. The way it generates SVG source code is to continuously use abi.encodePackedfunctions to combine template strings and corresponding parameter values, and finally combine them into a complete svg source code string.

3. Rare properties of NFT in UniswapV3

To remind again, the NFT in UniswapV3 is actually the liquidity you add, don’t give it away (sell) casually. At the same time, this NFT is divided into rare or common, so what kind of NFT is rare? Here is the judgment code:

function isRare(uint256 tokenId, address poolAddress) internal pure returns (bool) {
    
    
    bytes32 h = keccak256(abi.encodePacked(tokenId, poolAddress));
    return uint256(h) < type(uint256).max / (1 + BitMath.mostSignificantBit(tokenId) * 2);
}

The first step of the code is to combine the tokenId and the transaction pair (pool) address to perform a hash operation, and then compare the result of the calculation with the result of a certain operation. Let's calculate it according to the code:

Before the calculation, we need to obtain the Pool address of the corresponding fee. From the above figure, we can see that the two tokens and addresses of the transaction pair corresponding to the NFT are:

  • DAI:0x6b175474e89094c44da98b954eedeac495271d0f
  • WETH : 0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2
  • fee: 10000. Because our commission rate is 1% and the denominator is 1,000,000.
  • poolAddress:0xa80964C5bBd1A0E95777094420555fead1A26c1e

We directly query the corresponding pool address in the Factory contract. The query address is:
https://cn.etherscan.com/address/0x1f98431c8ad98523631ae4a59f267346ea31f984#readContract

Click the getPool button, enter the above address and rate, click the query button to get the address: 0xa80964C5bBd1A0E95777094420555fead1A26c1e. This is our poolAddress.

In order to calculate whether it is rare, we decompose the above function (internal, cannot be called directly), and write a contract to calculate.

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.7.6;

import '@uniswap/v3-core/contracts/libraries/BitMath.sol';

contract RareTest{
    
    
    function getBytes(uint256 tokenId, address poolAddress) public pure returns (bytes32) {
    
    
        bytes32 h = keccak256(abi.encodePacked(tokenId, poolAddress));
        return h;
    }
    
    function getUint(bytes32 h) public pure returns(uint) {
    
    
        return uint(h);
    }
    
    function getResult(uint tokenId) public pure returns(uint) {
    
    
        return type(uint256).max / (1 + BitMath.mostSignificantBit(tokenId) * 2);
    }
    
    function isRare(uint256 tokenId, address poolAddress) public pure returns (bool) {
    
    
        bytes32 h = keccak256(abi.encodePacked(tokenId, poolAddress));
        return uint256(h) < type(uint256).max / (1 + BitMath.mostSignificantBit(tokenId) * 2);
    }
}

We use remix directly for testing (JavaScript VM is selected when deploying), and the results obtained by calling the above functions are:

getBytes:  0x7510738a918c5116c753b45e7b5a58aa3994cf345e426f54cd9405b1fda306f6
getUint:   52949670273909147826988446709444914284054628203600607669243403349492999849718
getResult: 4631683569492647816942839400347516314130799386625622561578303360316525185597
isRare:    false

From the above output, we can verify that our NFT is not rare, so what is the rare one? code show as below:

function generateSVGRareSparkle(uint256 tokenId, address poolAddress) private pure returns (string memory svg) {
    
    
    if (isRare(tokenId, poolAddress)) {
    
    
        svg = string(
            abi.encodePacked(
                '<g style="transform:translate(226px, 392px)"><rect width="36px" height="36px" rx="8px" ry="8px" fill="none" stroke="rgba(255,255,255,0.2)" />',
                '<g><path style="transform:translate(6px,6px)" d="M12 0L12.6522 9.56587L18 1.6077L13.7819 10.2181L22.3923 6L14.4341 ',
                '11.3478L24 12L14.4341 12.6522L22.3923 18L13.7819 13.7819L18 22.3923L12.6522 14.4341L12 24L11.3478 14.4341L6 22.39',
                '23L10.2181 13.7819L1.6077 18L9.56587 12.6522L0 12L9.56587 11.3478L1.6077 6L10.2181 10.2181L6 1.6077L11.3478 9.56587L12 0Z" fill="white" />',
                '<animateTransform attributeName="transform" type="rotate" from="0 18 18" to="360 18 18" dur="10s" repeatCount="indefinite"/></g></g>'
            )
        );
    } else {
    
    
        svg = '';
    }
}

It can be seen that the rare one has an additional deformation (animation), and I don't know the specific effect if it is not a rare token. Maybe SVG pros can revert it back.

4. Other

Well, that's it for the NFT image generation of UniswapV3.

Here are a few beautiful commemorative coins that we specially made when we demonstrated EIP-2569 (the images are also stored on Ethereum in SVG format). Originally, the last commemorative coins for Children's Day could be received for free, but due to the change of the gas cost of some operations in the Ethereum Berlin upgrade in April this year, it out of gascannot be received now (other commemorative coins cannot be purchased successfully due to this impact), sorry! ! ! . The address is released here, and friends who are interested can go and have a look.

http://toh.best/latest

Guess you like

Origin blog.csdn.net/weixin_39430411/article/details/118882290