6

This is a very simplified example but it of course extends to more complicated contracts too, I just haven't found an elegant solution.

contract Group {
    struct Person {
        uint age;
        bytes32 name;
    }
    mapping (address => Person) members;
}

contract RockBand {
    struct Musician {
        uint age;
        bytes32 name;
        bytes32 instrument;
    }
    mapping (address => Musician) members;
}

contract FootballClub {
    struct Player {
        uint age;
        bytes32 name;
        bytes32 position;
    }
    mapping (address => Player) members;
}

Now it makes sense to me to make a 'library' of sorts out of Group, and inherit from it in the other two contracts. But I haven't found a way to modify a defined struct in a child contract. The best I can come up with is:

contract Group {
    struct Person {
        uint age;
        bytes32 name;
    }
    mapping (address => Person) members;
}

contract RockBand is Group{
    struct MusicianExtra {
        bytes32 instrument;
    }
    mapping (address => MusicianExtra) membersMusicianExtra;
}

contract FootballClub is Group{
    struct PlayerExtra {
        bytes32 position;
    }
    mapping (address => PlayerExtra) membersFootbalExtra;
}

This is the same as above functionality wise, but it requires an additional mapping for the extra information. Also it will become confusing and messy in bigger contracts, and it probably is less efficient if you need to look in two mappings instead of one to get the information.

I hope someone in the community has some insight for me.

Thanks! :)

1 Answer 1

7

I see where you're going with this and why the struggle. In summary, inheritance isn't going to work out with this approach. The fundamental thing is the structs and mappings (etc) forever define the layout of storage, so it's not something that can be extended by a subclass. A subclass instead needs to deal with storage as it is, not as it would wish it to be. I hope that makes sense.

There's more than one way to approach this and I imagine you probably have something more intricate than the example in mind. Here's a general idea.

Consider isolating all the common elements into People and then maintaining relationships with more specialised classes. Some people are band members, and band members are always people. I've had some success using separate data storage contracts on a 1:1 basis. That is, each entity (class) has a storage contract. Storage should in my opinion enforce referential integrity.

Some ideas and examples: https://medium.com/@robhitchens/enforcing-referential-integrity-in-ethereum-smart-contracts-a9ab1427ff42

Another approach is to use an abstract storage layout using key/value pairs. This is employed by Consensys' uPort project. The advantage of using key/value pairs is in extending document layouts with more fields as you go. Some experimental work here: https://bitbucket.org/rhitchens2/soliditystoragepatterns/src/ec5997449f4d99a2357c3b134f8794a790660a59/GeneralizedCollection.sol?at=master&fileviewer=file-view-default. The layout is basically: record key + field key refersTo value.

The two methods can be combined to form flexible document storage with enforced joins and "meat and potatoes" functions along the lines of "find related". generalizedCollection solves for upgrading schema-like design details in an upgradable contract scenario.

Hope it helps.

Your Answer

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

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