合成できるNFT「Marge」のContract（solidity）をのぞいてみる

## "Marge"とは？
pak氏というデジタルアーテイストが作成したNFTのプロジェクトです。
普通のNFTと違って面白い特徴は、**NFT同士が合体（結合=マージ）する**という点です！
そう結合すると言われてもイメージがしづらいですよね（笑）

具体的には、MargeのNFTを1つ所持していて2つ目を入手したときに、2つのNFTが結合するようにコントラクトが組まれています。そして、結合する度に表示される**白い点が大きくなっていきます**。
![opensea_marge.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2236744/7719613f-8f35-6ffb-331a-48111a786909.png)

NFTの点の大きさが、それぞれ若干異なるのがわかりますでしょうか?（笑）
タイトル「m(数字)」の数字が大きいほど、白い点が大きく、結合回数が多いということみたいですね。

## スマートコントラクト
ethscanのリンクは以下です。
https://etherscan.io/address/0xc3f8a0f5841abff777d3eefa5047e8d413a1c9ab#code

では、実際のコードを見ていきます。
### 結合
結合は以下の`function _marge`によって行われているようです。
結合と言っても、具体的にここで行っている処理は、2つのNFTの白い点の大きさの比較、結合後の大きさを決定するといった内容です。

    //Marge.sol
    function _merge(uint256 tokenIdRcvr, uint256 tokenIdSndr) internal returns (uint256 tokenIdDead) {
        require(tokenIdRcvr != tokenIdSndr, "Merge: Illegal argument identical tokenId.");

        uint256 massRcvr = decodeMass(_values[tokenIdRcvr]);
        uint256 massSndr = decodeMass(_values[tokenIdSndr]);
        
        uint256 massSmall = massRcvr;
        uint256 massLarge = massSndr;

        uint256 tokenIdSmall = tokenIdRcvr;
        uint256 tokenIdLarge = tokenIdSndr;

        if (massRcvr >= massSndr) {

            massSmall = massSndr;
            massLarge = massRcvr;

            tokenIdSmall = tokenIdSndr;
            tokenIdLarge = tokenIdRcvr;
        }

        _values[tokenIdLarge] += massSmall;

        uint256 combinedMass = massLarge + massSmall;

        if(combinedMass > _alphaMass) {
            _alphaId = tokenIdLarge;
            _alphaMass = combinedMass;
            emit AlphaMassUpdate(_alphaId, combinedMass);
        }
        
        _mergeCount[tokenIdLarge]++;

        delete _values[tokenIdSmall];

        _countToken -= 1;

        emit MassUpdate(tokenIdSmall, tokenIdLarge, combinedMass);

        return tokenIdSmall;
    }

### 転送
`function _marge`をどこで呼び出しているかしているかというと、NFTを転送する`function _transfer`のようです。
既にMargeを持っている人に転送されると、先ほどの`_marge`によって白い点が小さい方のNFTは`deadTokenId`はaddress(0)に転送、つまりバーンされるようです。

    //marge.sol
    function _transfer(address owner, address from, address to, uint256 tokenId) internal notFrozen {
        require(owner == from, "ERC721: transfer of token that is not own");
        require(to != address(0), "ERC721: transfer to the zero address");
        require(!_blacklistAddress[to], "Merge: transfer attempt to blacklist address");
    //---略---
                } else {
                    uint256 sentTokenId = tokenId;

                    // compute token merge, returning the dead token
                    uint256 deadTokenId = _merge(currentTokenId, sentTokenId);

                    // logically, the token has already been transferred to `to`
                    // so log the burning of the dead token id as originating ‘from’ `to`
                    emit Transfer(to, address(0), deadTokenId);

                    // thus inferring the alive token
                    uint256 aliveTokenId = currentTokenId;
                    if (currentTokenId == deadTokenId) {
                        aliveTokenId = sentTokenId;
                    }



### 白い点の大きさ
先ほどの`_marge`の中に以下のようなコードがあります。

        _values[tokenIdLarge] += massSmall;

`_values[]`の中に保管されているようですね。このコードのもっと前に

    // Mapping token ID to mass value.
    mapping (uint256 => uint256) private _values;

mappingで定義されていました。（やっとmappingの使い方がわかってきました汗）

## おわりに
NFTは単純なアートだけでなく、スマートコントラクトによってさまざまな機能を持たせることが出来て夢いっぱいですね。

Twitterを探索していたらフルオンチェーンやフェニックスNFTなるものまで、いっぱい面白いNFTのプロジェクトが見つかるので、今後も勉強がてら紹介していきたいと思います。
ご覧いただきありがとうございました。