Wasm view functions

Background

In Solidity, contract developers can create view functions for their contract to help clients to access contract state.

In Substrate, we have runtime APIs that achieve similar goal. That’s totally useable and a good enough solution. However, it still have a lot of room for improvement.

One of the nice property of the Solidity view function is that the same getter can be used for both contract logic and front-end and that reduces a lot of duplicated codes. For example, a rebase token exposes a balanceOf function that returns the rebased token amount that can be changed for every block. Both other contracts and frontend/wallets are using the same balanceOf API to query balances and does not need to care where is this number stored nor how it is calculated. It just works.

We lost this nice property in the current storage based API. Most of the Substrate UI are accessing the storage directly and sometimes have to reimplement the additional calculation logic to convert them to user friendly representation. This means duplicated code between the Rust runtime logic and UI JS/TS logic. The more code, the more work, and the more bug.

Runtime API partially address by allowing runtime developers to expose Wasm runtime API interface to call some Wasm code to get the right number. This avoids the duplicated code issue. However, it also lost a nice property of using storage based accessor: change subscription.

Polkadot.js and many other UI subscribes the storage changes and able to update the changes to user in real time. This allow users always see up-to-dated value, bots able to subscript for changes in realtime. This allow developers to build efficient and elegant applications easily.

By switching to Runtime API based accessor, pulling are required to detect change. For some applications that need to monitor many/all accounts, it will completely destroy the performance.

Solution

I am here (re)proposing an idea that was floating between discussion threads for many years and finally now feasible: Wasm view functions.

The goal is simple, take the best of both worlds.

We can avoid code duplication by reusing the same Rust code implemented in the runtime and compile them to wasm function and make then available to clients.

We can detect the accessed storage when executing the wasm function and subscribe those storages for change notifications. When any of those storages changed, rerun the wasm function and resubscribe to the storages.

In this way, we can for example implements a rebase token, and notify user every time the balances changed, no matter if it is triggered by block number of a transfer or some other storage changes. We have all the dependent storage for this property and can only subscribe the required storages, no more, no less.

Implementation

I had this idea for many years but it wasn’t really feasible back then. This have changed.

With Chopsticks and smoldot, it is proven possible to:

  • Run wasm runtime in any modern JS environment
  • Create a custom backend for the storages and connect it to the wasm host

To make this working e2e, we need:

  • Rust framework to create Wasm accessor functions
  • Custom wasm execution env similar to the one in Chopsticks and collect all the accessed storages within a wasm function
  • Some helper JS library to put everything together
11 Likes

I see a lot of utility to this idea, and makes sense to have such options available to our ecosystem.

I just wanted to note in the context of creating community standards for Wallets or other UIs to interface with, I still believe something at the level of XCM makes more sense to me.

Feels to me that Wasm view functions would solve a specific set of problems, allowing front-ends to execute specific logic implemented inside the runtime, and I expect this will be most useful for specific app scenarios unique from chain to chain.

3 Likes

This is totally the case in nomination pools point/balance logic as well. Points is almost always the same as balance, and all wallets have to re-implement it.

In the short term, we will fix it with runtime API. Long term, this could be a good excuse to try out the wasm view functions as well.

I think this only applies to the Balance example and XCM is not so relevant for the broader use cases

Here’s an alternative: tap into the existing machinery for RPC-based storage update notifications. The runtime would be allowed to declare a set of “virtual storage items”. these are never stored anywhere and are instead “getter functions” as you said. They have a key in the trie as well, ergo a client can subscribe to them in the same way that it can subscribe to a normal storage item.

3 Likes

The runtime would be allowed to declare a set of “virtual storage items”. these are never stored anywhere and are instead “getter functions” as you said. They have a key in the trie as well, ergo a client can subscribe to them in the same way that it can subscribe to a normal storage item.

Could you expand a bit more on how this would work? If these are virtual storage items, how do they have a key in the trie?

Wasm view functions would be really useful for graceful upgrade of clients when some storage migration happens that client depends on by just releasing a new set of wasm view functions.

Wondering if there has been any progress on this?

Could you expand a bit more on how this would work? If these are virtual storage items, how do they have a key in the trie?

Truthfully I don’t exactly remember nor can I re-understand it, which means it was probably not a good idea :sweat_smile:. The idea you had and we discussed elsewhere seems better to me:

make the runtime API, record storage that was touched, then subscribe all those storage keys, upon subscription update, re-execute the runtime-api


Wondering if there has been any progress on this?

PSA to builders: A good way to express interest is to upvote [FRAME Core] Add support for `view_functions` · Issue #216 · paritytech/polkadot-sdk · GitHub with a :+1:

I had a working PoC but it is unusable due to restore disable-runtime-api · Issue #1621 · paritytech/polkadot-sdk · GitHub that the wasm will include the whole runtime, which makes it super large.

1 Like

Created a new issue based on our discussion
Subscribe-able runtime apis · Issue #3594 · paritytech/polkadot-sdk · GitHub. I think this should be achievable and very useful. Any feedback is welcome. :slight_smile:

2 Likes