0

my function has only one input string description :

function createProposal(string description) {
        Proposal memory p;
        p.description = description;
        proposals.push(p);
    }

and according to ABI I shoud only pass one argument to the function when I call it in web3:

 {  
      "constant":false,
      "inputs":[  
         {  
            "name":"description",
            "type":"string"
         }
      ],
      "name":"createProposal",
      "outputs":[  

      ],
      "payable":false,
      "type":"function"
   }

But when I call it using web3 I get this error:

Error: Invalid number of arguments to Solidity function
    at Object.InvalidNumberOfSolidityArgs (inpage.js:1)
    at u.validateArgs (inpage.js:1)
    at u.toPayload (inpage.js:1)
    at u.call (inpage.js:1)
    at b.$scope.createProposal (controller.js:44)
    at fn (eval at compile (angular.min.js:1), <anonymous>:4:162)
    at e (angular.min.js:288)
    at b.$eval (angular.min.js:151)
    at b.$apply (angular.min.js:151)
    at HTMLButtonElement.<anonymous> (angular.min.js:288)
    at HTMLButtonElement.dispatch (jquery.min.js:2)
    at HTMLButtonElement.v.handle (jquery.min.js:2)

Here is the code I wrote:

$scope.contract.createProposal.call({description: $scope.createproposal},
            (err, response) => {console.log(response)})

How can I fix this?

  • 2
    To begin with, you should be using send and not call, because the function changes the blockchain. Second, you should pass the input argument as is and not embed it in a json object. Third, you should pass to to createProposal and not to call or send. Fourth, you should pass the callback function inside a then clause following the call or send. – goodvibration May 10 at 8:28
0

I guess you're using Web3JS 0.3+ if yes, we will get the result to the callback function, otherwise you will get the result in promise.

When you trying to change any contract storage, that will be treated as a transaction. createProposal function you're adding a new proposal to contract storage. That means its a transaction, any state change caller of the function has to do code sign the data (using a private key). Why because any state change can charge gas.

The call() is for getting data from the chain usually, you no need to pay gas for that.

Coming to your implementation for web3 0.3+,

With unlocking account:

contractObj.createProposal($scope.createproposal,{from:<YOUR_ADDRESS>,gas:"<GAS_UNITS>", gasPrice:"<GASPRICE_IN_WEI>"},(err, response) => {console.log(response)})

Without unlock account:

data = contractObj.createProposal.getData($scope.createproposal);
sendPublicRawTx(web3, data, {from:"<FROM>", privateKey:"PRIVATE_KEY"}).then(txHash=>{
//TODO your logic
}).catch(e=>{
//TODO your logic.
});

function sendPublicRawTx(connection, txData, options) {
    return new Promise((resolve, reject) => {
        if (txData && !txData.toLowerCase().startsWith('0x')) {
            txData = '0x' + txData;
        }
        if (!options.from) {
            reject("Invalid from address");
        }
        options.from = options.from.toLocaleLowerCase()
        if (!options.from.startsWith("0x")) {
            options.from = "0x" + options.from;
        }
        let transactionObj = {
            nonce: options.nonce,
            from: options.from,
            data: txData,
            gasPrice: 0,
            to: options.to,
        }
        connection.eth.estimateGas(transactionObj, function (error, gas) {
            if (error) {
                reject(error);
                return;
            }
            gas = parseInt(gas * 1.3);
            transactionObj.gas = gas;
            const privateKey = Buffer.from(options.privateKey, 'hex');
            const tx = new Tx(transactionObj);
            tx.sign(privateKey);
            const serializedTx = tx.serialize();
            connection.eth.sendRawTransaction('0x' + serializedTx.toString('hex'), function (error, data) {
                if (error) {
                    reject(error);
                    return
                }
                resolve(data)
            })
        })
    })
}

For unlocking account:

web3.personal.unlockAccount("<ADDRESS>","<PASSWORD>");
  • when I try to unlock the account I get this error: MetaMask - RPC Error: The method personal_unlockAccount does not exist/is not available – Yashar May 10 at 14:39

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.