Solidityの多重継承やダイヤモンド継承を攻略する（Solidity0.8.0対応）

こんにちは<a href="https://twitter.com/yamapyblack">ヤマピーブラック</a>です。
ブロックチェーンゲーム会社の<a href="http://cryptogames.co.jp/">CryptoGames</a>と<a href="https://www.doublejump.tokyo/" target="_blank">double jump.tokyo</a>でコントラクト開発をしています。

今回はSolidityにおける多重継承、ダイヤモンド継承の書き方、挙動について解説します。

特にSolidity0.6.0以降、継承したfunctionにはoverrideを記載しなければならず、書き方もひとクセあります。そのあたりも含めて解説します。

# 多重継承における実行順

以下のような構成を考えます。

![無題のプレゼンテーション (2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117960/d500ed5e-61b2-c9bf-f895-040bda5581cd.png)

※console.logはhardhatを使用

```
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
import "hardhat/console.sol";

contract A {
    function foo() virtual public {
        console.log("foo A");
    }
}

contract B {
    function foo() virtual public {
        console.log("foo B");
    }
}

contract C is A, B {
    function foo() override(A,B) public {
        console.log("foo C");
    }
}
```

solidity0.6.0から継承元を列挙、override(A,B)と記載する必要があります。

実行結果

```
foo C
```

当然ですが<b>super</b>をつけない場合はCが実行されて終わりです。

### superあり

superつけた場合はどうなるでしょう。

```
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
import "hardhat/console.sol";

contract A {
    function foo() virtual public {
        console.log("foo A");
    }
}

contract B {
    function foo() virtual public {
        console.log("foo B");
    }
}

contract C is A, B {
    function foo() override(A,B) public {
        console.log("foo C");
        super.foo();
    }
}
```

実行結果

```
foo C
foo B
```

contract C is A, Bの後に書いたほう、<b>Bが後勝ち</b>となる形です。
contract C is B, Aと逆に書くと、Aが実行されます。（割愛します）

### overrideを逆順に

ここで、contract C is A, Bのまま<b>override(B,A)</b>とoverrideのところだけ逆に記載するとどうなるでしょうか。

```
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
import "hardhat/console.sol";

contract A {
    function foo() virtual public {
        console.log("foo A");
    }
}

contract B {
    function foo() virtual public {
        console.log("foo B");
    }
}

contract C is A, B {
    function foo() override(B,A) public {
        console.log("foo C");
        super.foo();
    }
}
```

実行結果

```
foo C
foo B
```

結果は変わりませんでした。つまり、<b>contract C is A,Bの順番が重要で、overrideに記載する順番はどうでもよい</b>ということです。

まあ、functionの順番だけ器用に変更するのは無理でしょうかね。訳わかんなくなりそうですし。

# ダイヤモンド継承における実行順

続いてダイヤモンド継承を考えます。B,C,Dにsuperをつけました。

![無題のプレゼンテーション (3).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117960/d1fdd096-f407-7dca-64ca-36b7a3b512db.png)

```
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
import "hardhat/console.sol";

contract A {
    function foo() virtual public {
        console.log("foo A");
    }
}

contract B is A {
    function foo() override virtual public {
        console.log("foo B");
        super.foo();
    }
}

contract C is A {
    function foo() override virtual public {
        console.log("foo C");
        super.foo();
    }
}

contract D is B,C {
    function foo() override(B,C) public {
        console.log("foo D");
        super.foo();
    }
}
```

実行結果

```
foo D
foo C
foo B
foo A
```

D→C→B→A。このような順番で実行されます。Cの親はAですが、<b>兄弟クラスであるBが先に評価</b>されるのが特徴です。

### 一部superなし

続きまして、Bのsuperを削除します。

![無題のプレゼンテーション (4).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117960/b28c1cd7-cd1b-9404-62a1-627ab3c31c53.png)

```
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
import "hardhat/console.sol";

contract A {
    function foo() virtual public {
        console.log("foo A");
    }
}

contract B is A {
    function foo() override virtual public {
        console.log("foo B");
    }
}

contract C is A {
    function foo() override virtual public {
        console.log("foo C");
        super.foo();
    }
}

contract D is B,C {
    function foo() override(B,C) public {
        console.log("foo D");
        super.foo();
    }
}
```

実行結果

```
foo D
foo C
foo B
```

D→C→Bといって<b>BでSTOP</b>します。

### 一部function削除

さらに、Bのfoo()自体を削除してみるとどうなるでしょう。

![無題のプレゼンテーション (5).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117960/d31aaeca-d095-dbd3-b122-25133378e014.png)

```
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
import "hardhat/console.sol";

contract A {
    function foo() virtual public {
        console.log("foo A");
    }
}

contract B is A {
}

contract C is A {
    function foo() override virtual public {
        console.log("foo C");
        super.foo();
    }
}

contract D is B,C {
    function foo() override(A,C) public {
        console.log("foo D");
        super.foo();
    }
}
```

実行結果

```
foo D
foo C
foo A
```

今度はAがちゃんと実行されます。また、Dは<b>override(A,C)</b>と記載しなければエラーとなります。

# まとめ

・contract is A,B... 後から書いたものから実行される
・親より兄弟が先に実行される
・superがないとそこでSTOP
・兄弟のfunctionがない場合は親にいく

以上で一応理解はできましたが、hardhatでconsoleを出しながらやるのが一番かもしれませんね。
constructorの実行についてもいずれ書きたいと思います。