Solidity Arrays – Storing a Dynamic Array of Structs Containing Dynamic Array of Structs

arrayssoliditystruct

I am trying to create the following objects:

Bounty[] public bounties;

struct Bounty {
    address issuer;
    uint256 fulfillmentAmount;
    uint256 balance;
    bool bountyOpen;
    Fulfillment[] fulfillments;
    mapping (bytes32 => bool) used;
}

struct Fulfillment {
    uint256 fulfillmentAmount;
    address fulfiller;
}

A brief explanation is that I want to have an array of "Bounties" which can be satisfied multiple times. Each bounty has a number of properties, one of which is an array of "Fulfillments". The fulfillments describe and store the different times the bounty was fulfilled.

When trying to create a new bounty, I use the following:

bounties.push(Bounty(msg.sender, _fulfillmentAmount, msg.value, true));

This gives me the error that I have one fewer parameters than needed to initialize the object, because I am missing the Fulfillment[] array. (I do not need the mapping to be initialized.)

When I try to initialize with the array object like so:

bounties.push(Bounty(msg.sender, _fulfillmentAmount, msg.value, true, new Fullfillment[](0)));

I get the following error:

UnimplementedFeatureError: Copying of type struct ContractName.Fulfillment memory[] memory to storage not yet supported.

I have looked at a number of similar questions: [1] [2] [3]

They seem to imply that creating an object like this is not supported.
Instead, I could turn Fullfillment[] fulfillments into:

uint fulfillmentLength;
mapping(uint => Fullfilment) fullfillments;

Keeping track of the length myself and doing some tricks to replace push().

Is this the best practice here? Or should I look at other alternatives like having the fulfillments be an object outside of the Bounty.

This is what Bounties-Network/StandardBounties seems to do:

https://github.com/Bounties-Network/StandardBounties/blob/master/contracts/StandardBounties.sol

Looking for best practice advice here.

Best Answer

It worked a lot better using a mapping to store the struct.

I used user-supplied keys for random access to the mapped structs and added a list of the keys in case iteration is important.

pragma solidity 0.4.24;

contract Bounties {

    // an unordered list of keys

    bytes32[] public bountyList;

    struct Bounty {
        bool isBounty; // simple collision check
        address issuer;
        uint256 fulfillmentAmount;
        uint256 balance;
        bool bountyOpen;
        Fulfillment[] fulfillments;
        mapping (bytes32 => bool) used;
    }

    struct Fulfillment {
        uint256 fulfillmentAmount;
        address fulfiller;
    }

    // a mapping to the Bounty structs
    mapping(bytes32 => Bounty) public bountyStructs;

    function isBounty(bytes32 key) public view returns(bool isIndeed) {
        return bountyStructs[key].isBounty;
    }

    function newBounty(bytes32 key, address _issuer, uint amount) public payable returns(bool success) {
        require(!isBounty(key)); // no duplicates
        bountyList.push(key);
        bountyStructs[key].isBounty = true;
        bountyStructs[key].issuer = _issuer;
        bountyStructs[key].fulfillmentAmount = amount;
        bountyStructs[key].balance = msg.value;
        bountyStructs[key].bountyOpen = true;
        // no need to initialize the mapping
        // should emit an event here
        return true;
    }

    function appendFulfillment(bytes32 bountyKey) public payable returns(bool success) {
        require(isBounty(bountyKey));
        Fulfillment memory f;
        f.fulfillmentAmount = msg.value;
        f.fulfiller = msg.sender;
        bountyStructs[bountyKey].fulfillments.push(f);
        // should emit an event here.
        return true;
    }

}

Hope it helps.

Related Topic