Suppose a contract C inherits an interface I. What is the difference between I c; and C c;?

For instance, in both cases C c = new C() and I c = new C()it seems c points to a newly deployed instance of C, no? (I'm looking here.)

2 Answers 2


Consider this:

contract C {
  // tonnes of code, lots of bytecode

interface I {
  // function signatures only ... small footprint

contract Dapp {

  C c; // Dapp must inherit C, i.e. Dapp size grows by size(C). 
  I c; // Dapp must inherit I, i.e. Dapp size grows by size(I), which is smaller. 

Since I and C are both elsewhere, they are instantiated with an address. Consequently, the internal workings of C are unreachable code inside Dapp because it's calling something else. The interface will suffice for that purpose. It just describes how to format the message, for this Type, and what to expect in response.

Unnecessarily large code leads to unnecessarily costly deployments. It doesn't take too much of that to hit the maximum bytecode size allowed by the protocol, so always use an interface if an interface will suffice.

As style suggestion, to ensure I is always a replica of C's interface, consider:

contract C is I {

That effectively sets up a commitment to implement all of the functions, exactly as defined in I. That plus the override keyword which C's functions must have if they are implementations the functions described in the interface enable the compilerto catch divergence between I and C in the development process, which is what we should want. It's a compiled-time sanity check that has no bearing on the final size of C.

In terms of a practical example, one could contract Token is ERC20 and inherit all the code from another implementation but if one was planning to build it from the ground up, then contract is IERC20 would at least ensure that there has been an attempt to implement the minimum required functions and there are no missing or extra parameters.

Hope it helps.


There is no difference.

All information about types in Solidity is used only during compilation and doesn't present in a resulting bytecode. Both C c and I c will be an addresses of a deployed C contract but specifying a type allows a compiler to check that functions your are calling in the contract do exist there.

Accordingly the only difference you can notice is that you receive compilation error if you try to call a function of a contract C that in not inherited from I on a variable of type I despite the function in fact exists (assuming the variable with type I is pointing to an instance of the contract C).

pragma solidity ^0.8.24;

import { IERC20 } from "@openzeppelin/contracts/interfaces/IERC20.sol";

interface I {
    function inheritedFunction() external pure;

contract C is I {
    function inheritedFunction() external pure override {}
    function notInheritedFunction() external pure {}

contract Example {
    function foo() external {
        C c = new C();
        I i = new C();

        c.inheritedFunction(); // Works
        c.notInheritedFunction(); // Works

        i.inheritedFunction(); // Works
        i.notInheritedFunction(); // Compilation error

        IERC20(address(c)).totalSupply(); // Runtime error

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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