LoginSignup
7
3

More than 3 years have passed since last update.

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

Last updated at Posted at 2021-02-01

こんにちはヤマピーブラックです。
ブロックチェーンゲーム会社のCryptoGamesdouble jump.tokyoでコントラクト開発をしています。

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

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

多重継承における実行順

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

無題のプレゼンテーション (2).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

当然ですがsuperをつけない場合は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が後勝ちとなる形です。
contract C is B, Aと逆に書くと、Aが実行されます。(割愛します)

overrideを逆順に

ここで、contract C is A, Bのままoverride(B,A)と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

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

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

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

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

無題のプレゼンテーション (3).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が先に評価されるのが特徴です。

一部superなし

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

無題のプレゼンテーション (4).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でSTOPします。

一部function削除

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

無題のプレゼンテーション (5).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はoverride(A,C)と記載しなければエラーとなります。

まとめ

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

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

7
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
3