In Solidity, you specify base constructor arguments in two ways (according to https://docs.soliditylang.org/en/latest/contracts.html#arguments-for-base-constructors):
The first way to do it is more convenient if the constructor argument is a constant and defines the behaviour of the contract or describes it. The second way has to be used if the constructor arguments of the base depend on those of the derived contract. Arguments have to be given either in the inheritance list or in modifier-style in the derived constructor. Specifying arguments in both places is an error.
Your inheritance structure is as follows: D->C->B->A.
If we change the order a little bit (just to be sure that constructor executions are different than the code sequence) and add some logging, we get:
contract A {
constructor(uint paramA) public {
log0(bytes32(0x00000000));
}
}
contract C is A(0) {
constructor(string paramC) public {
log0(bytes32(0x00000002));
}
}
contract B is A{
constructor(address paramB) public {
log0(bytes32(0x00000001));
}
}
contract D is B, C {
constructor(uint paramForA, address paramForB, string paramForC) public C(paramForC) B(paramForB)// stuff here to call A, B and C constructors with non-static parameters
{
log0(bytes32(0x00000003));
}
}
You get the following execution of constructors: A->B->C->D
Note that if you change the order of inheritance and rewrite D as follows:
contract D is C, B {
constructor(uint paramForA, address paramForB, string paramForC) public C(paramForC) B(paramForB)// stuff here to call A, B and C constructors with non-static parameters
{
log0(bytes32(0x00000003));
}
}
You will get different order of execution A->C->B->D
So the order does matter.
EDIT1:
As the above was not exactly to the point, below I try to address the question:
"How should contract D specify its constructor arguments for contracts A B and C to provide all required parameters and in what order? Is there only one correct order?" directly.
You can assign parameters to parent contract constructors in two ways:
1. Directly in the declaration of inheritance:
contract D is B('0x0b'), C('0x0c') {
constructor(uint paramForA, address paramForB, string paramForC) public
{
}
}
where 0x0b and 0x0c are address literals.
2. Via assigning them using a syntax similar to Solidity modifiers if you want to use constructor D arguments:
contract D is B, C {
constructor(uint paramForA, address paramForB, string paramForC) public C(paramForC) B(paramForB) {}
}
There is no other way to initialize parent contract constructors that I'm aware of. If you would like to initialize state variables of a parent contract, you can make it within the D constructor. Those state variables has to be either public or internal in the parent contract. Hope this helps.