[Ethereum] Passing an array as a parameter from javascrpt (web3) to a Solidity function

arrayssolidityweb3js

I have the following Solidity function:

function func(bytes32[2] data) external {
        bytes32 x;
        bytes32 y;

        x = data[0];
        y = data[1];
}

I'm trying to call it from Javascript (using web3), but passing an array a parameter doesn't work. I tried the following methods:

var arr = ["field1", "field2"];
contracts['MyContract'].contract.func(arr);

or:

contracts['MyContract'].contract.func(["field1", "field2"]);

or:

contracts['MyContract'].contract.func("field1", "field2");

None of those methods work.

Is there a way to pass an array as a parameter from Javascript to a Solidity function?

Best Answer

Here's what works for me using getData to populate a transaction data: field

The contract is an array getter setter:

pragma solidity ^0.4.0;

contract SetGetArray {

  uint[] someNumbers;

  function getArray() public constant returns (uint[]) {
    return someNumbers;
  }

  function setArray(uint[] setNumbers) public  {

    someNumbers = setNumbers;

  }

}

// After Deploying (in Kovan)
txHash:0x1cc74caab5c64c493a67cf8e3a8de656ba0db1d6f59c634e3c68e27a41afe7bf
// Successfully deployed Contract with address: 
// 0x1e1300614978efe2bf5c4b532daef69441314205

The gotcha is that if you are changing state your array needs to be in storage and modified with a transaction...

This is a verbose ( includes gas estimation) but hopefully clear way to set it in web3, I am using Node.js and Parity:

console.log('Setting up...');
const solc = require ('solc');
const Web3 = require ('web3');
console.log('Reading abi');
const contractABI = require("./SetGetArray.json");
console.log('Connecting');
const web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));
console.log('Creating contract instance');
var contract = web3.eth.contract(contractABI).at("0x1e1300614978efe2bf5c4b532daef69441314205");
var receiverAddress = '0x1e1300614978efe2bf5c4b532daef69441314205';

var setNumbers = [5,2,4,1];
var setData = contract.setArray.getData(setNumbers);

// console.log(setData);

var gasEstimate = web3.eth.estimateGas({
    from: web3.eth.coinbase,
    to: receiverAddress,
    data: setData
});

var gasPrice = web3.eth.gasPrice;

console.log('gas Price: ' + gasPrice);
console.log('Estimated Transaction gas: ' + gasEstimate);


console.log('unlocking Coinbase account');
const password = "yourPassword";
try {
  web3.personal.unlockAccount(web3.eth.coinbase, password);
} catch(e) {
  console.log(e);
  return;
}

console.log ('sending Transaction to the contract');

const transaction = {
  from: web3.eth.coinbase,
  to:receiverAddress,
  value: '0x00',
  gas: gasEstimate + 1,
  gasPrice: gasPrice + 1,
  data: setData
}


web3.eth.sendTransaction( transaction, function(err, txHash) {
  if (err != null) {
         console.error("Error while sending transaction: " + err);
       }
       else{
         console.log("Transaction Sent here's you  txHash: " + txHash);
       }
});

And getting the Array value:

console.log('Setting up...');
const solc = require ('solc');
const Web3 = require ('web3');
console.log('Reading abi');
const contractABI = require("./SetGetArray.json");
console.log('Connecting');
const web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));
console.log('Creating contract instance');
var contract = web3.eth.contract(contractABI).at("0x1e1300614978efe2bf5c4b532daef69441314205");

console.log ('calling contract');
var getArray = contract.getArray();
console.log('Get Array : ' + getArray);
// Get Array : 5,2,4,1
// getArray[2] = 4

You could also just call a contract's function with parameters, but the function needs to be pure, which is not enforced yet( i.e. your compiler might not compile the contract).

function passArray(uint[] otherNumbers) pure public returns (uint[]) {
    return otherNumbers;
}

and calling it :

  var otherNumbers = [4,3,2,1];
  var passArray = contract.passArray(otherNumbers);
  console.log('Pass Array : ' + passArray);
  // Pass Array : 4,3,2,1

The above works with compilers 0.4.17 and above (just update solc) , below you are back to calling it with a transaction that costs gas...

function passArray(uint[] otherNumbers) public returns (uint[]) {
    return otherNumbers;
}

Check both in remix the one on top doesn't incur in gas, while the one in the bottom does.