// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;
/* Inheritance tree
A
/ \
B C
\ /
D
*/
contract A {
// This is called an event. You can emit events from your function
// and they are logged into the transaction log.
// In our case, this will be useful for tracing function calls.
event Log(string message);
function foo() public virtual {
emit Log("A.foo called");
}
function bar() public virtual {
emit Log("A.bar called");
}
}
contract B is A {
function foo() public virtual override {
emit Log("B.foo called");
A.foo();
}
function bar() public virtual override {
emit Log("B.bar called");
super.bar();
}
}
contract C is A {
function foo() public virtual override {
emit Log("C.foo called");
A.foo();
}
function bar() public virtual override {
emit Log("C.bar called");
super.bar();
}
}
contract D is B, C {
// Try:
// - Call D.foo and check the transaction logs.
// Although D inherits A, B and C, it only called C and then A.
// - Call D.bar and check the transaction logs
// D called C, then B, and finally A.
// Although super was called twice (by B and C) it only called A once.
function foo() public override(B, C) {
super.foo();
}
function bar() public override(B, C) {
super.bar();
}
}
As far as I understand, calling D.bar() first executes C.bar (Immediate parent) which will then call A.bar (Contract Cs parent). Can anyone explain why is Contract B called here?
Best Answer
Please don't fall into the trick of thinking Solidity is an object-oriented language; it is not.
Hierarchy is just a way to tell the compiler where to put functions in the final smart contract. It has its own parsing logic, so if things get too weird, simply get rid of it at all.
Hierarchy capability was introduced to simplify the development. If it creates confusion, like in this case, it's better to avoid it.
When you compile the mentioned contract in fact, what happens is that all the code and functions are merged into a single contract.
While merging the code, if the compiler finds a function with the same signature (name + parameter's type), it overwrites the previous function, and only the last one is preserved.
The order is determined by the
is
keyword:contract D is B, C
means that the functions ofB
are merged before the functions ofC
.In this case
C.foo()
wins overB.foo()
, andB.foo()
does not actually exist in the final contract. This is why the flow isD.foo() -> C.foo() -> A.foo()
.On the contrary, if you specify the
super
keyword, the Solidity compiler understands that you want to retain the corresponding parent's functions in the final contract, and then it put those functions in the reverse order it reads itD.bar() -> C.bar() -> B.bar() -> A.bar()
Also, please note that