7

In OOP languages, composition over inheritance is a well-known best practice. Solidity is an OOP language too but there is also the gas efficiency issue.

Question is, in Solidity, how do composition and inheritance compare to each other considering respective gas costs?

P.S. Also asked in SO: https://stackoverflow.com/questions/52515714/composition-over-inheritance-in-solidity-gas-efficiency

  • I would also add the case of using library, which is a great way to save gas usages when you have multiple instances of the same contract code. – Miao ZhiCheng Oct 4 '18 at 20:11
7
+50

We have two approaches:

Inheritance

contract Parent {
    string w;

    function Set(string _w) public {
        w = _w;
    }

    function Greet(string name) internal view returns(string) {
        return string(abi.encodePacked(w, name));
    }
}

contract Child is Parent {
    function Get() public view returns(string) {
        return Greet("Bob");
    }
}

Composition

contract Greeting {
    string w;

    function Set(string _w) public {
        w = _w;
    }

    function Greet(string name) public view returns(string) {
        return string(abi.encodePacked(w, name));
    }
}

contract User {
    address greeting;

    constructor(address _greeting) public {
        greeting = _greeting;
    }

    function Set(string w) public {
        Greeting(greeting).Set(w);
    }

    function Get() public view returns(string) {
        return Greeting(greeting).Greet("Bob");
    }
}

Results:

Inheritance

  • Deployment: 264164 gas (Child)
  • Execution: 42966 gas (Child.Set("Hello"))

Composition

  • Deployment: 266416 gas (Greeting) + 309393 gas (User) = 575809 gas
  • Execution: 45412 gas (User.Set("Hello"))
  • 2
    Just doing the last step of math here, these numbers say that in this case, composition was ~118% more expensive to deploy and 5% more expensive to call. These specific numbers will vary between contracts and functions, but in a general case, deploying more contracts will always be more expensive than deploying fewer, and calls within contracts will always be cheaper than calls across them. – ohsully Oct 10 '18 at 2:26
4

From the perspective of gas usage, composition will be more expensive, mainly because you will have more CALL opcodes and more contracts instantiations. See Appendix G Fee Schedules (G_call, G_create, G_callvalue, ...) in the yellow paper -> https://ethereum.github.io/yellowpaper/paper.pdf

  • I don't see any numbers here. I need data to justify my decisions. – ferit Oct 9 '18 at 9:02
4

In a 1-1 comparison Composition is more expensive to deploy, and execute. That said, if you need to deploy many instances, you could use the Library pattern and use composition in that way.

Libraries can lower deployment costs if you're going to be deploying many copies of the same code. If you imagine deploying 100 copies of the same code, you may only have to deploy one Library, and then the more code you can push into the Library the cheaper each dependent contract deployment will be. But calling that library increases execution costs.

Another thing to consider with libraries: there are significant security concerns to shared libraries. The Parity hack which locked up funds was due to usage of composition.

Inheritance is a much simpler model and will always be cheaper to execute.

But, you will have to make the call as to what fits your use case. Will there be many versions of this contract? If so, will each instance be executed often enough to make up for deployment costs?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.