0

I am trying to create a canvas with pixels where each pixel work as a separate contract since they're supposed to store values. But once I've cleared all errors and try to deploy the code it complains that my loops takes up too much gas costs.

How can I create let's say 10000 instances of an object where each object can store individual values without making the gas cost insanely high?

pragma solidity ^0.4.19;    

import "./ERC20.sol"; 
import "./SafeMath.sol";

contract Pixel is IERC20() {

    uint256 public Id;

    //Color default to white
    string public Colorhex = "#ffffff";

    //Inital price
    uint256 public StartPrice = 5;

    //Times pixel has been purchased
    uint256 public TimesPurchased = 0;

    function Pixel(uint256 id, string colorhex, uint256 startprice, uint256 timespurchased) public {
        Id = id;
        Colorhex = colorhex;
        StartPrice = startprice;
        TimesPurchased = timespurchased;
    }

    using SafeMath for uint256;

    //Get the Cost
    function getCost(uint _startprice, uint256 _times) public pure returns(uint cost) {
        return _startprice*(_times+1);
    }
    //The current price of pixel
    uint256 public cost = getCost(StartPrice,TimesPurchased);

    //The current pixel owner
    address public pixelowner;

    //Safely increments counter
    function addpixelpurchasecounter() public view {
       TimesPurchased.add(1);
    }

    //Returns hex color
    function setcolorhex(string _hex) public {
        Colorhex = _hex;
    }
}

contract Canvas {

    //Address to which the funds will be sent
    address public owner;

    mapping(address => uint256) balances;
    mapping(address => mapping(address =>uint256)) allowed;

    using SafeMath for uint256;

    address[] newContracts;

    //Use these somehow?
    //This checks whether Ether has been paid to the address
    function () private payable {
        UpdateColor();
    }
    //Automatically send tokens.
    function UpdateColor() public payable {
        require(msg.value > 0);
        owner.transfer(msg.value);
    }

     function balanceOf(address _owner) public constant returns (uint256 balance) {
        return balances[_owner];
    }

    function approve(address _spender, uint256 _value) public returns (bool success) {
        allowed[msg.sender][_spender] = _value;
        Approval(msg.sender, _spender, _value);
        return true;
    }

    //Spend from sender funds.
    //This recieves pixel.Cost at index i.
    function transfer(address _to, uint256 _value) public returns (bool success) {
        require(balances[msg.sender] >= _value && _value > 0);
        balances[msg.sender].sub(_value);
        balances[_to].add(_value);
        Transfer(msg.sender, _to, _value);
        return true;
    }

    function getTransfer(address _to, uint256 _value, uint256 _id, string _hex) public returns (bool success) {
        require(transfer(_to,_value)); 
            setColor(_id,_hex);
            increaseCost(_id);
            return true;
    }

    function increaseCost(uint256 i) public view {
        getContract(i).addpixelpurchasecounter();
    }

    function allowance(address _owner, address _spender) public constant returns (uint256 remaining) {
         return allowed[_owner][_spender];
    }

    uint256 constant PIXELAMOUNT = 10000;

    //Add one empty pixel contract
    function AddEmptyPixel(uint256 id) private{
                address newContract =  Pixel(id);
                //Add contract to list of contracts
                newContracts.push(newContract);
    }

    //Populate pixel contract with PIXELAMOUNT
    function AddEmptyPixels(uint256 amount) private {
        for (uint i = 0; i < amount; i++) {
            AddEmptyPixels(i);
        }
    }

    //Set Color to hex at index i
    function setColor(uint i, string _hex) public {
        getContract(i).setcolorhex(_hex);
    }

    //Get cost of pixel at index i
    //This should be called from Front once the pixel is hovered and Metamask transaction started.
    function getCost(uint i) public view returns (uint256 cost) {
       return getContract(i).cost();
    }

    function getContract(uint i) public view returns (Pixel chosenPixel) {
        return Pixel(newContracts[i]);
    }

    //Add initial empty pixels on contract start.
    function Canvas() public {
        AddEmptyPixels(PIXELAMOUNT);
    }

    event Transfer(address indexed _from, address indexed _to, uint256 _value);
    event Approval(address indexed _owner, address indexed _spender, uint256 _value);
}

Edit: This is the error message I receive.

Fallback function of contract Canvas requires too much gas (infinite). If the fallback function requires more than 2300 gas, the contract cannot receive Ether.

  • Why does Pixel have to be a contract? You could use struct for this within Canvas. Instantiating a contract is costly. structs are cheaper. You could also do lazy init on demand (when used for the first time). – ivicaa Feb 20 '18 at 20:05
  • @JacobEriksson The message is a warning, it is an issue if you intend to call fallback function from another contract. If the contract fallback is called by a user they can set a higher gas limit than the 2300 gas stipend. – Ismael Feb 21 '18 at 20:46
1

I would highly suggest not creating separate contracts for each pixel. Contract creation costs at a minimum 53k gas, plus whatever state initialization/bytecode it takes. You should store pixels as structs in a 2 dimensional array.

0

There is seems to be a bug in the following code. You should replace AddEmptyPixels with AddEmptyPixel. Notice the extra s in your method name. Because of this bug, your code is recursively calling AddEmptyPixels and consuming a lot of gas.

function AddEmptyPixels(uint256 amount) private {
    for (uint i = 0; i < amount; i++) {
        AddEmptyPixels(i);
    }
}

`

  • Thanks for you response! The bug seemed to fix itself but I am still limited to generate a max pixel count of 100 since the gas cost otherwise goes beyond the block limit. Do you see any way to optimize the code in this regard? – Jacob Eriksson Feb 20 '18 at 19:48

Your Answer

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

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