6

I have a smart contract with some members which should get modified on certain events. For simplicity, let's imagine we have a greeter contract.

If I call greeter.greet() in the JavaScript console, it responds with something like Hello World. But what if I want to count the number of greetings and store them inside the contract?

My first try was the following:

contract greeter
{
  string greeting;
  uint calls;

  function greeter ( string _greeting ) public
  {
    greeting = _greeting;
    calls = 0;
  }

  function greet ( ) constant returns ( string )
  {
    calls = calls + 1; // does not work
    return greeting;
  }
}

The calls variable was not updated. My first guess is that I need to somehow create a transaction to pay for the gas. I used web3.js to deploy the contract. Should I use web3 also for calling greet() with a transaction?

Or could I fund the contract account with some Ether to enable the contract to pay its own gas? How to manipulate data in a Solidity smart contract?

  • I have not looked at web3.js yet but I am curious to learn how you, as you wrote, deployed your contract with it. Could you please point me at further details? – hcvst Jan 28 '16 at 22:35
  • @hcvst you can follow my baby steps deploying with web3js on github. – Waqar Lim Jan 28 '16 at 22:37
9

Because your greet() function is marked constant, web3 by default simulates the call and returns the value, but no transaction is sent, and thus no state changes are retained. This behavior can be forced in non-constant methods using contract.method.call().

To call the function in a way that does send a transaction and update the chain, use greeter.greet.sendTransaction({from:eth.coinbase, gas:100000}).

Unfortunately, while other contracts will recieve the proper return value, web3 will return a transaction hash, as opposed to a return value. In this case, it makes sense to use Events.

For example, the solidity would look something like:

contract greeter { 
    string greeting; 
    uint calls; 
    event Greet (string greeting);
    function greeter ( string _greeting ) public { 
        greeting = _greeting; 
        calls = 0; 
     }
     function greet ( ) public returns ( string ) { 
         calls = calls + 1; 
         Greet(greeting);
          return greeting; 
     } 
 }

And to get a return value:

 greeter.greet.sendTransaction(
    {from:eth.coinbase, gas:100000},

    function (error, result){ 
    var event = greeter.Greet()
    event.watch(
        function(error, result){ 
             if (!error) console.log(result.args.greeting); 
         }
    );
)
  • 1
    Upvoted, good point about the return value; edited my answer to refer to yours for web3js and events, and briefly mentioned the contract-to-contract case. – eth Jan 28 '16 at 22:27
  • I think you want to remove the "constant" in greet()? Also, "result.greeting" might not be precise. IIRC may need something like "result.args.greeting". – eth Jan 28 '16 at 22:33
  • 1
    @eth Thanks, I totally forgot about the constant, and the result.args.greeting is correct. – Tjaden Hess Jan 28 '16 at 22:58
7

The problem is that greet is a constant function so no state changes are allowed. Changing the value of something in storage, would be a state change.

Solution: remove constant.

Yes, you should use web3 sendTransaction when you want a state change. sendTransaction is the default for non-constant functions, but it wasn't used because constant was specified. Note that the return value will only be accessible from another contract -- with web3js transactions, Events will need to be used to get the return value as Tjaden Hess answered.

More from the FAQ:

What is the difference between a function marked constant and one that is not?

constant functions can perform some action and return a value, but cannot change state (this is not yet enforced by the compiler). In other words, a constant function cannot save or update any variables within the contract or wider blockchain. These functions are called using c.someFunction(...) from geth or any other web3.js environment.

“non-constant” functions (those lacking the constant specifier) must be called with c.someMethod.sendTransaction({from:eth.accounts[x], gas: 1000000}); That is, because they can change state, they have to have a gas payment sent along to get the work done.


To answer the remaining question, a contract cannot pay for its own gas, in the way that might be imagined with this example, so funding it with Ether doesn't help.

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.