2

From reading I have surmised that it is impossible to get the return value of state changing calls. Consider this example.sol:

contract F {
  function createChildContract() public returns (address child) {
    child = address(new C());
  }
}

contract C { }

If I instantiate F, then call F.createChildContract() this will return the transaction receipt. This is reasonable, because the transaction will not have been mined yet. But is there not a way to wait on the transaction, then once it is mined get that child address that it returns?

Everywhere I look, I see people telling me it can't be done, and I should use Events: How do I get the return value of a solidity function with web3? How to get return values when function with argument is called? https://github.com/ethereum/web3.js/issues/1207

Fine, I'll use events - but I would like to understand why this not possible? It seems like something that should be very much possible.

EDIT: Clearly I'm not asking what the difference between a transaction and a call is, because I make it clear that it is a call, specifically a state-changing call. The second dup - I can't even.

The Burger King
  • 350
  • 3
  • 13
  • But is there not a way to wait on the transaction, then once it is mined get that child address that it returns? - The "convention" is to wait at least 12 blocks (12*16 seconds on mainnet) after it is mined, which guarantees in a reasonably high probability that the transaction is there for good. – goodvibration Feb 07 '19 at 10:16
  • why this not possible? - as you said - the transaction will not have been mined yet (so you've technically answered yourself). – goodvibration Feb 07 '19 at 10:17
  • @goodvibration crazy idea, read what I wrote. – The Burger King Feb 07 '19 at 10:20
  • I did, and I answered both your questions. If you're not satisfied with the answers, then I suggest that you explain why instead of responding sarcastically (!), and I might be able to update them accordingly. – goodvibration Feb 07 '19 at 10:26
  • Perhaps you missed this: "But is there not a way to wait on the transaction, then once it is mined get that child address that it returns?" – The Burger King Feb 07 '19 at 10:36
  • That's the opening statement in my first comment (so perhaps you're the one who missed it). In any case, from the off-chain, it should be done asynchronously (with a callback or other (for example, await, if you're using a JS client)). But I don't quite understand your claim about there is no technical reason afaik as to why this would not be implemented - implemented where? Clearly it can be done from the off-chain pretty easily, so the infrastructure for that is already well implemented. – goodvibration Feb 07 '19 at 10:38
  • This could be done easily with a callback. – The Burger King Feb 07 '19 at 10:40
  • So what's your point about "there is no technical reason afaik as to why this would not be implemented"? It IS implemented in every standard web3 client available (for example, web3.js). – goodvibration Feb 07 '19 at 10:41
  • omg, that is my question. So if you know how to do it, why don't you answer with the code? – The Burger King Feb 07 '19 at 10:43
  • Because that's how the authors of web3.js chose to implement it. The asynchronous call awaits for the receipt, and then receives 24 additional confirmation events. You can wait for them as well (i.e., not just for the receipt), which allows to you be sure that the transaction is in the block-chain for permanent. – goodvibration Feb 07 '19 at 10:45
  • Why don't I answer it with code??? Perhaps because you haven't posted any (off-chain) code of your own, so I cannot refer to what you've done and how you can improve or fix it! – goodvibration Feb 07 '19 at 10:46
  • In any case, here is a coding example from the official documentation. Check the one under using the event emitter It allows you to wait for the hash, then for the receipt, then for each one of 24 confirmation events. – goodvibration Feb 07 '19 at 10:52
  • The hash is typically returned pretty much immediately, the receipt is returned when the transaction is mined, and each confirmation event is returned when an entire block is mined (the more confirmations you wait for, the more confident you are about the validity of the transaction in the block-chain). Please note that the link above is for web3.js v1.x (in case you're using web3.js v0.x). – goodvibration Feb 07 '19 at 10:52
  • Not a duplicate of either. – The Burger King Feb 07 '19 at 18:23
  • "state-changing call" is basically an oxymoron because a call cannot change a state full-stop. – Greg Jeanmart Feb 07 '19 at 18:48
  • a method call on the contract. obvious really. – The Burger King Feb 07 '19 at 18:54
  • Leave it up - who knows, maybe a smart person will wander by ?! – The Burger King Feb 07 '19 at 19:09

1 Answers1

4

impossible to get the return value of state changing calls.

If it is state-changing, it is not a call. It's a transaction.

Consider this example.sol:

It doesn't compile.

If I instantiate F

You can't do that, because it doesn't compile.

then call F.createChildContract()

We have to make some assumptions about what you want to do so we're talking about something that works.

You could create a new C and return the address:

address c = new Child();
return c;

Or, possibly return the address of child if it is a C that was properly instantiated previously/elsewhere, e.g. C child = C(addressC);

return address(child);

If you call the function, you will indeed receive an address. There just won't be a contract there because it was a read-only activity. If you send it a transaction, you will get a receipt.

But is there not a way to wait on the transaction, then once it is mined get that child address that it returns?

Yes. There are several ways to do that. It is a client-side responsibility. The blockchain is concerned with processing or rejecting the transaction. It is not concerned with client-side interpretation.

Everywhere I look, I see people telling me it can't be done, and I should use Events

That is one of the ways a client can be informed about an on-chain state transition. You can also use the receipt and poll the blockchain until you see the transaction is mined. In either case and at every stage, you have the option of inspecting the contract state.

For emphasis, it's a client-side responsibility to decide how many confirmations are acceptable, etc. This library may help: https://gist.github.com/xavierlepretre/88682e871f4ad07be4534ae560692ee6

A little more explanation: Optimal way of waiting for transactions to be mined in geth

So, basically:

return myContract.doSomething();})
.then(function(txnHash) {return getTransactionReceiptMined(txnHash);})
.then(function(receipt) { //... it's mined once

That can be improved for readability with async/await.

I would like to understand why this not possible

There is a logical issue with relieving clients of their responsibility. Let me show you a conundrum.

Suppose we have a simple contract:

pragma solidity 0.5.1;

contract OrderMatters {

// the children's game of tag

address it; 

constructor() public {
    it = msg.sender;
}

function tagYoureIt(address _newIt) public returns(address newIt) {
    require(msg.sender == it); // only "it" can tag someone else
    it = _newIt;
    return it;
}

}

Further, suppose we have three transactions signed and sent simultaneously:

  • A - Alice deploys the contract
  • B - Alice tags Bob
  • C - Bob tags Carol

Mining disambiguates the order.

Suppose it goes, A, B, C. B returns Bob and C returns Carol.

But wait! Suppose there is chain reorganization and the canonical chain reverses the order, so now it goes, C, B, A. C, and B are invalid because there is no contract. They return an error.

What if the order is shuffled again? Say, A, C, B? C returns an error cause Carol isn't "it". B returns Bob.

There will eventually be a stable canonical transaction order but this is far from certain after only one or two confirmations and it can have an effect on outputs. A system that tries to address this has to deal not only with the callback and result but also with the possibility that it needs to recall a prevision emission and revise the result. It's not an easy thing to work out a catch-all solution that somehow balances speed and confidence for all dapps.

So, clients have all the primitives they need to work out something satisfactory. That's how it probably should be because it's not a one-approach-fits-all type of problem. And the node/EVM-level teams favor minimalism in any case. I believe those are the reasons why.

Hope it helps.

Rob Hitchens
  • 55,151
  • 11
  • 89
  • 145
  • The short answer is because that information is not stored on-chain. If it were stored on-chain it would be part of the consensus data as events are. Why did they chose to only have events? I am not aware of the rationale behind the design decision. If someone knows please leave a comment and let me know.
  • – Peter Prographo Feb 11 '19 at 09:53