2

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?

Xaler
  • 65
  • 2
  • 5
  • You might also be interested in https://ethereum.stackexchange.com/questions/23945/how-to-whitelist-up-to-50k-addresses-in-a-single-contract – carver Apr 18 '18 at 01:47
  • My idea was to create a contract GraphEditor where you push all the data and then pass the vector data to a function initializing a specific base model contract Graph with the informations about the order and the edges of the graph just in one call from GraphEditor. Then having the address of the deployed specific Graph than I can query it from other contracts. Starting from a GraphEditor will let every user create their own graph using the base contract Graph with the informations pushed in GraphEditor – Xaler Apr 19 '18 at 09:17

2 Answers2

0

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.

Adam Kipnis
  • 10,175
  • 10
  • 35
  • 48
0

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;
    }
}
gaiazov
  • 1,908
  • 14
  • 26
  • I commented my post answering to why I need it to be initialized at construction time. Also I have a question. If you deploy a graph contract in the contractB constructor passing the specific address of the specific graph you created, this produce a local copy of that specific instance retrieving all the data ? or it's just pointing to that contract without the need of recreating a copy to interact with ? – Xaler Apr 19 '18 at 09:19