0

I am following this example to experiment with contract from within a contract (using testrpc).

contract Product {
    bytes32 public Name;

    function Product (bytes32 _Name) {
        Name = _Name;
    }
}

contract ProductFactory {

    bytes32[] public productNames;
    address[] public productAddresses;

    function addProduct (bytes32 _productName) {
        address newProduct = new Product(_productName);
        productAddresses.push(newProduct);
    }

    function getName (uint i) {
    Product prod = Product(productAddresses[i]);

    productNames[i] = prod.Name();
  }
}

My filename is Product.sol and the deloy file in truffle is:

var Product = artifacts.require("./Product.sol");
var ProductFactory = artifacts.require('ProductFactory');


module.exports = function(deployer) {
  // deployer.deploy(supplyChain);
  deployer.deploy(Product);
  deployer.deploy(ProductFactory);
};

On deploy, ProductFactory and Product both work well, as demonstrated by creating a new product with addProduct. However, on doing a getName, error: VM Exception is returned.

truffle(development)> ProductFactory.deployed().then(function(instance){PF=ProductFactory.at(instance.address)})

truffle(development)> PF.addProduct('abcd')
{ tx: '0xe304f9194fb9b06ae5f0a3664f64255e74d88de099cce0cd38092030b5d48d7f',
  receipt:
   { transactionHash: '0xe304f9194fb9b06ae5f0a3664f64255e74d88de099cce0cd38092030b5d48d7f',
     transactionIndex: 0,
     blockHash: '0x09d3e5547b1d4cd9ef40d05b5b15561a45b9f62f55410d03c175886dd1c591eb',
     blockNumber: 122,
     gasUsed: 122275,
     cumulativeGasUsed: 122275,
     contractAddress: null,
     logs: [] },
  logs: [] }

truffle(development)> PF.getName(0)
Error: VM Exception while processing transaction: invalid opcode
    at Object.InvalidResponse (/usr/local/lib/node_modules/truffle/build/cli.bundled.js:37047:16)
...

EDIT1: When I am trying the following,

truffle(development)> PF.productAddresses(0)
'0x4b01bd4691c306c59e8985bb6bd5bcc88abf1e81'
truffle(development)> Product.at('0x4b01bd4691c306c59e8985bb6bd5bcc88abf1e81').Name()
'0x6162686900000000000000000000000000000000000000000000000000000000'

understandably it works. PF.productAddresses(0) is an object as I gathered, and when I am using Product.at(PF.productAddresses(0)) it fails the same way as getName. However, on directly using the address string as just shown, it works. How to manage this issue?

EDIT1 over:

I am a newbie. If someone could let me know what might I am doing wrong. I think it is to do with async call, but I am not sure. I have tried multiple variations and on Remix as well, but it simply doesn't work.

Manganese
  • 226
  • 1
  • 7

1 Answers1

1

I don't think this is about having multiple contracts, you have an issue in the getName() function with this statement:

    productNames[i] = prod.Name();

You are not allowed to assign to an index beyond the end of a dynamically sized array. More specifically, when you first call getName(), the productNames array has zero size, so you can't assign to one of its elements: there aren't any.

You'd be better using productNames.push(_productName) within your addProduct() function and removing getName() entirely.

Alternatively, you could use a mapping instead of an array with i as your key. That is, change your declaration of productNames as follows; the rest of the code is unchanged:

mapping (uint => bytes32) productNames;

This works, but it's a weird way to code it (imho).

See also this Q&A.

benjaminion
  • 9,247
  • 1
  • 23
  • 36
  • Perfect, rather than removing getName altogether, I tried increasing array size by 1 (in addName) and checking whether it works - as you gave the reference for wherein there are two ways to increase array size, push, or increasing length. Both approach work! The mapping idea is interesting, must say, though I will go ahead with conventional ones. Thanks again! – Manganese Aug 20 '17 at 09:42