The Power of Predicates: A Purpose-Built Mechanism for Transaction Authorization

Ryan Sproule
·
co-authored with ·
No items found.
1.18.2023
·
Engineering

Intro to Predicates

The FuelVM introduces a unique primitive blockchain construct called a predicate. A predicate is essentially a new mechanism for authorizing transactions. In Ethereum, the only way to have permission to transfer ether or call a smart contract function is to own the private key of the account that owns that ether or initiates the transaction. The other option is to lock the assets in a smart contract that has some custom authenticate logic (smart contract wallet). The limitation of smart contract wallets in the Ethereum Virtual Machine (EVM) is that all transactions still must be initiated from an externally owned account (EOA). This leads to challenges around the behavior of smart contracts that depend on the transaction sender field (msg.sender != msg.origin).

A predicate is not a smart contract but still allows custom authentication logic for spending coins. This means that predicates can be spent without the need for a private key, unlike any EVM transaction. In practice, this means users can construct predicates that can be spent fully permissionless. When combined with the Fuel concept of scripts, the user experience for interacting with smart contracts becomes supercharged.

What is a Predicate?

On-chain, a predicate looks like an address, much like the Ethereum address we are familiar with 0x123…789. Users can send coins to it, receive coins from it, etc. like Ethereum addresses; however, a predicate is not derived from a private key. Rather, the address of a predicate is derived from the hash of its bytecode.

Unlike contract code, predicate code is never “deployed” to the chain. To create a predicate, someone simply writes the code for the predicate and then uses a compiler to generate a code hash, there is no interaction with the chain. Since the hash is both deterministic and collision-resistant, the coins locked in this address can only ever be spent if the predicate conditions are met. To spend a predicate, someone would provide the predicate bytecode (which must match the hash/address that the coins are behind) and a transaction that has the predicate resolve to true.

Critically, a predicate can only access the data in a transaction, it cannot view the current chain state. Allowing a predicate to access the chain state can create a denial-of-service (DoS) vector because one transaction can evaluate conflicting results (both true and false) depending on the state of the blockchain that is flip-flopping underneath it. In order to avoid this, predicates must be deterministic and pure (not read state). In order to prevent DoS, a node cannot do work on behalf of a client unless the client compensates them (the transaction is valid and gas is paid) or the client can be blocked because the request was intentionally malicious (transaction is invalid and gas cannot be paid).

To access the coins sent to a predicate, users just need to provide a transaction that will have this predicate evaluate as true. Notably, anybody can theoretically provide this transaction as long as they are able to construct the transaction that will evaluate to true.

Stable diffusion’s opinion on what a predicate looks like

Power of Predicates

The reason predicates are so powerful is that they can work in great tandem with two other key FuelVM features. One, the fact that transactions explicitly define all inputs and outputs, and second, the ability of the VM to inspect the full transaction data. This means that a predicate can inspect exactly what a transaction is going to do before it even executes the transaction.

In practice, this allows the predicate to check things like which smart contract will the transaction interact with and which accounts will receive coins. This leads to an extremely expressive authentication engine.

By Example — Predicate OTC Swap

First off, a swap is just 2 simple token transfers. Why is this complicated? Let’s use a classic pattern in computer science to understand how a swap actually works. We know that in order to swap two variables it is required to create a temporary variable. This is the same when trying to swap tokens.

Directly transferring coins to another person without a temporary “escrow” leaves the opportunity for the other participant to walk away without transferring their side of the trade.

Instead, the non-atomic swap can be made atomic, trustlessly, using the predicate to coordinate. The predicate just needs to define the condition in which the taker can spend the coins. So, the maker would create a predicate with a spending condition that guarantees that the output coin that they are expecting is an output to the spending transaction and they are the receiver. This means that nobody can spend the predicate unless they send the correct coins to the maker. Once the maker defines this predicate, the static bytecode for the predicate is hashed, and the maker can transfer the coins to this hash.

The expressiveness of this predicate is not limited here. Theoretically, the maker can add any conditions they want to the predicate. For example, they can leave an escape hatch condition that allows them to cancel the swap and transfer the coins back to their own account (this prevents the free option problem that was heavily explored in HTLCs).

Predicate OTC trustless swap

More Predicates in Practice

Let’s explore what else can we do with the power of predicates.

Multi-signature wallet

A predicate can check a series of signatures and only allow spending if m out of n of those signatures are present in the transaction.

Enforce that coin transfer update a smart contract

Only allow coins to be spent if they will also update some state on a smart contract. With this system, users can maintain on-chain state about historical asset holdings. This is useful, for example, in a voting token where users want to snapshot balances at a block height.

Virtual atomic bundles by script locking

It is possible in a predicate to enforce that a predicate can only be spent if the transaction is constructed by a specific script bytecode. This script bytecode can include several unrelated transactions, but guarantees that the coins can only be spent if the full script is executed.

Script locking actually unlocks a lot of very interesting UX patterns where users can tie the behavior of several different contracts or assets together by binding them in the predicate spending conditions.

Off-chain fraud monitoring

Users can lock their coins behind a predicate that can only be spent if a centralized fraud monitoring entity also signs off on it. This brings money transfer UX more like a credit card or bank in traditional finance. Obviously, users can give themselves the power to override, because they are still the owner of the coins.

Transaction automation

Since predicates can be constructed in a way that anyone can execute them, it would be trivial to automate transactions based on time or conditions. The predicate itself can include a bounty in its logic for some relayer to take if they execute the on-chain task on user’s behalf.

Trust-less order flow markets

Using the predicate hash as a way to obscure an order, users can guarantee that only a block builder that knows that predicate hash can execute the transaction. This is a way for order makers and order takers to make an off-chain agreement about the execution of a given order, but then fully execute the order trustlessly.

Conclusion

Account abstraction has been a topic at the forefront of discussion within the Ethereum world since the chain itself launched. Due to prioritization and technical migration complexity, we have not seen any major changes in production to date. New execution layers, such as layer 2s, present the opportunity for innovation on account abstraction without any dependency on the Ethereum core protocol. Fuel’s predicate design is the result of years of learning from both the Bitcoin and Ethereum communities and offers perhaps the most expressive and rigorously designed way to achieve full account abstraction and much more.

For a more high-level overview of the other unique properties of the FuelVM see my previous post.

Disclosures: Blockchain Capital is an investor in several of the protocols mentioned above. The views expressed in each blog post may be the personal views of each author and do not necessarily reflect the views of Blockchain Capital and its affiliates. Neither Blockchain Capital nor the author guarantees the accuracy, adequacy or completeness of information provided in each blog post. No representation or warranty, express or implied, is made or given by or on behalf of Blockchain Capital, the author or any other person as to the accuracy and completeness or fairness of the information contained in any blog post and no responsibility or liability is accepted for any such information. Nothing contained in each blog post constitutes investment, regulatory, legal, compliance or tax or other advice nor is it to be relied on in making an investment decision. Blog posts should not be viewed as current or past recommendations or solicitations of an offer to buy or sell any securities or to adopt any investment strategy. The blog posts may contain projections or other forward-looking statements, which are based on beliefs, assumptions and expectations that may change as a result of many possible events or factors. If a change occurs, actual results may vary materially from those expressed in the forward-looking statements. All forward-looking statements speak only as of the date such statements are made, and neither Blockchain Capital nor each author assumes any duty to update such statements except as required by law. To the extent that any documents, presentations or other materials produced, published or otherwise distributed by Blockchain Capital are referenced in any blog post, such materials should be read with careful attention to any disclaimers provided therein.

more by
Ryan Sproule
subscribe
more on
Search
subscribe:
Thank you! Your submission has been received!
Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.