Intro to Oracle Multicalls

Synthetix’s Perps V3 Testnet Competition is underway. In addition to testing the new functionality in the perpetual futures market implementation, the use of spot synthetic assets as collateral, and multiple liquidity pools backing different combinations of markets, this deployment is also testing a new pattern for consuming oracle data: ERC-7412.

This is implemented with an external node for the oracle manager, requires a client library for front-ends/integrations (that could ideally be built into wallet applications), and a minor protocol upgrade. The Synthetix V3 system can still operate with the existing oracle nodes as before.

Though this should be transparent to end users, it may cause some confusion for integrators. I thought it would be beneficial to provide some context on the problem this is solving, some technical details, and where this fits into the broader context of what decentralized finance is aiming to accomplish.

Price Oracles

DeFi has historically relied on push oracles. Decentralized oracle networks consist of nodes that come to consensus around the current price of an asset and execute a transaction to write this price on-chain. This may occur at a regular interval (a “heartbeat”) or when the price moves more than a certain amount (crossing a “deviation threshold”).

These prices can be useful for specific applications—like processing liquidations—but aren’t viable for very time-sensitive operations like quoting trade prices. Even if prices were written to every block, blocks aren’t generated fast enough to prevent latency arbitrage. This model is also problematic because consumers of the price data effectively can’t be charged for their use and oracle providers inevitably pay for unused price updates.

An alternative pattern involves pull oracles. Rather than only allowing the decentralized oracle network to write prices to a contract, cryptographically signed price data can be made available by the decentralized oracle network off-chain. Then, whoever needs a price to execute a transaction can retrieve it, have it cryptographically verified on-chain, and stored. Here, the consumer pays the gas cost to execute the verification and storage in addition to a fee when the data is verified. This is essentially a superset of the push oracle pattern; anyone can run an automated price pusher with a heartbeat, deviation threshold, or any other logic.

With pull oracles, Synthetix has been able to implement asynchronous order settlement to prevent front-running. Under this pattern, a trader commits an amount of assets to trade in an initial transaction and the timestamp of the order commitment is recorded. Then, anyone can provide cryptographically-signed price data associated with the timestamp of the commitment and the order is settled at the exchange rate determined by that price.

So how do we implement this?

ERC-3668

When using Ethereum, users have a public wallet address and a private key associated with it. When submitting a transaction (like a transfer of assets or a trade), users cryptographically “sign” the transaction request with the private key associated with the wallet and send it to a node in the Ethereum network for execution.

Rather than actually submitting the transaction to the network for execution, users can also have the node simulate what would happen if they were to submit it. This allows users to see how much ETH would need to be spent to execute a transaction (the “gas cost”) and also whether it seems like it may run into an error during an actual execution attempt.

ERC-3668 is a standard in which a special error is emitted by a smart contract indicating that it needs off-chain data from an oracle to complete the transaction: OffchainLookup(address sender, string[] urls, bytes callData, bytes4 callbackFunction, bytes extraData).

For example, if someone simulates the settleOrder function on the Synthetix V3 Spot Market, the smart contract can say, “To settle this order, instead, go to one of these urls, and provide the price data there to this callbackFunction for verification and execution.”

For functions that require a single piece of off-chain data—like a simple order settlement or liquidation—this pattern works well. A problem arises if protocols are attempting to execute more complex operations, which may rely on a dynamic set of off-chain data. For example, a position may have multiple types of collateral margining positions across many markets. And we may want the collateral backing this market to be held on different chains.

Although theoretically it might be possible to build protocols that handle these situations with ERC-3668, it would prevent composability and add complexity to the code. You could find yourself in what JavaScript developers have dubbed “Callback Hell”.

ERC-7412

ERC-7412 is a draft standard inspired by ERC-3668. Here, rather than having the error include a callback function, it indicates the contract address where its seeking off-chain data along with a query which specifies the data it’s seeking: OracleDataRequired(address oracleContract, bytes oracleQuery). The structure of the oracle query depends on the oracle contract. For example, the error could communicate “I need to be able to retrieve a fresh price for ETH at oracleContract“.

The oracle contract provides an oracleId, analogous to a Chain ID in Ethereum. Rather than needing the smart contract to provide a URL, the client can determine a gateway for accessing the signed off-chain data specified in the oracle query. (This could be an HTTP URL, a node in a decentralized network, or anything else.) It can construct a call to a fulfillOracleQuery function on the oracle contract with this data.

Then, it takes the fulfillOracleQuery call and prepends it to a multicall. A multicall is a transaction that concatenates multiple transactions together. It can then simulate the multicall to see whether the error is resolved or if another fulfillOracleQuery call needs to be added to the multicall. This process can be repeated until the simulation succeeds (or reaches an unrelated error).

Now, instead of specifying callbacks, developers can just call functions on oracle contracts to retrieve off-chain data like any other smart contract function. Need four different prices to process an order in a perpetuals futures market? Not a problem—just call a function four times.

Technical Considerations

We need to employ some novel engineering methods to achieve this due to design decisions in Ethereum. Over time, this could become a standard adopted throughout the Ethereum ecosystem, streamlining developer and user experience.

The main issue is that it currently isn’t trivial to generate multicalls that preserve the original caller of the transaction (referred to as msg.sender in Solidity). Luckily, this is something that can be solved by account abstraction (ERC-4337), which is a major focus in the Ethereum community but not yet widely adopted in practice.

To support externally owned accounts (most commonly used by wallet applications currently), one relatively straightforward method involves adding a trusted multicall forwarder that includes standard Multicall3 functions. These functions are adapted to append msg.sender to msg.data (as in ERC-2771) and fix error message bubbling.

Another concern is that it may take many simulations to build desired transactions. This could take too long for time-sensitive use cases, like trading applications. In these cases, we recommend purpose-built clients which anticipate the oracle data required by a transaction and provide it on the first attempt. For instance, if an order involves a specific asset, the transaction can be initially prepared as a multicall which includes price data for that asset.

This pattern also means that end users will need to pay the gas cost for the verification and storage of off-chain data. Although this may be a short-term user experience concern, this ultimately makes the use of decentralized oracles networks economically sustainable. Further, adoption of this standard and the development of oracles that can query data from other chains (not only price data) will allow developers to create protocols that operate seamlessly across multiple layer two and layer three scaling solutions. By allowing transactions to be executed across an arbitrary number of chains, congestion can be avoided. This should bring gas costs down as much as possible for end users.

Why are we doing this?

Undeniably, the most proven use case for blockchain technology is permissionless, open financial infrastructure. Synthetix V2, which has processed over $20 billion of volume this year, proves that it’s possible to write open source code which allows anyone with an internet connection to take a position on derivatives of any asset with a price feed. It’s pretty incredible.

Ethereum has already demonstrated more consistent uptime than traditional financial infrastructure. Plus, the ability to maintain full control of owned assets and having transparency across the system is obviously superior to relying on opaque, privately-owned services (for both developers and end users).

Now we need to scale these systems, making them faster and cheaper. As the quality of order execution becomes competitive with rent-extractive, centralized services, the global financial system will become a more even playing field. Similar to how the internet reshaped the media industry, decentralized finance will disrupt the financial industry. Avoiding “Callback Hell” is part of how we get there.

Subscribe to Noah
Receive the latest updates directly to your inbox.
Mint this entry as an NFT to add it to your collection.
Verification
This entry has been permanently stored onchain and signed by its creator.