0

I'm attempting to have 4 different contracts sharing the same data fields. The reasoning behind having 4 instead of 1, is that the different functionality can be broken down so that it is cleaner, and easier to understand. However, I wish to share particular storage between the contracts. The desired relationship can be seen below:

enter image description here

Thefactory contract will store the data required by each of the other 4 controller contracts Admin, user, Operator, and System, and spawns other contracts that will be interacted with by the controller contracts.

I have tried hierarchical inheritance:

import "./Admin.sol";
import "./User.sol";
import "./Operator.sol";
import "./System.sol";


contract Factory is Admin, User, Operator, System{

    uint public stage;
}

contract Admin {
    function adminDoesSomething () { if(stage == 1){ stage = 2} }
}

contract User {
    function userDoesSomething () { if(stage == 2){ stage = 3} }
}

contract Operator {
    function operatorDoesSomething () { if(stage == 3){ stage = 4} }
}

contract System {
    function systemDoesSomething () { if(stage == 4){ stage = 5} }
}

However, this results in Admin, User, Operator and System not having access to stage as it is isn't declared in their own contracts. Leading me to then try it in the opposite way:

import "./Factory.sol";    

contract Factory {

    uint public stage;
}

contract Admin is Factory {
    function adminDoesSomething () { if(stage == 1){ stage = 2} }
}

contract User is Factory {
    function userDoesSomething () { if(stage == 2){ stage = 3} }
}

contract Operator is Factory {
    function operatorDoesSomething () { if(stage == 3){ stage = 4} }
}

contract System is Factory {
    function systemDoesSomething () { if(stage == 4){ stage = 5} }
}

Now this results in multiple different instances of the factory contract, and that stage variable is not shared among the contracts.

So another potential solution is having an eternal storage contract. This is a good solution, apart from the total amount of calls being made, changing of data, grabbing data so the gas costs may be high.

Does anybody have any suggestions, a little bit stuck right now?

2

You are confusing a few concepts.

  • Inheritance and composition
  • Factory (I suspect this is a misuse of the factory idea. More to follow).
  • Eternal storage. The structure seems like it might have been inspired by a well-known pattern.

Let us turn our attention to the top of the graph, the ERC20 contract, the escrow contract and the dotted connectors, "Creates and Interacts". I think you know this but the recital will help with the rest of the diagram.

To "create", the Factory will need knowledge of the bytecode of each contract to create. The ERC20 and escrow will be data within the factory contract so it knows what to deploy on new Escrow().

To "interact", the Factory will need knowledge of the interfaces for each contract. This is considerably more lightweight than the full bytecode (The Factory could be smaller). We don't need code to spell out the interface in this case because the Factory will have the full code of each.

In any case, the ERC20 and escrow contracts are external to the factory. This is not inheritance. We wouldn't say contract Factory is ERC20 (because it isn't). We would instead instantiate an instance of the ERC20 interface inside Factory (ERC20 token = ERC20(<address>) and then talk to the functions (token.transferFrom(...)).

The lower portion of the graph with the solid connectors looks like a misuse of inheritance. Admin, User, Operator, System appear to need to "Uses Storage and Interacts with".

As with the upper section, "Interacts with" means they need to know the Factory address and they need to know the interfaces for the functions they want to invoke. "Uses Storage" is also interaction.

Consider this idea in Admin:

factory.setSomething(args, ...);

And in Factory:

function setSomething(type arg, ...) public onlyAdmin {...

Admin needs knowledge of the Factory interface (keyword "interface contracts") and that's all. Factory needs to know who it can trust (e.g. whitelist). Neither inherits from the other.

It's not obvious to me that this division is optimal. I'm unsure if there are expected to be n Admin, User, Operator, System contracts, or if the Factory is expected to deploy them. It may make sense, for example, to deploy contracts to deal with each class and attend to their own storage needs. Possibly what you are looking for includes a Hub contract with a sort of registry so each contract knows which callers to trust. Possibly what you are looking for is an upgrade path. IMO the current structure isn't obviously optimized for a self-evident use-case.

In any case, I would suggest thinking about deploying instances, contract interactions and access control and storage architecture as separate concerns.

You will find inheritance is still a useful tool, e.g. Factory is Ownable or Storage is KeyValueStore. Contracts talking to each other or deploying instances are both something else.

Hope it helps.

Your Answer

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