Solidity: Base constructor arguments given twice - how to properly solve this?

Created on 29 May 2018  路  12Comments  路  Source: ethereum/solidity

So I have a situation like this:

contract standard {
    constructor(string name)
    {
        //does something with the name 
    }
}

Then I have an extension:

contract extension1 is standard {
    constructor(string name)
     standard(name)
    {
        //sets some stuff
    }
}

and another extension:

contract extension2 is standard {
    constructor(string name)
     standard(name)
    {
        //sets some other stuff
    }
}

Then I want to use both extension in some context like:

contract implementation is extension1, extension2 {
    constructor(string name)
     extension1(name)
     extension2(name)
    {
        //sets some stuff
    }
}

This is where I get the warning: Warning: Base constructor arguments given twice.

Now I know why I get the warning but I don't know how to fix it while still keeping the modularity of the contracts. What is to proper way I am supposed to do this?

language design

Most helpful comment

@xpepermint @MoMannn The goal of the discussion here is to describe is to describe the new desired behavior, make a test case and get it into here -> Ping: https://github.com/ethereum/solidity/pull/3729

Disclosure, the three of us work together.


Current test case:

pragma experimental "v0.5.0"; 
pragma solidity ^0.4.23;

contract standard {
    constructor(string name) public
    {
    }
}

contract extension1 is standard {
    constructor(string name)
    standard(name)
    public
    {
    }
}

contract extension2 is standard {
    constructor(string name)
    standard(name)
    public
    {
    }
}

contract implementation is extension1, extension2 {
    constructor(string name)
    extension1(name)
    extension2(name)
    public
    {
    }
}

Error is:

26:1: DeclarationError: Base constructor arguments given twice.
contract implementation is extension1, extension2 {
^ (Relevant source part starts here and spans across multiple lines).

I agree with the compiler in this case. It is not reasonable for the compiler to evaluate that extension1 and extension2 both initialize Standard in the same way. Therefore it must complain that the two are competing.


One way to work around this is to have

  • standard
  • extension1
  • extension2
  • standardimplementation
  • extension1implementation
  • extension2implementation

In this case then the extension1/2 will NOT need to call the standard constructor because the extension1/2 are not deployable.

https://github.com/OpenZeppelin/openzeppelin-solidity/tree/master/contracts/token/ERC20

All 12 comments

Are you at liberty to share more information about your specific use-case? This problem is tricky because extension1 and extension2 could use two different strings and then the compiler does not know which one to use for standard.

@chriseth Lets just say for the sake of this that constructor just adds its EIP165 interface and there is no clash in changing the same variable.

Perhaps something like this works for you:

pragma experimental "v0.5.0";

contract A {
    constructor(string arg) public {}
}
contract B is A {
    constructor() public {}
}
contract C is A {
    constructor() public {}
}
contract Final is A, B, C {
    constructor() A("abc") public {

    }
}

The problem is that Final can also be B and C who (should) also call the A constructor.
@chriseth is this something that pragma experimental "v0.5.0"; would solve?

@xpepermint That's why I'm asking for actual real-world examples. Real-world examples are the only way we can come up with proper language design.

@xpepermint @MoMannn The goal of the discussion here is to describe is to describe the new desired behavior, make a test case and get it into here -> Ping: https://github.com/ethereum/solidity/pull/3729

Disclosure, the three of us work together.


Current test case:

pragma experimental "v0.5.0"; 
pragma solidity ^0.4.23;

contract standard {
    constructor(string name) public
    {
    }
}

contract extension1 is standard {
    constructor(string name)
    standard(name)
    public
    {
    }
}

contract extension2 is standard {
    constructor(string name)
    standard(name)
    public
    {
    }
}

contract implementation is extension1, extension2 {
    constructor(string name)
    extension1(name)
    extension2(name)
    public
    {
    }
}

Error is:

26:1: DeclarationError: Base constructor arguments given twice.
contract implementation is extension1, extension2 {
^ (Relevant source part starts here and spans across multiple lines).

I agree with the compiler in this case. It is not reasonable for the compiler to evaluate that extension1 and extension2 both initialize Standard in the same way. Therefore it must complain that the two are competing.


One way to work around this is to have

  • standard
  • extension1
  • extension2
  • standardimplementation
  • extension1implementation
  • extension2implementation

In this case then the extension1/2 will NOT need to call the standard constructor because the extension1/2 are not deployable.

https://github.com/OpenZeppelin/openzeppelin-solidity/tree/master/contracts/token/ERC20

I posted the same problem to reddit ethdev and didn't get a good solution either.

So what I decided to do is to delegate the responsibility of setting the name to the implementation. So that the base contract (standard) constructor doesn't have any input parameters.

This way everything works without warnings. The downside is that the setting of a variable is not enforced by code and any variable that need to get set cannot be private.

Just bumped on this problem as well. For now the best workaround is given in https://github.com/ethereum/solidity/issues/4199#issuecomment-393266346, with examples if you look at the ERC20 contracts.

But yeah it would be nice to have this properly solved. It would allow to to test extension1 and extension2 in Truffle without having 2 files for each of them (one "modular" version to be used in final contracts, one "implementation" version to be used in independent tests).

For the moment I decided to create a ./contracts/test/4199 directory in which I put modified versions of Extension1.sol and Extension2.sol. They have a contract name "Extension14199.sol" and "Extension24199". They both inherit from ../../ExtensionN.sol. They both have a constructor initializing Standard. In my Truffle tests, I just use artifacts.require('ExtensionN4199'). Then in final contracts I just require /.ExtensionN.sol' and have a constructor on Standard.

contract Final is Standard, Extension1, Extension2 {
    constructor(string _name) Standard(_name)
    ...
}

Here is the updated test case for latest Solidity

pragma solidity ^0.5.1;

contract standard {
    constructor(string memory name) public
    {
    }
}

contract extension1 is standard {
    constructor(string memory name)
    standard(name)
    public
    {
    }
}

contract extension2 is standard {
    constructor(string memory name)
    standard(name)
    public
    {
    }
}

contract implementation is extension1, extension2 {
    constructor(string memory name)
    extension1(name)
    extension2(name)
    public
    {
    }
}

Error:

browser/ballot_test.sol:25:1: DeclarationError: Base constructor arguments given twice.
contract implementation is extension1, extension2 {
^ (Relevant source part starts here and spans across multiple lines).
browser/ballot_test.sol:11:5: First constructor call is here:
standard(name)
^------------^
browser/ballot_test.sol:19:5: Second constructor call is here:
standard(name)
^------------^

Nobody has argued that the compiler's behavior is wrong. And no proposed improvement is on the table. For these reasons I recommend to close the issue and move this question to StackOverflow.

I might be totally wrong but I see that state variables follow C3, that functions follow C3, that both can be overwritten in C3 order, but that constructor cannot be written twice and overwritten following C3 order.

It's not a big deal and I agree that this could just be documented. But it might be improved too from the smart contracts developer point of view, just saying. I know nothing about the compiler and again it's not a big deal if this has little interest regarding the dev cost.

@guix77 I would really love a proper solution to this problem in the language, but currently I don't see any. The difference between constructors and state variables and functions with respect to C3 is that constructors have different signatures, so you cannot really call super(x), because the constructor one level up may take a different number of parameters.

Hi @chriseth and thanks for the precision ! I guess it's not a big deal at all and I would favour closing this issue, and maybe document it elsewhere (but this issue already documents it a lot).

What I ended up with is just use modular versions without constructors and I use those for inheritance in higher-order contracts, and tests versions in ./contracts/test, which have the constructor.

import '../Module.sol';
contract ModuleTest is Module {
  constructor {
     ...
  }
}

and in Truffle tests I use

const Module = artifacts.require('ModuleTest');

And that's it.

I can provide an example link for other people encountering this problem next week or the one after, when I publish the code on GitHub. I'd agree with @fulldecent that this one could be closed.

Was this page helpful?
0 / 5 - 0 ratings