-->

Initialize a big fixed length array in Solidity

2019-08-04 18:18发布

问题:

I'm building a game on ethereum as my first project and I'm facing with the storage and gas limits. I would like to store a storage smart contract on the blockchain to be queried after the deployment. I really need to initialize a fixed length array with constant values I insert manually. My situation is the following:

    contract A {

...some states variables/modifiers and events......

uint[] public vector = new uint[](162);

vector = [.......1, 2, 3,......];

function A () {

....some code....

ContractB contract = new ContractB(vector);

}

....functions....

}

This code doesn't deploy. Apparently I exceed gas limits on remix. I tried the following:

  • I split the vector in 10 different vectors and then pass just one of them to the constructor. With this the deploy works.

I really need to have just one single vector because it represents the edges set of a graph where ContractB is the data structure to build a graph. Vectors elements are ordered like this:

vector = [edge1From, edge1To, edge2From, edge2To,.......]

and I got 81 edges (162 entries in the vector).

I tought I can create a setData function that push the values in the vector one by one calling this function after the deployment but this is not my case because I need to have the vector filled before the call

ContractB contract = new ContractB(vector);

Now I can see I have two doubts:

1) Am I wrong trying to pass a vector as parameter in a function call inside the A constructor ?

2) I can see that I can create a double mapping for the edges. Something like

mapping (bool => mapping(uint => uint)) 

but then I will need multi-key valued mappings (more edges starting from the same point) and I will have the problem to initialize all the mappings at once like I do with the vector?

回答1:

Why does the contract need to be initialized at construction time?

This should work

pragma solidity ^0.4.2;

contract Graph {
    address owner;

    struct GraphEdge {
        uint128 from;
        uint128 to;
    }

    GraphEdge[] public graph;
    bool public initialized = false;

    constructor() public {
        owner = msg.sender;
    }

    function addEdge(uint128 edgeFrom, uint128 edgeTo) public {
        require(!initialized);
        graph.push(GraphEdge({
            from: edgeFrom,
            to: edgeTo
        }));
    }

    function finalize() public {
        require(msg.sender == owner);
        initialized = true;
    }
}

contract ContractB {
    Graph graph;

    constructor(address graphAddress) public {
        Graph _graph = Graph(graphAddress);
        require(_graph.initialized());
        graph = _graph;
    }
}


回答2:

If the range of values for you array are small enough, you can save on gas consumption by using a more appropriate size for your uints. Ethereum stores values into 32-bytes slots and you pay 20,000 gas for every slot used. If you are able to use a smaller sized uint (remember, uint is the same as uint256), you'll be able to save on gas usage.

For example, consider the following contract:

pragma solidity ^0.4.19;

contract Test {
    uint256[100] big;
    uint128[100] small;

    function addBig(uint8 index, uint256 num) public {
        big[index] = num;
    }

    function addSmall(uint8 index, uint128 num1, uint128 num2) public {
        small[index] = num1;
        small[index + 1] = num2;
    }
}

Calling addBig() each time with a previously unused index will have an execution cost of a little over 20,000 gas and results in one value being added to an array. Calling addSmall() each time will cost about 26,000, but you're adding 2 elements to the array. Both only use 1 slot of storage. You can get even better results if you can go smaller than uint128.

Another option (depending on if you need to manipulate the array data) is to store your vector off chain. You can use an oracle to retrieve data or store your data in IPFS.

If neither of those options work for your use case, then you'll have to change your data structure and/or use multiple transactions to initialize your array.