Michelson contracts do not treat internal transactions as function calls but rather explicitly return a list of operations to be executed. This has a few benefits and a few drawbacks.
- Guards against reentrancy bugs
- Conceptually close to what cross-shard communication might look like
- Adds complexity when data is needed from other contracts
- A few constructs are impossible (e.g. Marbe flash lending on Ethereum)
Views are supposed to address some of the complexity of getting read only data from another contract but, in some cases, you need to be able to update those contracts.
For instance, suppose that a contract X owns some basked of FA1.2 token and provides the functionality that, when you call it with a secret code, you receive 10% of that basket. In this example, the callback is read-only and would be addressed by views, but this is only for clarity of exposition, there are examples with stateful operations.
When you call X, X needs to find out its balance in each of the tokens it holds, so that it can pay 10% of it. The way this would work right now is that you’d want to have two entrypoint
type entrypoint = | RedeemCode of string | ProcessCallback of nat
RedeemCode would be called first, and send a callback to the first token contract it might hold a balance in, passing
ProcessCallback as a callback. The issue is that, when the callback is called, we need to remember
- whether we are in a valid session (a code is being redeemed)
- who to send the tokens to
- which balance are we currently getting
The only way to do so, right now, would be to store all of this information in the contract’s storage so that it is preserved between calls.
On top of being cumbersome, this is highly inefficient as gas cost for writing data to storage are quite high, and they will be incurred for every single write even though none of them may actually be committed to disk.
getBalance function were to provide an extra field for information, and pass it back to us, this would be a lot easier than having to store the data. This can be done explicitly today, but is not part of the standard.
One possible extension of Michelson would be to attach data to an entry point. For instance if we have
an entry point
%callback_foo of type
(pair x y) we might pass an entry point
%callback_foo x of type y. I’m using
pair in this example but my preference has long been to allow mini stacks as types, so we might have
%callback_foo taking type
x :: y (meaning a x stacked on a y) and pass
%callback_foo x taking type
Another, largely equivalent approach would be to use an ephemeral storage, a storage per contract that explicitly does not persist between calls.