# [Decipher] UniswapV3の『NonfungibleTokenPositionDescriptor』コントラクトを理解しよう！

> Uniswap V3プロトコルの「NonfungibleTokenPositionDescriptor（NFTポジションの詳細）」コントラクトは、非代替トークン（NFT）のポジションについて詳細な情報を提供します。

- URL: https://cardene.dev/articles/e59072e94c78e7
- 公開日: 2023-09-02
- 更新日: 2023-09-02
- トピック: Blockchain
- タグ: solidity, ethereum, smartcontract, defi

# はじめに

初めまして。
**CryptoGames**というブロックチェーンゲーム企業でエンジニアをしている **cardene（かるでね）** です！
スマートコントラクトを書いたり、フロントエンド・バックエンド・インフラと幅広く触れています。

https://cryptogames.co.jp/

代表的なゲームは**クリプトスペルズ**というブロックチェーンゲームです。

https://cryptospells.jp/

今回はBunzzの新機能『**DeCipher**』を使用して、**UniswapV3**の「**NonfungibleTokenPositionDescriptor**」のコントラクトを見てみようと思います。

『**DeCipher**』はAIを使用してコントラクトのドキュメントを自動生成してくれるサービスです。

https://www.bunzz.dev/decipher

詳しい使い方に関しては以下の記事を参考にしてください！

https://zenn.dev/heku/articles/33266f0c19d523

:::message
この記事はあくまで個人が「**DeCipher**」を使用してみての記事になります。
間違った部分などもあるかと思いますが、その際はコメントなどで教えてください。
:::

今回使用する『**DeCipher**』のリンクは以下になります。

https://app.bunzz.dev/decipher/chains/1/addresses/0x91ae842A5Ffd8d12023116943e72A606179294f3

Etherscanのリンクは以下になります。

https://etherscan.io/address/0x91ae842A5Ffd8d12023116943e72A606179294f3

# 概要

**Uniswap V3**プロトコルの「**NonfungibleTokenPositionDescriptor（NFTポジションの詳細）**」コントラクトは、非代替トークン（NFT）のポジションについて詳細な情報を提供します。

「**NonfungibleTokenPositionDescriptor**」コントラクトの目的は、**Uniswap V3**プロトコル内のNFTポジションに関する包括的な説明を提供することです。
具体的には、トークンのID、プールのアドレス、ティックの範囲、流動性の量など、特定のNFTポジションに関連する重要な情報をユーザーが取得できます。

このコントラクトは、NFTポジションを視覚的に表現するためのSVG画像を生成する役割も担っています。
そのために、さまざまなライブラリやインターフェース（例: **SafeERC20Namer**、**ChainId**、**NFTDescriptor**、**TokenRatioSortOrder**など）を使用して、NFTポジションを正確に説明し、視覚化します。

「**NonfungibleTokenPositionDescriptor**」コントラクトがNFTポジションの詳細な概要を提供することで、ユーザーエクスペリエンスが向上し、**Uniswap V3**プロトコルの理解と分析が容易になります。
これにより、ユーザーは自身のNFTポジションに関する情報を得て、トレード戦略を最適化するための意思決定を行えるようになります。

# 使い方

「**NonfungibleTokenPositionDescriptor**」コントラクトは、**Uniswap V3**プロトコル内の非代替トークン（NFT）ポジションとやり取りするためのスマートコントラクトです。
このコントラクトを通じて、開発者はNFTポジションに関連する情報を取得したり、SVG情報を取得したりすることができます。
また、ポジションに対してさまざまな操作を実行することもできます。

## 目的
このコントラクトの目標は、**Uniswap V3**プロトコル内のNFTポジションに関するメタデータやSVG表現を標準的な方法で取得できるようにすることです。
また、開発者はミントやバーンといった操作、そしてメタデータの更新なども行えます。

## 使い方

1. 「**NonfungibleTokenPositionDescriptor**」コントラクトをデプロイします。
2. `tokenURI`関数を使用して、NFTポジションのメタデータURIを取得します。
3. 特定のポジションIDを指定して、特定のNFTポジションのメタデータURIを取得します。
4. 特定のポジションIDとティック間隔を指定して、カスタムなティック間隔でNFTポジションのメタデータURIを取得します。
5. 特定のポジションIDとカスタムなSVGパラメータを指定して、カスタムなSVGパラメータでNFTポジションのメタデータURIを取得します。
6. 特定のポジションIDとカスタムなSVGパラメータ、ティック間隔を指定して、カスタムなSVGパラメータとティック間隔でNFTポジションのメタデータURIを取得します。
7. `mint`関数を使用して新しいNFTポジションを作成します。
8. `burn`関数を使用して既存のNFTポジションを破棄します。
9. `updateTokenURI`関数を使用して既存のNFTポジションのメタデータURIを更新します。

## 関数

### tokenURI(uint256 tokenId) 
NFTポジションのメタデータURIを取得します。

### tokenURI
カスタムなティック間隔で特定のNFTポジションのメタデータURIを取得します。

### tokenURI
カスタムなSVGパラメータを持つ特定のNFTポジションのメタデータURIを取得します。

### tokenURI
カスタムなSVGパラメータとティック間隔を持つ特定のNFTポジションのメタデータURIを取得します。

### mint
新しいNFTポジションをミントします。

### burn
既存のNFTポジションをバーンします。

### updateTokenURI
既存のNFTポジションのメタデータURIを更新します。

## イベント

### Mint
新しいNFTポジションがミントされたときに発行されます。

### Burn
既存のNFTポジションがバーンされたときに発行されます。

### UpdateTokenURI
既存のNFTポジションのメタデータURIが更新されたときに発行されます。

# コントラクト

## NonfungibleTokenPositionDescriptor

### DAI

```solidity
address private constant DAI = 0x6B175474E89094C44Da98b954EedeAC495271d0F;
```

**概要**
DAIトークンのコントラクトアドレス。

---

### USDC

```solidity
address private constant USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;
```

**概要**
USDCトークンのコントラクトアドレス。

---

### USDT

```solidity
address private constant USDT = 0xdAC17F958D2ee523a2206206994597C13D831ec7;
```

**概要**
USDTトークンのコントラクトアドレス。

---

### TBTC

```solidity
address private constant TBTC = 0x8dAEBADE922dF735c38C80C7eBD708Af50815fAa;
```

**概要**
TBTCトークンのコントラクトアドレス。

---

### WBTC

```solidity
address private constant WBTC = 0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599;
```

**概要**
WBTCトークンのコントラクトアドレス。

---

### WETH9

```solidity
address public immutable WETH9;
```

**概要**
WETHトークンのコントラクトアドレス。

---

### tokenURI

:::details tokenURI
```solidity
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: SafeERC20Namer.tokenSymbol(quoteTokenAddress),
                baseTokenSymbol: 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)
            })
        );
}
```
:::

**概要**
指定されたトークンIDに対応する**Uniswap V3**のポジションのメタデータを取得する関数。

**詳細**
1. 与えられたトークンIDからポジションの情報を取得します。
2. ポジションのトークン0およびトークン1のアドレス、手数料、`tick`の範囲などを取得します。
3. ポジションのトークン0およびトークン1を使用して対応するプールのアドレスを計算します。
4. トークン0およびトークン1のアドレスから通貨シンボルおよび小数点以下桁数を取得します。
5. トークン0およびトークン1のアドレスを基にフリップ比率を計算します。
6. プールから現在の`tick`を取得します。
7. **NFTDescriptor**ライブラリのメソッドを使用して、トークンURIを構築します。

**引数**
- `positionManager`
	- **INonfungiblePositionManager**インターフェースを持つポジションマネージャのインスタンス。
- `tokenId`
	- トークンID。

**戻り値**
- `string`
	- ポジションのメタデータを含むトークンURIの文字列。

:::message
**例：**
仮想通貨Aと仮想通貨Bをトークン0およびトークン1として持つ**Uniswap V3**のポジションがあり、それに対応するトークンIDが`12345`とします。
この場合、以下のように関数が動作します。

1. トークンID`12345`に対応するポジションの情報を取得します。
この情報にはトークン0のアドレス、トークン1のアドレス、手数料、`tick`の範囲などが含まれます。
2. トークン0とトークン1のアドレスを使用して、**Uniswap V3**のプールのアドレスを計算します。
3. トークン0とトークン1のアドレスからそれぞれの通貨のシンボル（例: **ETH**, **USDC**）および小数点以下の桁数を取得します。
4. トークン0とトークン1のアドレスを使用して、通貨交換の優先度を計算します（フリップ比率を決定します）。
5. プールから現在の`tick`を取得します。
6. **NFTDescriptor**ライブラリの`constructTokenURI`メソッドを使用して、トークンURIを作成します。
この際、トークンID、通貨のシンボル、小数点以下の桁数、フリップ比率、`tick`の範囲、手数料などの情報が含まれます。
7. 最終的に、構築されたトークンURIが文字列として返されます。
このURIは、この特定のトークンIDに関連する**Uniswap V3**ポジションのメタデータを表します。

このように、`tokenURI`関数は与えられたトークンIDに対応する**Uniswap V3**ポジションのメタデータを取得し、それを元にトークンURIを構築して返す役割を果たします。
このURIは、外部からポジションの詳細情報やメタデータを取得するために使用されます。
:::

---

### flipRatio

:::details flipRatio
```solidity
function flipRatio(
    address token0,
    address token1,
    uint256 chainId
) public view returns (bool) {
    return tokenRatioPriority(token0, chainId) > tokenRatioPriority(token1, chainId);
}
```
:::

**概要**
与えられた2つのトークンのフリップ比率（通貨交換の優先順位）を計算して返す関数。

**詳細**
1. `tokenRatioPriority`関数を使用して、各トークンの優先度を取得します。
2. トークン0の優先度がトークン1の優先度より高ければ、`true`を返し、それ以外の場合は`false`を返します。

**引数**
- `token0`
	- トークン0のアドレス。
- `token1`
	- トークン1のアドレス。
- `chainId`
	- チェーンID。

**戻り値**
- `bool`
	- トークンのフリップ比率が反転しているかどうかを示すブール値。

---

### tokenRatioPriority

:::details tokenRatioPriority
```solidity
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;
}
```
:::

**概要**
トークンのフリップ比率の優先順位を計算して返す関数。

**詳細**
1. トークンが**WETH9（Wrapped Ether）** の場合、`TokenRatioSortOrder.DENOMINATOR` を返します。
2. メインネット（チェーンIDが`1`）の場合、特定のトークンに対して優先度を割り当てます。
異なるトークンには異なる値が割り当てられます。
3. 上記の条件に当てはまらない場合、デフォルトで`0`を返します。

**引数**
- `token`
	- トークンのアドレス。
- `chainId`
	- チェーンID。

**戻り値**
- `int256`
	- トークンのフリップ比率の優先順位を示す整数値。

---

## ChainId

以下を参考にしてください。

https://zenn.dev/cryptogames/articles/96c6fa9d04460c#chainid

## PoolAddress

以下を参考にしてください。

https://zenn.dev/cryptogames/articles/96c6fa9d04460c#pooladdress

## NFTDescriptor

### sqrt10X128

:::details sqrt10X128
```solidity
uint256 constant sqrt10X128 = 1076067327063303206878105757264492625226;
```
:::

**概要**
浮動小数点数として表される数値の平方根（√10）を整数値の定数。

**詳細**
固定小数点数表現を使用して浮動小数点数を整数で近似的に表すために使用されます。
浮動小数点数の平方根を整数で表現することで、計算の効率を向上させることができます。

---

### ConstructTokenURIParams

:::details ConstructTokenURIParams
```solidity
struct ConstructTokenURIParams {
    uint256 tokenId;
    address quoteTokenAddress;
    address baseTokenAddress;
    string quoteTokenSymbol;
    string baseTokenSymbol;
    uint8 quoteTokenDecimals;
    uint8 baseTokenDecimals;
    bool flipRatio;
    int24 tickLower;
    int24 tickUpper;
    int24 tickCurrent;
    int24 tickSpacing;
    uint24 fee;
    address poolAddress;
}
```
:::

**概要**
`tokenURI`関数内で使用されるパラメータのセットを定義している構造体。
この構造体のインスタンスを使用して、トークンURIの作成に必要な情報を保持します。

**パラメータ**
- `tokenId`
	- トークンのID。
- `quoteTokenAddress`
	- クォート通貨のアドレス。
- `baseTokenAddress`
	- ベース通貨のアドレス。
- `quoteTokenSymbol`
	- クォート通貨のシンボル（例: **USDC**）。
- `baseTokenSymbol`
	- ベース通貨のシンボル（例: **ETH**）。
- `quoteTokenDecimals`
	- クォート通貨の小数点以下桁数。
- `baseTokenDecimals`
	- ベース通貨の小数点以下桁数。
- `flipRatio`
	- 通貨交換のフリップ比率が反転しているかどうかのブール値。
- `tickLower`
	- 下限の`tick`値。
- `tickUpper`
	- 上限の`tick`値。
- `tickCurrent`
	- 現在の`tick`値。
- `tickSpacing`
	- `tick`間隔。
- `fee`
	- 手数料率。
- `poolAddress`
	- **Uniswap V3**プールのアドレス。

---

### constructTokenURI

:::details constructTokenURI
```solidity
function constructTokenURI(ConstructTokenURIParams memory params) public pure returns (string memory)
```
:::

**概要**
トークンのURIを構築する関数。
URIにはトークンの名前、説明、画像などが含まれます。

**詳細**
- `ConstructTokenURIParams`構造体を引数として受け取り、それを使用してトークンの各要素を生成しています。
- 生成された要素はJSON形式の文字列としてエンコードされ、`data:application/json;base64,`で始まる形式の文字列として返されます。

**引数**
- `params`
	- `ConstructTokenURIParams`構造体のメモリ上のインスタンス。
	- トークンの要素を生成するための情報が含まれています。

**戻り値**
- トークンの情報が含まれたURI文字列。

---

### escapeQuotes

:::details escapeQuotes
```solidity
function escapeQuotes(string memory symbol) internal pure returns (string memory)
```
:::

**概要**
文字列内のダブルクォーテーションをエスケープ処理する関数。

**詳細**
パラメータで渡された文字列内のダブルクォーテーションをエスケープし、新しい文字列を返します。

**引数**
- `symbol`
	- エスケープする対象の文字列。

**戻り値**
エスケープされた文字列。

---

### generateDescriptionPartOne

:::details generateDescriptionPartOne
```solidity
function generateDescriptionPartOne(
    string memory quoteTokenSymbol,
    string memory baseTokenSymbol,
    string memory poolAddress
) private pure returns (string memory)
```
:::

**概要**
トークンの説明の一部を生成する関数。
**Uniswap V3**プールの情報が含まれます。

**詳細**
引数として受け取ったトークンのシンボル、プールのアドレスなどを使用して、トークンの説明の一部を生成します。

**引数**
- `quoteTokenSymbol`
	- 引用通貨のシンボル。
- `baseTokenSymbol`
	- 基本通貨のシンボル。
- `poolAddress`
	- プールのアドレス。

**戻り値**
トークンの説明の一部を表す文字列。

---

### generateDescriptionPartTwo

:::details generateDescriptionPartTwo
```solidity
function generateDescriptionPartTwo(
    string memory tokenId,
    string memory baseTokenSymbol,
    string memory quoteTokenAddress,
    string memory baseTokenAddress,
    string memory feeTier
) private pure returns (string memory)
```
:::

**概要**
トークンの説明の別の一部を生成する関数。
トークンのアドレスや手数料などが含まれます。

**詳細**
引数として受け取ったトークンの情報やアドレスを使用して、トークンの説明のもう一部を生成します。

**引数**
- `tokenId`
	- トークンのID。
- `baseTokenSymbol`
	- 基本通貨のシンボル。
- `quoteTokenAddress`
	- 引用通貨のアドレス。
- `baseTokenAddress`
	- 基本通貨のアドレス。
- `feeTier`
	- 手数料のティア。

**戻り値**
トークンの説明の別の一部を表す文字列。

---

### generateName

:::details generateName
```solidity
function generateName(ConstructTokenURIParams memory params, string memory feeTier)
    private
    pure
    returns (string memory)
```
:::

**概要**
トークンの名前を生成する関数。
**Uniswap V3**プールの情報が含まれます。

**詳細**
引数として受け取ったトークンの情報やティア、アドレスを使用して、トークンの名前を生成します。

**引数**
- `params`
	- `ConstructTokenURIParams`構造体のメモリ上のインスタンス。
	- トークンの要素を生成するための情報が含まれています。
- `feeTier`
	- 手数料のティア。

**戻り値**
トークンの名前を表す文字列。

---

### DecimalStringParams

:::details DecimalStringParams
```solidity
struct DecimalStringParams {
    uint256 sigfigs;
    uint8 bufferLength;
    uint8 sigfigIndex;
    uint8 decimalIndex;
    uint8 zerosStartIndex;
    uint8 zerosEndIndex;
    bool isLessThanOne;
    bool isPercent;
}
```
:::

**概要**
小数を文字列に変換する際のパラメータを定義する構造体。
この構造体のインスタンスを使用して、小数を指定されたフォーマットに従って文字列に変換する際の詳細な設定を行います。

**パラメータ**
- `sigfigs`
	- 有効数字の桁数。
- `bufferLength`
	- 文字列の長さ。
- `sigfigIndex`
	- 有効数字の末尾インデックス（有効数字のコピー時に逆向きに動作します）。
- `decimalIndex`
	- 小数点のインデックス（小数点がない場合は`0`）。
- `zerosStartIndex`
	- 非常に小さい/大きい数値のための先頭/末尾の`0`の開始インデックス。
- `zerosEndIndex`
	- 非常に小さい/大きい数値のための先頭/末尾の`0`の終了インデックス。
- `isLessThanOne`
	- 小数が`1`未満かどうかを示すブール値。
- `isPercent`
	- 文字列に「`%`」を含めるかどうかを示すブール値。

---

### generateDecimalString

:::details generateDecimalString
```solidity
function generateDecimalString(DecimalStringParams memory params) private pure returns (string memory)
```
:::

**概要**
数値を小数表記の文字列に変換する関数。

**詳細**
- `DecimalStringParams`構造体を引数として受け取り、指定されたパラメータに基づいて数値を小数表記の文字列に変換します。
- 小数点以下の桁数やパーセンテージ表示、小数点未満の数値など、さまざまな条件に応じて適切な文字列を生成します。

**引数**
- `params`
	- `DecimalStringParams`構造体のメモリ上のインスタンス。
	- 数値変換のためのパラメータが含まれています。

**戻り値**
変換された小数表記の文字列。

---

### tickToDecimalString

:::details tickToDecimalString
```solidity
function tickToDecimalString(
    int24 tick,
    int24 tickSpacing,
    uint8 baseTokenDecimals,
    uint8 quoteTokenDecimals,
    bool flipRatio
) internal pure returns (string memory)
```
:::

**概要**
**Uniswap V3**のトークン価格を小数表記の文字列に変換する関数。

**詳細**
与えられた`tick`値に基づいて、対応するトークン価格を計算し、小数表記の文字列に変換します。最小と最大の`tick`値に対する特別な処理も行います。

**引数**
- `tick`
	- トークン価格を表す`tick`値。
- `tickSpacing`
	- `tick`間隔。
- `baseTokenDecimals`
	- 基本通貨の小数点以下の桁数。
- `quoteTokenDecimals`
	- 引用通貨の小数点以下の桁数。
- `flipRatio`
	- 通貨比率を反転するかどうか。

**戻り値**
変換されたトークン価格の小数表記の文字列。

---

### sigfigsRounded

:::details sigfigsRounded
```solidity
function sigfigsRounded(uint256 value, uint8 digits) private pure returns (uint256, bool)
```
:::

**概要**
この関数は、指定された桁数の有効数字を四捨五入するためのものです。

**詳細**
- 数値と桁数を受け取り、指定された桁数に基づいて有効数字を四捨五入します。

**パラメータ**
- `value`
	- 四捨五入する数値。
- `digits`
	- 有効数字の桁数。

**戻り値**
四捨五入された数値と、四捨五入によって1桁余分になったかどうかを表すブール値のペア。

---

### adjustForDecimalPrecision

:::details adjustForDecimalPrecision
```solidity
function adjustForDecimalPrecision(
    uint160 sqrtRatioX96,
    uint8 baseTokenDecimals,
    uint8 quoteTokenDecimals
) private pure returns (uint256 adjustedSqrtRatioX96)
```
:::

**概要**
通貨間の小数点以下の桁数の差を考慮して価格比率を調整する関数。

**詳細**
平均価格比率と通貨間の小数点以下の桁数の差を受け取り、適切に価格比率を調整します。

**引数**
- `sqrtRatioX96`
	- 平均価格比率の平方根。
- `baseTokenDecimals`
	- 基本通貨の小数点以下の桁数。
- `quoteTokenDecimals`
	- 引用通貨の小数点以下の桁数。

**戻り値**
調整された価格比率。

---

### abs

:::details abs
```solidity
function abs(int256 x) private pure returns (uint256)
```
:::

**概要**
与えられた整数の絶対値を計算する関数。

**引数**
- `x`
	- 絶対値を計算する整数。

**戻り値**
`x`の絶対値を表す正の整数。

---

### fixedPointToDecimalString

:::details fixedPointToDecimalString
```solidity
function fixedPointToDecimalString(
    uint160 sqrtRatioX96,
    uint8 baseTokenDecimals,
    uint8 quoteTokenDecimals
) internal pure returns (string memory)
```
:::

**概要**
固定小数点数を小数表記の文字列に変換する関数。

**詳細**
固定小数点数を受け取り、指定された通貨間の小数点以下の桁数の差を考慮して価格比率を調整し、小数表記の文字列に変換します。
価格が`1`未満の場合と`1`以上の場合で処理が異なります。

**引数**
- `sqrtRatioX96`
	- 平均価格比率の平方根。
- `baseTokenDecimals`
	- 基本通貨の小数点以下の桁数。
- `quoteTokenDecimals`
	- 引用通貨の小数点以下の桁数。

**戻り値**
変換された価格比率の小数表記の文字列。

---

### feeToPercentString

:::details feeToPercentString
```solidity
function feeToPercentString(uint24 fee) internal pure returns (string memory)
```
:::

**概要**
手数料をパーセント形式の文字列に変換する関数。

**詳細**
手数料を受け取り、それをパーセント形式の文字列に変換します。
手数料が`0`の場合や、特定の桁数以下の手数料に対して適切な処理が行われます。

**引数**
- `fee`
	- 手数料の量。

**戻り値**
変換された手数料のパーセント形式の文字列。

---

### addressToString

:::details addressToString
```solidity
function addressToString(address addr) internal pure returns (string memory)
```
:::

**概要**
アドレスを16進数文字列に変換する関数。

**詳細**
アドレスを受け取り、それを`16`進数文字列に変換して返します。

**引数**
- `addr`
	- 変換するアドレス。

**戻り値**
`16`進数表記のアドレス文字列。

---

### generateSVGImage

:::details generateSVGImage
```solidity
function generateSVGImage(ConstructTokenURIParams memory params) internal pure returns (string memory svg)
```
:::

**概要**
NFTのSVG画像を生成する関数。

**詳細**
`ConstructTokenURIParams`構造体から必要な情報を受け取り、それを基にNFTのSVG画像を生成します。
SVGの各要素に対するパラメータや色、座標などを計算し、NFTのデザインを構築します。

**引数**
- `params`
	- `ConstructTokenURIParams`構造体のメモリ上のインスタンス。
	- NFTの情報を含むパラメータが含まれています。

**戻り値**
生成されたSVG画像の文字列。

---

### overRange

:::details overRange
```solidity
function overRange(int24 tickLower, int24 tickUpper, int24 tickCurrent) private pure returns (int8)
```
:::

**概要**
トークンの価格が価格範囲外にあるかどうかを判定する関数。

**詳細**
与えられた現在のトークン価格を基に、価格が設定された範囲よりも上または下にあるかどうかを判定します。

**引数**
- `tickLower`
	- 下限のトークン価格を表す`tick`値。
- `tickUpper`
	- 上限のトークン価格を表す`tick`値。
- `tickCurrent`
	- 現在のトークン価格を表す`tick`値。

**戻り値**
価格が価格範囲外にある場合、`-1`（下限外）または`1`（上限外）、それ以外の場合は`0`。

---

### scale

:::details scale
```solidity
function scale(uint256 n, uint256 inMn, uint256 inMx, uint256 outMn, uint256 outMx) private pure returns (string memory)
```
:::

**概要**
数値を指定された範囲にスケーリングする関数。

**詳細**
与えられた数値を入力範囲から出力範囲にスケーリングします。

**引数**
- `n`
	- スケーリングする数値。
- `inMn`
	- 入力範囲の最小値。
- `inMx`
	- 入力範囲の最大値。
- `outMn`
	- 出力範囲の最小値。
- `outMx`
	- 出力範囲の最大値。

**戻り値**
スケーリングされた数値を文字列として返します。

:::message
例えば、以下のような状況を考えてみます。

- 入力範囲 (`inMn`から`inMx`) は`0`から`100`です。
- 出力範囲 (`outMn`から`outMx`) は`10`から`200`です。
- スケーリングしたい数値`n`は`50`です。

この場合、`n`の値を入力範囲から出力範囲にスケーリングしたいと考えます。
つまり、入力範囲の最小値から最大値までの値を、出力範囲の最小値から最大値までの値に変換したいわけです。

以下に`scale`関数の計算手順を示します。

1. `n`の値は入力範囲の中でどの位置にあるかを計算します。
入力範囲が`0`から`100`なので、`n`の位置は`n - inMn`です。
つまり、`50 - 0 = 50`です。

2. 入力範囲から出力範囲に変換するために、スケールファクターを計算します。
スケールファクターは、出力範囲の幅を入力範囲の幅で割ることで求められます。
この場合、スケールファクターは`outMx - outMn`を`inMx - inMn`で割った値です。
つまり、`150 / 100 = 1.5`です。

3. スケーリングされた値は、位置(`n - inMn`)にスケールファクターを乗じ、出力範囲の最小値を足したものです。
計算式は次の通りです。
`scaledValue = (n - inMn) * スケールファクター + outMn`。
つまり、`50 * 1.5 + 10 = 85`です。

以上の計算により、元々の数値`n`が入力範囲 [0, 100] から出力範囲 [10, 200] にスケーリングされて`85`に変換されました。

このように、`scale` 関数は数値を指定された入力範囲から出力範囲にスケーリングする際に利用される関数です。
:::

---

### tokenToColorHex

:::details tokenToColorHex
```solidity
function tokenToColorHex(uint256 token, uint256 offset) internal pure returns (string memory str)
```
:::

**概要**
トークン情報を元に色の16進数コードを生成する関数。

**詳細**
与えられたトークン情報を基に、指定されたオフセットでビットシフトを行い、`16`進数の色コードを生成します。

**引数**
- `token`
	- トークン情報を表す整数値。
- `offset`
	- ビットシフトのオフセット。

**戻り値**
生成された`16`進数の色コード文字列。

---

### getCircleCoord

:::details getCircleCoord
```solidity
function getCircleCoord(uint256 tokenAddress, uint256 offset, uint256 tokenId) internal pure returns (uint256)
```
:::

**概要**
トークンアドレスやオフセット、トークンIDを元に円の座標を計算する関数。

**詳細**
与えられたトークンアドレス、オフセット、トークンIDを基に、円の座標を計算します。

**引数**
- `tokenAddress`
	- トークンアドレスを表す整数値。
- `offset`
	- ビットシフトのオフセット。
- `tokenId`
	- トークンIDを表す整数値。

**戻り値**
計算された座標を表す整数値

---

### sliceTokenHex

:::details sliceTokenHex
```solidity
function sliceTokenHex(uint256 token, uint256 offset) internal pure returns (uint256)
```
:::

**概要**
トークン情報を元にビットスライスを行う関数。

**詳細**
与えられたトークン情報を指定されたオフセットでビットスライスし、その結果を返します。

**引数**
- `token`
	- トークン情報を表す整数値。
- `offset`
	- ビットスライスのオフセット。

**戻り値**
ビットスライスの結果を表す整数値。

---

## TokenRatioSortOrder

### NUMERATOR_MOST

:::details NUMERATOR_MOST
```solidity
int256 constant NUMERATOR_MOST = 300;
```
:::

**概要**
数値計算において使用される定数。

**詳細**
分数や割合の分子の値を表すために使用されます。
値は`300`です。

---

### NUMERATOR_MORE

:::details NUMERATOR_MORE
```solidity
int256 constant NUMERATOR_MORE = 200;
```
:::

**概要**
数値計算において使用される定数。

**詳細**
- この定数は、分数や割合の分子の値を表すために使用されます。
- 値は`200`です。

---

### NUMERATOR

:::details NUMERATOR
```solidity
int256 constant NUMERATOR = 100;
```
:::

**概要**
数値計算において使用される定数。

**詳細**
分数や割合の分子の値を表すために使用されます。
値は`100`です。

---

### DENOMINATOR_MOST

:::details DENOMINATOR_MOST
```solidity
int256 constant DENOMINATOR_MOST = -300;
```
:::

**概要**
`DENOMINATOR_MOST` は、数値計算において使用される定数です。分母の値を表すために使用されます。

**詳細**
分数や割合の分母の値を表すために使用されます。
- 値は`-300`です。

---

### DENOMINATOR_MORE

:::details DENOMINATOR_MORE
```solidity
int256 constant DENOMINATOR_MORE = -200;
```
:::

**概要**
数値計算において使用される定数。

**詳細**
分数や割合の分母の値を表すために使用されます。
値は`-200`です。

---

### DENOMINATOR

:::details DENOMINATOR
```solidity
int256 constant DENOMINATOR = -100;
```
:::

**概要**
数値計算において使用される定数。

**詳細**
分数や割合の分母の値を表すために使用されます。
値は`-100`です。

---

:::message
これらの定数は、分数や割合の計算に使用されます。
分数や割合を表現する際に、分子と分母の値を指定する必要があります。

例として、分数や割合を計算するシナリオを考えます。

```solidity
int256 numerator = NUMERATOR;  // 分子の値を NUMERATOR (100) に設定
int256 denominator = DENOMINATOR;  // 分母の値を DENOMINATOR (-100) に設定

int256 fraction = numerator / denominator;  // 分子を分母で割った値を計算
```

この例では、`numerator`に`NUMERATOR`の値である`100`を、`denominator`に`DENOMINATOR`の値である`-100`を設定しています。
そして、分子を分母で割って`fraction`の値を計算しています。

同様に、`NUMERATOR_MOST`、`NUMERATOR_MORE`、`DENOMINATOR_MOST`、`DENOMINATOR_MORE`も同じように使われます。
これらの定数を使用することで、分数や割合を簡単かつ正確に計算することができます。

なお、具体例では単純な割り算の場面を示しましたが、これらの定数は分数や割合を含むさまざまな計算で使用されることがあります。
分母や分子の値を指定する際に、これらの定数を使うことで計算の正確性と可読性を高めることができます。
:::

## HexStrings

### ALPHABET

:::details ALPHABET
```solidity
bytes16 internal constant ALPHABET = '0123456789abcdef';
```
:::

**概要**
`16`進数の文字列変換に使用される定数。
16進数表記の文字を格納しています。

**詳細**
`ALPHABET`は、`16`進数の文字`'0'`から`'9'`および`'a'`から`'f'`までの文字を含む`bytes16`型の変数です。

---

### toHexString

:::details toHexString
```solidity
function toHexString(uint256 value, uint256 length) internal pure returns (string memory)
```
:::

**概要**
`uint256`型の数値を固定長の**ASCII**`16`進数文字列に変換する関数。

**詳細**
`buffer`バイト配列を使用して変換結果の文字列を生成します。
`16`進数の文字は`ALPHABET`定数を使用して選択され、ビット演算を使用して変換が行われます。

**引数**
- `value`
	- 変換する整数値。
- `length`
	- 生成する16進数文字列の固定長。

**戻り値**
変換された`16`進数文字列。

---

### toHexStringNoPrefix

:::details toHexStringNoPrefix
```solidity
function toHexStringNoPrefix(uint256 value, uint256 length) internal pure returns (string memory)
```
:::

**概要**
`uint256`型の数値を固定長の**ASCII**`16`進数文字列に変換します。
プレフィックス`'0x'`を含まない形式で変換されます。

**詳細**
`buffer`バイト配列を使用して変換結果の文字列を生成します。
`16`進数の文字は`ALPHABET`定数を使用して選択され、ビット演算を使用して変換が行われます。

**引数**
- `value`
	- 変換する整数値。
- `length`
	- 生成する文字列の固定長。

**戻り値**
変換された`16`進数文字列（プレフィックス`'0x'`なし）。

---

## NFTSVG

### curve1, curve2, curve3, curve4, curve5, curve6, curve7, curve8

:::details curve1, curve2, curve3, curve4, curve5, curve6, curve7, curve8
```solidity
string constant curve1 = 'M1 1C41 41 105 105 145 145';
string constant curve2 = 'M1 1C33 49 97 113 145 145';
string constant curve3 = 'M1 1C33 57 89 113 145 145';
string constant curve4 = 'M1 1C25 65 81 121 145 145';
string constant curve5 = 'M1 1C17 73 73 129 145 145';
string constant curve6 = 'M1 1C9 81 65 137 145 145';
string constant curve7 = 'M1 1C1 89 57.5 145 145 145';
string constant curve8 = 'M1 1C1 97 49 145 145 145';
```
:::

**概要**
これらの定数は、SVG (Scalable Vector Graphics) のパスデータを表す文字列です。
これらの文字列は、SVG ス要素内のコンテンツを定義し、曲線を描画するために使用されます。

**詳細**
- 各`curveX`定数は、SVGパスデータを定義する文字列です。
- これらの文字列は`M`（移動）および`C`（カーブ）コマンドを使用して曲線を描画します。
- `M`コマンドは、パスの始点を指定するために使用されます。
- `C`コマンドは、制御点を指定して曲線を描画するために使用されます。
- パスデータは、始点からカーブの制御点を経て、終点までの座標を指定します。

---

### SVGParams

:::details SVGParams
```solidity
struct SVGParams {
    string quoteToken;
    string baseToken;
    address poolAddress;
    string quoteTokenSymbol;
    string baseTokenSymbol;
    string feeTier;
    int24 tickLower;
    int24 tickUpper;
    int24 tickSpacing;
    int8 overRange;
    uint256 tokenId;
    string color0;
    string color1;
    string color2;
    string color3;
    string x1;
    string y1;
    string x2;
    string y2;
    string x3;
    string y3;
}
```
:::

**概要**
SVG生成に必要なパラメータを格納するための構造体。

**詳細**
- `quoteToken`, `baseToken`
	- トークンのアドレスを示す文字列。
- `poolAddress`
	- プールのアドレスを示す`address`型の変数。
- `quoteTokenSymbol`, `baseTokenSymbol`
	- トークンのシンボル（記号）を示す文字列。
- `feeTier`
	- 手数料の割合を示す文字列。
- `tickLower`, `tickUpper`
	- カーブの下限と上限の`tick`値を示す整数。
- `tickSpacing`
	- カーブの`tick`間隔を示す整数。
- `overRange`
	- `tick`値が範囲外にあるかどうかを示す整数。
- `tokenId`
	- NFTのトークンIDを示す整数。
- `color0`~`color3`
	- カーブの描画に使用するカラーコードを示す文字列。
- `x1`, `y1`, `x2`, `y2`, `x3`, `y3`
	- カーブの制御点座標を示す文字列。

---

### generateSVG

:::details generateSVG
```solidity
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>'
                )
            );
}
```
:::

**概要**
SVG形式の文字列を生成する関数。
与えられた`SVGParams`構造体のパラメータに基づいて、複数のSVG要素を組み合わせて1つの文字列として返します。

**詳細**
- `SVGParams`構造体には、SVGの要素を生成するための複数のパラメータが含まれています。
- 関数内部では、`generateSVGDefs`、`generateSVGBorderText`、`generateSVGCardMantle`、`generageSvgCurve`、`generateSVGPositionDataAndLocationCurve`、`generateSVGRareSparkle`の各関数を呼び出して、それぞれのSVG要素を生成します。
- 最終的にこれらのSVG要素を結合して1つの文字列として返します。

**引数**
- `SVGParams memory params`
	- SVGの要素を生成するためのパラメータが格納された構造体。

**戻り値**
- `svg`
	- 生成されたSVG形式の文字列。

---

### generateSVGDefs

:::details generateSVGDefs
```solidity
function generateSVGDefs(SVGParams memory params) private pure returns (string memory svg) {
        svg = string(
            abi.encodePacked(
                '<svg width="290" height="500" viewBox="0 0 290 500" xmlns="http://www.w3.org/2000/svg"',
                " xmlns:xlink='http://www.w3.org/1999/xlink'>",
                '<defs>',
                '<filter id="f1"><feImage result="p0" xlink:href="data:image/svg+xml;base64,',
                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>"
                        )
                    )
                ),
                '"/><feImage result="p1" xlink:href="data:image/svg+xml;base64,',
                Base64.encode(
                    bytes(
                        abi.encodePacked(
                            "<svg width='290' height='500' viewBox='0 0 290 500' xmlns='http://www.w3.org/2000/svg'><circle cx='",
                            params.x1,
                            "' cy='",
                            params.y1,
                            "' r='120px' fill='#",
                            params.color1,
                            "'/></svg>"
                        )
                    )
                ),
                '"/><feImage result="p2" xlink:href="data:image/svg+xml;base64,',
                Base64.encode(
                    bytes(
                        abi.encodePacked(
                            "<svg width='290' height='500' viewBox='0 0 290 500' xmlns='http://www.w3.org/2000/svg'><circle cx='",
                            params.x2,
                            "' cy='",
                            params.y2,
                            "' r='120px' fill='#",
                            params.color2,
                            "'/></svg>"
                        )
                    )
                ),
                '" />',
                '<feImage result="p3" xlink:href="data:image/svg+xml;base64,',
                Base64.encode(
                    bytes(
                        abi.encodePacked(
                            "<svg width='290' height='500' viewBox='0 0 290 500' xmlns='http://www.w3.org/2000/svg'><circle cx='",
                            params.x3,
                            "' cy='",
                            params.y3,
                            "' r='100px' fill='#",
                            params.color3,
                            "'/></svg>"
                        )
                    )
                ),
                '" /><feBlend mode="overlay" in="p0" in2="p1" /><feBlend mode="exclusion" in2="p2" /><feBlend mode="overlay" in2="p3" result="blendOut" /><feGaussianBlur ',
                'in="blendOut" stdDeviation="42" /></filter> <clipPath id="corners"><rect width="290" height="500" rx="42" ry="42" /></clipPath>',
                '<path id="text-path-a" d="M40 12 H250 A28 28 0 0 1 278 40 V460 A28 28 0 0 1 250 488 H40 A28 28 0 0 1 12 460 V40 A28 28 0 0 1 40 12 z" />',
                '<path id="minimap" d="M234 444C234 457.949 242.21 463 253 463" />',
                '<filter id="top-region-blur"><feGaussianBlur in="SourceGraphic" stdDeviation="24" /></filter>',
                '<linearGradient id="grad-up" x1="1" x2="0" y1="1" y2="0"><stop offset="0.0" stop-color="white" stop-opacity="1" />',
                '<stop offset=".9" stop-color="white" stop-opacity="0" /></linearGradient>',
                '<linearGradient id="grad-down" x1="0" x2="1" y1="0" y2="1"><stop offset="0.0" stop-color="white" stop-opacity="1" /><stop offset="0.9" stop-color="white" stop-opacity="0" /></linearGradient>',
                '<mask id="fade-up" maskContentUnits="objectBoundingBox"><rect width="1" height="1" fill="url(#grad-up)" /></mask>',
                '<mask id="fade-down" maskContentUnits="objectBoundingBox"><rect width="1" height="1" fill="url(#grad-down)" /></mask>',
                '<mask id="none" maskContentUnits="objectBoundingBox"><rect width="1" height="1" fill="white" /></mask>',
                '<linearGradient id="grad-symbol"><stop offset="0.7" stop-color="white" stop-opacity="1" /><stop offset=".95" stop-color="white" stop-opacity="0" /></linearGradient>',
                '<mask id="fade-symbol" maskContentUnits="userSpaceOnUse"><rect width="290px" height="200px" fill="url(#grad-symbol)" /></mask></defs>',
                '<g clip-path="url(#corners)">',
                '<rect fill="',
                params.color0,
                '" x="0px" y="0px" width="290px" height="500px" />',
                '<rect style="filter: url(#f1)" x="0px" y="0px" width="290px" height="500px" />',
                ' <g style="filter:url(#top-region-blur); transform:scale(1.5); transform-origin:center top;">',
                '<rect fill="none" x="0px" y="0px" width="290px" height="500px" />',
                '<ellipse cx="50%" cy="0px" rx="180px" ry="120px" fill="#000" opacity="0.85" /></g>',
                '<rect x="0" y="0" width="290" height="500" rx="42" ry="42" fill="rgba(0,0,0,0)" stroke="rgba(255,255,255,0.2)" /></g>'
            )
        );
}
```
:::

**概要**
SVGの`<defs>`要素内に含まれるフィルターや画像などの要素を生成する関数。

**詳細**
- `SVGParams`構造体に含まれる各パラメータを使用して、異なる要素を生成します。
- 生成された要素は、後のSVG要素で使用されるためのものです。

**引数**
- `SVGParams memory params`
	- SVGの要素を生成するためのパラメータが格納された構造体。

**戻り値**
- `svg`
	- 生成されたSVGの`<defs>`要素内の文字列。

---

### generateSVGBorderText

:::details generateSVGBorderText
```solidity
function generateSVGBorderText(
        string memory quoteToken,
        string memory baseToken,
        string memory quoteTokenSymbol,
        string memory baseTokenSymbol
    ) private pure returns (string memory svg) {
        svg = string(
            abi.encodePacked(
                '<text text-rendering="optimizeSpeed">',
                '<textPath startOffset="-100%" fill="white" font-family="\'Courier New\', monospace" font-size="10px" xlink:href="#text-path-a">',
                baseToken,
                unicode' • ',
                baseTokenSymbol,
                ' <animate additive="sum" attributeName="startOffset" from="0%" to="100%" begin="0s" dur="30s" repeatCount="indefinite" />',
                '</textPath> <textPath startOffset="0%" fill="white" font-family="\'Courier New\', monospace" font-size="10px" xlink:href="#text-path-a">',
                baseToken,
                unicode' • ',
                baseTokenSymbol,
                ' <animate additive="sum" attributeName="startOffset" from="0%" to="100%" begin="0s" dur="30s" repeatCount="indefinite" /> </textPath>',
                '<textPath startOffset="50%" fill="white" font-family="\'Courier New\', monospace" font-size="10px" xlink:href="#text-path-a">',
                quoteToken,
                unicode' • ',
                quoteTokenSymbol,
                ' <animate additive="sum" attributeName="startOffset" from="0%" to="100%" begin="0s" dur="30s"',
                ' repeatCount="indefinite" /></textPath><textPath startOffset="-50%" fill="white" font-family="\'Courier New\', monospace" font-size="10px" xlink:href="#text-path-a">',
                quoteToken,
                unicode' • ',
                quoteTokenSymbol,
                ' <animate additive="sum" attributeName="startOffset" from="0%" to="100%" begin="0s" dur="30s" repeatCount="indefinite" /></textPath></text>'
            )
        );
}
```
:::

**概要**
SVG内のテキスト要素を生成する関数。
特に通貨ペアやシンボルなどの情報を含んだテキスト要素を生成します。

**詳細**
- 引数として渡された通貨やシンボルに基づいて、SVG内に配置されるテキスト要素を生成します。
- テキスト要素はアニメーションを含んでおり、テキストが徐々に移動する効果があります。

**引数 or パラメータ**
- `quoteToken`
	- 通貨ペアの一方の通貨名。
- `baseToken`
	- 通貨ペアのもう一方の通貨名。
- `quoteTokenSymbol`
	- 通貨ペアの一方の通貨のシンボル。
- `baseTokenSymbol`
	- 通貨ペアのもう一方の通貨のシンボル。

**戻り値**
- `svg`
	- 生成されたSVG内のテキスト要素の文字列。

---

### generateSVGCardMantle

:::details generateSVGCardMantle
```solidity
function generateSVGCardMantle(
        string memory quoteTokenSymbol,
        string memory baseTokenSymbol,
        string memory feeTier
    ) private pure returns (string memory svg) {
        svg = string(
            abi.encodePacked(
                '<g mask="url(#fade-symbol)"><rect fill="none" x="0px" y="0px" width="290px" height="200px" /> <text y="70px" x="32px" fill="white" font-family="\'Courier New\', monospace" font-weight="200" font-size="36px">',
                quoteTokenSymbol,
                '/',
                baseTokenSymbol,
                '</text><text y="115px" x="32px" fill="white" font-family="\'Courier New\', monospace" font-weight="200" font-size="36px">',
                feeTier,
                '</text></g>',
                '<rect x="16" y="16" width="258" height="468" rx="26" ry="26" fill="rgba(0,0,0,0)" stroke="rgba(255,255,255,0.2)" />'
            )
        );
}
```
:::

**概要**
カードの上部に表示されるテキスト要素を生成する関数。
通貨ペアや手数料層などの情報が含まれたテキスト要素を生成します。

**詳細**
引数として渡された通貨や手数料層に基づいて、カード上部に配置されるテキスト要素を生成します。

**引数**
- `quoteTokenSymbol`
	- 通貨ペアの一方の通貨のシンボル。
- `baseTokenSymbol`
	- 通貨ペアのもう一方の通貨のシンボル。
- `feeTier`
	- 手数料層を示す文字列。

**戻り値**
- `svg`
	- 生成されたカード上部のテキスト要素の文字列。

---

### generageSvgCurve

:::details generageSvgCurve
```solidity
function generageSvgCurve(
        int24 tickLower,
        int24 tickUpper,
        int24 tickSpacing,
        int8 overRange
    ) private pure returns (string memory svg) {
        string memory fade = overRange == 1 ? '#fade-up' : overRange == -1 ? '#fade-down' : '#none';
        string memory curve = getCurve(tickLower, tickUpper, tickSpacing);
        svg = string(
            abi.encodePacked(
                '<g mask="url(',
                fade,
                ')"',
                ' style="transform:translate(72px,189px)">'
                '<rect x="-16px" y="-16px" width="180px" height="180px" fill="none" />'
                '<path d="',
                curve,
                '" stroke="rgba(0,0,0,0.3)" stroke-width="32px" fill="none" stroke-linecap="round" />',
                '</g><g mask="url(',
                fade,
                ')"',
                ' style="transform:translate(72px,189px)">',
                '<rect x="-16px" y="-16px" width="180px" height="180px" fill="none" />',
                '<path d="',
                curve,
                '" stroke="rgba(255,255,255,1)" fill="none" stroke-linecap="round" /></g>',
                generateSVGCurveCircle(overRange)
            )
        );
}
```
:::

**概要**
カーブの描画に関するSVG要素を生成する関数。
カーブの情報やオーバーレンジの情報に基づいて、カーブの描画に必要なSVG要素を生成します。

**詳細**
- 引数として渡されたカーブのパラメータに基づいて、カーブの描画に必要な要素を生成します。
- オーバーレンジの情報に応じて、適切なグラデーションや描画方法を選択します。

**引数**
- `tickLower`
	- カーブの下限を示す整数値。
- `tickUpper`
	- カーブの上限を示す整数値。
- `tickSpacing`
	- カーブの間隔を示す整数値。
- `overRange`
	- カーブがオーバーレンジかどうかを示す整数値。

**戻り値**
- `svg`
	- カーブの描画に関するSVG要素の文字列。

---

### getCurve

:::details getCurve
```solidity
function getCurve(
    int24 tickLower,
    int24 tickUpper,
    int24 tickSpacing
) internal pure returns (string memory curve) {
    int24 tickRange = (tickUpper - tickLower) / tickSpacing;
    if (tickRange <= 4) {
        curve = curve1;
    } else if (tickRange <= 8) {
        curve = curve2;
    } else if (tickRange <= 16) {
        curve = curve3;
    } else if (tickRange <= 32) {
        curve = curve4;
    } else if (tickRange <= 64) {
        curve = curve5;
    } else if (tickRange <= 128) {
        curve = curve6;
    } else if (tickRange <= 256) {
        curve = curve7;
    } else {
        curve = curve8;
    }
}
```
:::

**概要**
与えられた価格レンジと間隔に基づいて、適切な曲線を選択する関数。

**詳細**
価格レンジの下限(`tickLower`)、上限(`tickUpper`)、および間隔(`tickSpacing`)を受け取り、価格レンジの範囲に応じて適切な曲線を選択します。
価格レンジの幅は`tickUpper - tickLower`で計算され、その結果を`tickSpacing`で除算して`tickRange`を計算します。
`tickRange`に基づいて、適切な曲線を選択します。

**引数**
- `tickLower`
	- 価格レンジの下限を表す整数値。
- `tickUpper`
	- 価格レンジの上限を表す整数値。
- `tickSpacing`
	- 価格レンジの間隔を表す整数値。

**戻り値**
- `curve`
	- 選択された曲線の名称を表す文字列。

---

### generateSVGCurveCircle

:::details generateSVGCurveCircle
```solidity
function generateSVGCurveCircle(int8 overRange) internal pure returns (string memory svg) {
        string memory curvex1 = '73';
        string memory curvey1 = '190';
        string memory curvex2 = '217';
        string memory curvey2 = '334';
        if (overRange == 1 || overRange == -1) {
            svg = string(
                abi.encodePacked(
                    '<circle cx="',
                    overRange == -1 ? curvex1 : curvex2,
                    'px" cy="',
                    overRange == -1 ? curvey1 : curvey2,
                    'px" r="4px" fill="white" /><circle cx="',
                    overRange == -1 ? curvex1 : curvex2,
                    'px" cy="',
                    overRange == -1 ? curvey1 : curvey2,
                    'px" r="24px" fill="none" stroke="white" />'
                )
            );
        } else {
            svg = string(
                abi.encodePacked(
                    '<circle cx="',
                    curvex1,
                    'px" cy="',
                    curvey1,
                    'px" r="4px" fill="white" />',
                    '<circle cx="',
                    curvex2,
                    'px" cy="',
                    curvey2,
                    'px" r="4px" fill="white" />'
                )
            );
        }
}
```
:::

**概要**
価格レンジが範囲外であるかどうかを示すフラグに基づいて、円と輪郭を持つSVGを生成する関数。

**詳細**
価格レンジの範囲外(`overRange`)が`1`または`-1`の場合、白い円と輪郭を持つSVGを生成します。それ以外の場合、2つの白い円を持つSVGを生成します。

**引数**
- `overRange`
	- 価格レンジが範囲外であることを示すフラグ。

**戻り値**
- `svg`
	- 生成されたSVGを表す文字列。

---

### generateSVGPositionDataAndLocationCurve

:::details generateSVGPositionDataAndLocationCurve
```solidity
function generateSVGPositionDataAndLocationCurve(
        string memory tokenId,
        int24 tickLower,
        int24 tickUpper
    ) private pure returns (string memory svg) {
        string memory tickLowerStr = tickToString(tickLower);
        string memory tickUpperStr = tickToString(tickUpper);
        uint256 str1length = bytes(tokenId).length + 4;
        uint256 str2length = bytes(tickLowerStr).length + 10;
        uint256 str3length = bytes(tickUpperStr).length + 10;
        (string memory xCoord, string memory yCoord) = rangeLocation(tickLower, tickUpper);
        svg = string(
            abi.encodePacked(
                ' <g style="transform:translate(29px, 384px)">',
                '<rect width="',
                uint256(7 * (str1length + 4)).toString(),
                'px" height="26px" rx="8px" ry="8px" fill="rgba(0,0,0,0.6)" />',
                '<text x="12px" y="17px" font-family="\'Courier New\', monospace" font-size="12px" fill="white"><tspan fill="rgba(255,255,255,0.6)">ID: </tspan>',
                tokenId,
                '</text></g>',
                ' <g style="transform:translate(29px, 414px)">',
                '<rect width="',
                uint256(7 * (str2length + 4)).toString(),
                'px" height="26px" rx="8px" ry="8px" fill="rgba(0,0,0,0.6)" />',
                '<text x="12px" y="17px" font-family="\'Courier New\', monospace" font-size="12px" fill="white"><tspan fill="rgba(255,255,255,0.6)">Min Tick: </tspan>',
                tickLowerStr,
                '</text></g>',
                ' <g style="transform:translate(29px, 444px)">',
                '<rect width="',
                uint256(7 * (str3length + 4)).toString(),
                'px" height="26px" rx="8px" ry="8px" fill="rgba(0,0,0,0.6)" />',
                '<text x="12px" y="17px" font-family="\'Courier New\', monospace" font-size="12px" fill="white"><tspan fill="rgba(255,255,255,0.6)">Max Tick: </tspan>',
                tickUpperStr,
                '</text></g>'
                '<g style="transform:translate(226px, 433px)">',
                '<rect width="36px" height="36px" rx="8px" ry="8px" fill="none" stroke="rgba(255,255,255,0.2)" />',
                '<path stroke-linecap="round" d="M8 9C8.00004 22.9494 16.2099 28 27 28" fill="none" stroke="white" />',
                '<circle style="transform:translate3d(',
                xCoord,
                'px, ',
                yCoord,
                'px, 0px)" cx="0px" cy="0px" r="4px" fill="white"/></g>'
            )
        );
}
```
:::

**概要**
トークンIDと価格レンジに基づいて、トークンの位置データと位置カーブを持つSVGを生成する関数。

**詳細**
トークンID(`tokenId`)、価格レンジの下限(`tickLower`)、価格レンジの上限(`tickUpper`)を受け取り、これらの情報に基づいてトークンの位置データと位置カーブを持つSVGを生成します。
SVG内でテキストや図形の要素が配置されます。

**引数**
- `tokenId`
	- トークンのIDを表す文字列。
- `tickLower`
	- 価格レンジの下限を表す整数値。
- `tickUpper`
	- 価格レンジの上限を表す整数値。

**戻り値**
- `svg`
	- 生成されたSVGを表す文字列。

---

### tickToString

:::details tickToString
```solidity
function tickToString(int24 tick) private pure returns (string memory) {
        string memory sign = '';
        if (tick < 0) {
            tick = tick * -1;
            sign = '-';
        }
        return string(abi.encodePacked(sign, uint256(tick).toString()));
}
```
:::

**概要**
整数値を文字列に変換する際に符号と数値を組み合わせた関数。

**詳細**
与えられた整数値(`tick`)を文字列に変換します。
符号が負の場合、符号を付けて正の整数部分を表現します。
それ以外の場合、正の整数部分のみを表現します。

**引数**
- `tick`
	- 変換する整数値。

**戻り値**
変換された整数値を表す文字列。

---

### rangeLocation

:::details rangeLocation
```solidity
function rangeLocation(int24 tickLower, int24 tickUpper) internal pure returns (string memory, string memory) {
        int24 midPoint = (tickLower + tickUpper) / 2;
        if (midPoint < -100_000) {
            return ('8', '7');
        } else if (midPoint < -50_000) {
            return ('8', '10.5');
        } else if (midPoint < -10_000) {
            return ('8', '14.25');
        } else if (midPoint < -100) {
            return ('10', '18');
        } else if (midPoint < 0) {
            return ('11', '21');
        } else if (midPoint < 100) {
            return ('13', '23');
        } else if (midPoint < 10_000) {
            return ('15', '25');
        } else if (midPoint < 50_000) {
            return ('18', '26');
        } else if (midPoint < 100_000) {
            return ('21', '27');
        } else {
            return ('24', '27');
        }
}
```
:::

**概要**
価格レンジの中心に基づいて位置データの座標を計算する関数。

**詳細**
価格レンジの中心座標(`midPoint`)を計算し、その座標に基づいて位置データのX座標とY座標を決定します。
価格レンジの中心座標が一定の範囲内にある場合、特定の座標値を返します。

**引数**
- `tickLower`
	- 価格レンジの下限を表す整数値。
- `tickUpper`
	- 価格レンジの上限を表す整数値。

**戻り値**
- X座標を表す文字列。
- Y座標を表す文字列。

---

### generateSVGRareSparkle

:::details generateSVGRareSparkle
```solidity
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 = '';
        }
}
```
:::

**概要**
トークンIDとプールアドレスに基づいて、稀少なスパークルエフェクトを持つSVGを生成する関数。

**詳細**
この関数は、与えられたトークンID(`tokenId`)とプールアドレス(`poolAddress`)に基づいて、スパークルエフェクトを持つSVGを生成します。
トークンIDとプールアドレスに応じて、スパークルが表示されるかどうかが判定されます。

**引数**
- `tokenId`
	- トークンのIDを表す整数値。
- `poolAddress`
	- プールのアドレスを表すアドレス。

**戻り値**
- `svg`
	- 生成されたSVGを表す文字列。

:::message
スパークルエフェクトは、視覚的に魅力的な演出を作り出すための効果の一つです。
通常、光が反射してきらめくような見た目を持ち、物体や背景に一時的な輝きを与えることが特徴です。
スパークルエフェクトは、アニメーションやグラフィックスの中で使われ、ファンタジーや神秘的な要素を強調するのに一般的です。

例えば、ダイヤモンドが光を反射してキラキラと輝く様子や、星が空に点滅するようなイメージがスパークルエフェクトの一例です。
これらのエフェクトは、ビデオゲーム、アニメーション映画、ウェブデザイン、広告などで広く利用されており、視覚的な魅力やアピールを高めるための手段として活用されています。
:::

---

### isRare

:::details isRare
```solidity
function isRare(uint256 tokenId, address poolAddress) internal pure returns (bool) {
    // 関数のコード
}
```
:::

**概要**
トークンIDとプールアドレスに基づいて、トークンが稀少であるかどうかを判定する関数。

**詳細**
この関数は、与えられたトークンID(`tokenId`)とプールアドレス(`poolAddress`)に基づいて、トークンが稀少であるかどうかを判定します。
計算されたハッシュ値とトークンIDの最上位ビットに基づいて、トークンの稀少性が判定されます。

**引数**
- `tokenId`
	- トークンのIDを表す整数値。
- `poolAddress`
	- プールのアドレスを表すアドレス。

**戻り値**
- トークンが稀少である場合は`true`、それ以外の場合は`false`。

---

# イベント

### UpdateTokenRatioPriority

:::details UpdateTokenRatioPriority
```solidity
/// @notice Emitted when a token is given a new priority order in the displayed price ratio
/// @param token The token being given priority order
/// @param priority Represents priority in ratio - higher integers get numerator priority
event UpdateTokenRatioPriority(address token, int256 priority);
```
:::

**概要**
表示される価格比率においてトークンに新しい優先順位が設定された時に発行されるイベント。

**詳細**
特定のトークンが価格比率の中でどのような優先順位を持つかが変更された際に発生します。
優先順位が高い整数値ほど、分子の優先順位が高くなるような設計となっています。

**パラメータ**
- `token`
	- 優先順位が設定されたトークンのアドレス。
- `priority`
	- 優先順位を表す整数値。

---

# コード

## ライブラリ

- `/contracts/libraries/`

## インターフェース

- `/contracts/interfaces/`

# 最後に

今回の記事では、Bunzzの新機能『**DeCipher**』を使用して、**UniswapV3**の「**NonfungibleTokenPositionDescriptor**」のコントラクトを見てきました。
いかがだったでしょうか？
今後も特定のNFTやコントラクトをピックアップしてまとめて行きたいと思います。

普段はブログやQiitaでブロックチェーンやAIに関する記事を挙げているので、よければ見ていってください！

https://chaldene.net/

https://qiita.com/cardene
