Virtual Contracts

Tezos Protocol upgrades commonly introduce new operations. Mumbai introduces Smart Rollup operations on L1. Here are examples of operations that have been added in the past:

  • Lima
    • Update_consensus_key
    • Drain_delegate
  • Kathmandu
    • Increase_paid_storage
  • Ithaca
    • Preendorsement
    • Double_preendorsement_evidence
    • Set_deposits_limit

There are many reasons why new operations are included. Sometimes, an operation affects the network in general. Sometimes, an operation affects a specifc manager account.

Let’s take a fictitious dummy example: some Tezos accounts participate in a random draw. At the beginning of every cycle, the protocol picks an account from the list and stores its address in a special place in the context as a winner. You can query the winner with a RPC call.

You can do that with a smart contract; that’s not the point. Here, we want to make a meta-point about protocol development. So, let’s say there is special protocol code dedicated to picking a winner.

How do we want accounts to register to this draw? We can create operations Register_draw and Unregister_draw: operations are sent to the mempool and included into blocks.

But adding new operations to the protocol is expensive:

  • new encodings need to be created
  • libraries need to add support
  • indexers need to parse them

Also, only manager accounts can send an operation. If we want smart contracts to be able to participate in the draw, a change in michelson is required as well.

A different semantic for interacting with the protocol are virtual contracts. Similar to precompiled contracts on Ethereum, virtual contracts have a fixed address on-chain. For example KT1000000000000000000000000000000001 could be a virtual contract. It has two entrypoints register() and unregister(). No token transfer is required to call these entrypoints.

Calling these contracts is functionally equivalent to calling a new operation:

  • contract parameters are operation parameters
  • a rejected operation is a backtracked contract

What are the benefits?

First, it makes it easier on the library developers such as Taquito: an operation may not affect the library, but they still have to add logic to allow sending it. If it’s a virtual contract, is it de facto supported. The same goes for wallet developers: wallets already support contract calls and would have no problem calling a special address.

Second, it’s more composable: indeed, you can have other smart contracts call this contract. But only if it makes sense. You can as well make sure that any such call will be backtracked.

An example where it makes sense is the cobaking operation: instead of two new operations cobake/uncobake, you simply call a virtual contract with two entrypoints cobake/uncobake and a value as a parameter. Contracts can also call them.

Any future protocol features that pertain to only one manager account can be represented with such virtual contracts, with each contract associated with an unique increasing address.

When virtual contracts exist, the only way to interact with the protocol feature that they represent is to run the TRANSFER_TOKENS Michelson instruction. This instruction is therefore poorly named, as the relevant part of it is not the transfer, but its effects.


Is this a proposal to add Virtual contracts into Tezos protocol?

This is just a suggestion. If it makes sense, and if we want cobaking, then cobaking could be implemented this way.

1 Like

Second, it’s more composable: indeed, you can have other smart contracts call this contract. But only if it makes sense. You can as well make sure that any such call will be backtracked

I don´t see the benefits here… Or better said, I don’t think it is an example of “good compositionality”. The trade off of making the business logic of smart contracts entangling with other protocol components (PoS, consensus, etc) is not worth it. IMHO this goes against having a more modular protocol, and increases the complexity of the protocol.

Maybe I’m too biased/close to our protocol design, but the fact that features are implemented using (classes of) operations makes it easier to add/remove/maintain and test features.

Excellent proposal. This is exactly how we treat tx rollups and smart rollups ops in our indexers. We just view all new operations for such isolated features as classes of smart contracts with special address types (rollup addresses in this case) and treat all operations like entrypoints to these contracts. This allows us to reuse existing functionality and database tables for operations/contracts instead of adding new tables and new data types for each feature.

IMHO, its the easiest way to support new classes of features. I wish the protocol team would have considered such a design to make integration easier for everyone. After all new features are supposed to increase adoption, but the way they are introduced right now just creates a lot of unnecessary work for tooling teams.

1 Like