# Ethereum is Changing, and Smart Contract Architecture Should Too *Architects are wise to respect the environment on top of which they will build. Smart contract developers who build on Ethereum should similarly iterate on dApp architecture as the blockchain changes.* | ![Design for extreme temperatures](https://i0.wp.com/www.printmag.com/wp-content/uploads/2022/04/Chiwan_07.jpg?resize=1000%2C564&ssl=1) | |:--:| | ***Design for extreme temperatures*** | | ![Design for rising water levels](https://www.featureshoot.com/wp-content/uploads/2020/01/James-Casebere-2-700x901.jpg) | |:--:| | ***Design for rising water levels*** | ## A proposal for how to design dApps that will scale with the rollup-centric future of Ethereum. [Ethereum](https://ethereum.org/en/developers/docs/intro-to-ethereum/), the most prominent decentralized blockchain, gifted computer programmers a new primitive of “self sovereign data” which unlocked a new era of application development. But Ethereum is changing and application designers should again fundamentally rethink how they architect programs in light of these impending changes. Ethereum is betting on a [rollup-centric future](https://ethereum-magicians.org/t/a-rollup-centric-ethereum-roadmap/4698) where rollups/l2s will be the fast and cheap layers that users interact with while ethereum will be the high security execution and data availability “base” layer for the L2s. ### This article will: - Present an architecture that dApp builders can use to leverage rollup centrism in order to design applications that offer speed and cost efficiency to users without sacrificing security. This architecture will scale with the rollup centric roadmap for Ethereum and become even more secure as zkrollups go live. - Compare this architecture to the dominant one today used by developers. - Demonstrate how this architecture is used today to power existing applications today and offer some other examples of practical use cases. This architecture is more than theoretical--it underlies how [Across](https://dune.com/unionepro/Across-Protocol-Stats) works, [a cross chain bridge](#Cross-chain-asset-bridge1) that pools assets on L1 while offering user entrypoints to bridge assets across L2s. ### Hardware features and constraints should be at the forefront of software development I am often guilty of treating hardware as a "black box" and developing software without concerns about whether it will be deployed on a managed cloud instance, a mini computer, or a cell phone. But this is not the way, especially for smart contract development that deploys onto decentralized blockchains. Ethereum will be our focus. Let's consider first the unique features that Ethereum offers applications: - Self custody: users self custody their assets and must grant permission to an application to take them - Always on, open to all: Once a user decides to interact with an application, they cannot be censored - Transparent: Applications are open source so users can verify that applications will not steal their assets - Immutability: Actions are permanent and cannot be undone These features come with their own unique constraints: - High latency: Transactions process every ~13 seconds - High cost: Users must pay for every single state-modifying transactions This hardware profile is very different from traditional servers or managed cloud instances which sacrifice features in the first list in order to offer low latency and cost. Given this hardware profile, it's not surprising that the teams who tried to copypasta central limit order books onto Ethereum were [outcompeted by Uniswap](https://twitter.com/santiagoroel/status/1351754351346380807?s=20&t=iOc53Ubr-Ut2QmJPKBXWCQ) which leveraged self-sovereign data to offer the first killer application of Ethereum: passive liquidity providing. Managing limit orders on a blockchain is untenably expensive and passively doing anything on a non-transparent and potentially-censored platform would be too risky for liquidity providers. ## What is the state of changes happening at the Ethereum infrastructure level? It's essential to follow the core devs meetings in order to stay updated with changes to the hardware we build on. The latest meeting notes can be found [here](https://github.com/ethereum/pm/issues/652). From these notes, it is clear to me that client teams are prioritizing improvements that will make rollups more usable, most notably [EIP-4844](https://www.eip4844.com/). We all should prepare soon for much [cheaper rollups](https://dev.optimism.io/eip-4844-an-optimistic-bet-on-rollup-scalability/)! Further into the future, this prioritization of scaling rollups should continue, which you can see in Vitalik's roadmap chart [here](https://twitter.com/VitalikButerin/status/1588669782471368704). In 2018 when a lot of the current "blue chip" DeFi apps were first conceived and implemented, there was no cheap alternative layer to Ethereum. Back then, deploying dApps entirely on Ethereum was the only choice for developers. In 2021 when L2s started to come online, dApp developers took the same mindset and conveniently redeployed their entire smart contract systems onto L2s. This architecture was strategic in order to get to market quickly but I don't think it will stand up well to the next generation of apps that spend time upfront to design their architecture to take advantage of the unique properties of rollups. The key property being: L2s and L1 share a security layer but offer very different user experiences in terms of speed and cost. ## Single layer dApps cannot serve all properties of the perfect dapp Today, rollups are used by developers like alternatives--*instead of complements*--to Ethereum. New dApps elect to deploy on one rollup and have longer term plans to deploy independent copies on all of the others. These dApps often duplicate assets and logic on each of these L2 deployments, and they usually do not share security. I'll label these programs "single layer dApps": a smart contract system comprised of contracts that are all deployed on one blockchain. These applications sacrifice one of the three ideal application properties: security, speed, cost. Applications deployed on rollups sacrifice security, while those on Ethereum give up speed. The ideal self sovereign dApp provides users some utility and has all of the following properties: - User experience is fast and incurs low fees - User data/assets is stored immutably in a highy secure place, until user wants to change their data again - Usage is censorship resistance, no one can stop the user from using the program As the most decentralized blockchain, Ethereum offers censorship resistance. Rollups with decentralized sequencers will offer censorship resistance, but [currently](https://developer.offchainlabs.com/mainnet-beta) their guarantees are [weaker](https://community.optimism.io/docs/protocol/#decentralizing-the-sequencer) than Ethereum because their sequencer set is whitelisted. Ethereum data is finalized or made immutable after 15 minutes and is probabilistically finalized in half that time. L2 data is rolled up and published to Ethereum, where it is stored immutably, after a finalization period of 7 days for optimistic rollups. Optimistic Rollup data is [probabilistically finalized](https://developer.offchainlabs.com/tx-lifecycle#---finality-check-ethereum-equivalent-finality---) after a 15 mins. Ethereun is 100x more expensive and NX slower than rollups. So, single layer dApps must give up one of the ideal properties of a self sovereign dapp. dApp developers can do better. The major benefit of rollup centrism is that applications can eat their cake and have it too. The key rollup centric feature to take advantage of is the shared security layer. I’ll explain how dapps can allow users to interact with smart contracts on rollups and simultaneously secure their assets on Etheruem. I’ll also explain the tradeoffs this architecture offers and what kind of dApps should opt for this architecture. ## Multilayer dapps are the solution for certain types of dapps The architecture we want is a multilayer dApp: an app whose smart contracts are deployed on L2s and L1. In order to determine which parts of the app to deploy on which layer, let’s first separate an application into its elemental parts: > An **Application** (1) receives user input, (2) loads existing data, and then outputs a signal using these inputs to execute logic. > > In the sketch below, the signal can take on one of four values: `{1,2,3,4}`. The signal then informs how new data is created; in other words, the signal is the input that triggers a (3) state transition. Usually the logic is the most complex and resource intensive part of the program. Permanent data storage (the stuff loaded in (2) and modified in (3)) is expensive while temporary input data is cheaper to use and is sometimes referred to as "memory" instead of "storage". | ![Simple application circuit](https://i.imgur.com/3UI0eso.png) | |:--:| | ***Simple application circuit*** | Recall the ideal features of a self sovereign dapp. I believe that we can be strategic about where (i.e. L1 or L2) we deploy the elemental parts (memory, data, logic, signals) of an application to achieve all of the ideal features (immutability, censorship resistance, speed, cost). This is the crux of this essay. Single layer apps deploy all of the elemental application parts on a single chain. The architecture I’ll present receives user input on L2s, stores critical data on L1, and incentivizes third parties to compute logic off chain and propose signals optimistically on L1. This is my attempt to illustrate the architecture: | ![dApp architecture with different components of the application deployed on different networks](https://i.imgur.com/uYK2WtI.png) | |:--:| | ***dApp architecture with different components of the application deployed on different networks*** | 1. User inputs data into smart contract on L2 2. Offchain worker "Dataworker" queries L2 input (from Event or contract data) and L1 data/state 3. Dataworker computes signal from L2 and L1 data and proposes signal to L1 contract 4. After signal proposal passes optimistic validation period (e.g. 2 hours in this example), smart contract can use signal to modify contract data The key innovation with this architecture is to move the logic completely off chain. The logic is the glue that connects the UX on L2 with the data update on L1. The reason that offchain logic works is that the app can actually ignore how the logic is implemented, as long as it’s resultant signal is correct. The logic can be closed source and totally untransparent (it can even be coded in Python!), but if the correct signal is brought on chain, then the resultant state transition will be expected and valid. ### Computing on-chain is expensive, especially on Ethereum and even more so for more complex applications. By moving the logic off-chain we can eliminate gas costs from the most gas intensive element of the application. The trick to allow the logic to be computed quickly offchain and have a signal produced on L1 is to let ***anyone*** stake a bond and propose the signal. The proposed signal must then be validated via an optimistic challenge period. If we allow anyone to stake a bond to challenge the proposed signal, then we only need ***one*** honest watcher who can validate its correctness off chain and claim part of the proposers bond if their challenge is valid. (This validation could be encoded in a zk circuit for simpler validation rule sets but this is likely to be too expensive for most useful applications). If the proposal is unchallenged, the proposer earns a reward. This setup incentivizes dataworkers to race to complete the offchain computation of the signal. Dataworkers are rewarded because they take on risk that they are challenged correctly. This can happen to even honest ones if the input data on the rollup or Ethereum [gets rolled back by the validator set](https://www.alchemy.com/overviews/what-is-a-reorg). Imagine in a voting app, the dataworker proposes the results of the vote to l1 but after their proposal the vote history changes on the l2. This is called dataworker finalization risk, the risk that input data is not finalized. Dataworkers also take on implementation risk: since the logic is computed offchain, there can be many acceptable implementations of the logic, so the dataworkers all need to implement the same logic correctly. #### What happens if a dispute occurs? The dApp should then allow anyone to propose a new signal and kickoff a new optimistic challenge period. The dispute itself should be resolved via a decentralized court or schelling point oracle like [UMA](https://docs.umaproject.org/). This is the oracle that secured [Across](#Cross-chain-asset-bridge1). ## Multilayer architecture relies on social consensus amongst validators about the logical rules for validating signal proposals Validators must agree to the same set of logical rules about how to take input and existing data to create new data and challenge any proposers who incorrectly implement the logical rules to produce an invalid signal. This also means that validator challenges must be resolved by social consensus. This is why schelling point oracle systems like Uma and Kleros are best positioned to answer the question of “did this Dataworker correctly apply the rule set to the input data to compute the signal?” To enable logic to be computed off chain, The app developer must define a clear set of rules, which can be lengthy for complex apps, and should open source a well documented implementation code of the rule set. This way there is the possibility for users to protect their own assets by running a validator when submitting input to the app. One way to do this is open source the ruleset as a reference for Dataworker implementations, [like Across does](https://github.com/UMAprotocol/UMIPs/blob/master/UMIPs/umip-157.md). The Dataworkers and the dispute resolution oracle must both reference this rule set in order to validate signal proposals. ### Multilayer dApp architecture summary: - Input received on L2: users get speed and cost efficacy. - Data stored on L1: long term data persistence on high security layer. - Logic applied off chain to produce signal, resulting in large gas savings. - Signal proposed to L1. This proposal undergoes optimistic challenge period where anyone can challenge proposal. - Challenges are resolved by decentralized dispute resolution layer that resolves disputes via social consensus - After optimistic challenge period is passed, signal can be used as input to execute state transition. ## Illustrative Examples Let’s walk through a few practical examples of apps you could build with this architecture. ### Governance: - What do users do on the rollup: users can vote on a rollup and have one voting power VP if they hold X on ethereum and two voting power if they stake X on ethereum in a staking contract. Users can change their votes. Users also earn one VP if they participate in a vote and earn. - What is included in the layer 1 signal proposal: a merkle root where each leaf contains a user account, their vote, and their VP - How is the signal is validated: message is valid if every user who submitted a vote is contained in a merkle leaf, their VP is correct, their vote value is correct. - Once signal is validated, how is layer 1 data modified: based on result of vote, execute governance action. ### Multi item Auction: - What do users do on the rollup: users submit bids on N items. Users can cancel and change their bids. - What is included in the layer 1 signal: a merkle root where each leaf contains one of the N items and the winner of the item. - How is the signal is validated: message is valid if every item is correctly rewarded to a winner based on the rules and the history of bids. For example, the auction rule might be a Dutch auction rule set. - Once signal is validated, how is layer 1 data modified: transfer items to winners ### Cross chain asset bridge[^1] - What do users do on the rollup: users lock up tokens on rollup R and request to receive tokens on rollup Q. users on Q can lend users on R this liquidity in exchange for a fee. The user R can set a fee that can be given to Q. The locked tokens on R get withdrawn to ethereum via the canonical token bridge. A liquidity pool on ethereum is drawn from to send to Q to refund the lender. If this bridge takes 7 days to process like it does on optimistic rollups then the ethereum LPs should receive a fee that reflects the opportunity cost they incurred. - What is included in the layer 1 signal: a merkle root where each leaf contains a deposit matched successfully with a loan. - How is the signal is validated: message is valid if every deposit requested that was loaned capital by a user on Q is included in the merkle root. If the user on Q failed to front the user R, (for example they sent too few funds or sent to the wrong address or took too large of a fee) then they should not be included - Once signal is validated, how is layer 1 data modified: send tokens from the liquidity pool to rollup R chain to refund all lenders included in the Merkle root. ### On chain game: - What do users do on the rollup: users interact with game. Users have in game items and attributes stored on layer 1. - What is included in the layer 1 signal proposal: a merkle root where each leaf contains a user account and their resultant user state after a period of time. - How is the signal is validated: message is valid if every users resultant state is correct given their actions on layer 2 during the period of time. - Once signal is validated, how is layer 1 data modified: user state is updated. [^1]: The cross chain bridge architecture is a distilled explanation of how Across is designed You can learn more about the cross chain bridge architecture of Across [here](https://github.com/across-protocol/contracts-v2) and [here](https://blog.openzeppelin.com/uma-across-v2-audit/). Across resolves disputes via Uma, a decentralized truth machine, which incentivizes dataworkers to monitor for invalid state transitions. The same team that designed and build UMA also built Across. ## What kind of dApps would benefit from this architecture? - Needs to interact with l1 contracts including l1 fungible and on fungible tokens. Uniswap v3 on optimism doesn’t need this. - Requires cheap and fast user interactions. Governance contracts with very infrequent voting doesn’t need this. - Can wait for data to finalize from l2 entrypoint to l1 and can take some level of finalization risk to accelerate this process. - Can incentivize Dataworker to compute offchain logic and propose signals to l1 that modify l1 data. ### Trade-off 1: Finality risk This architecture only works if the input layer is guaranteed to be permanent eventually. Doesn’t work if someone can change the data after the fact after a long period of time. This could result in double spend attacks by validators against the app. This is why rollups, which can be used as cheap and fast input layers and which are guaranteed to eventually finalize are perfectly suited. Distributed side chains like Polygon can also work because they offer finalization, but application builders must then balance different finalization processes for the input and data storage layers. Rollups offer the convenient quality that they will eventually finalize their data onto layer 1, so if you can trust the layer 1 data immutability property then you can trust the rollup’s. | ![Timeline of actors assuming finalization risk in multilayer dApp architecture](https://i.imgur.com/OerieRB.png) | |:--:| | ***Timeline of actors assuming finality risk*** | - During the challenge period, if layer 2 data changes, the Dataworker signal is no longer valid according to layer 2 state and can be disputed and the Dataworker can lose funds - After challenge period, if layer 2 data changes, the app can lose funds. For example, a double spend where user doesn’t actually pay for an asset but receives it from app because an invalid signal proposal passed the optimistic challenge period. Multilayer architecture essentially enhances UX at entry point without sacrificing data security by making the app and offchain Dataworkers take on finalization risk: the chance that data changes on the layer 2. Like all risks, finalization risk can be mitigated (with longer challenge periods) and priced appropriately, and dataworkers have flexibility with how much risk they want to take on. In the best case, the `(challenge period) > (true finality)`. This would mean that the input data on the rollup would finalize before the end of the challenge period meaning that the application would never get double spent because any signal that made it through the challenge period would accurately reflect finalized data. But this is not realistically fast enough for most use cases. In all cases, time to true finality must be finite. In centralized servers, we never get to true finality guarantee. Without true finality, app always assumes some risk. This architecture enables app to have sweet spot of good ux, high security, and allow a third party Dataworker to get rewarded for taking on an appropriate level of finality risk. As zk rollups become production ready and introduce shorter finality items, this challenge period can also be reduced. Practically, optimistic rollups today have seven day finality periods, but dataworkers could confidently assume that after 15-30 mins, l2 state is probabilistically finalized. Zk rollups will have finalization periods of 15 periods, the same time as layer 1. [Arbitrum dev docs](https://github.com/OffchainLabs/arbitrum/blob/master/docs/Finality.md https://developer.arbitrum.io/tx-lifecycle) distinguish between full finality and soft finality. You might be wondering, why do we even need a finalization period at all? Can’t we use fraud proofs and zero knowledge circuits to encode the logical rule set used to produce signals from input and bypass the challenge of setting a challenge period for optimistic validation? #### Aren't ZK proofs designed to succinctly prove that you know something, such as the validity of a signal? *TLDR*: I'm not sure if ZK proofs could be used for validating any dApps signal, certainly some, but I think future progress in the ZK space is needed first. ZK proofs can definitely be used for some useful logical rulesets. [Succinct](https://blog.succinct.xyz/post/2022/10/29/gnosis-bridge/) is building a light client that runs a circuit that can be used to prove that Ethereum validators included a piece of data in a block header, enabling anyone to supply state proofs, to a light client deployed on an L2, to prove any balances, storage, transactions, events that occurred on Ethereum. But more work is needed to build similar light clients for different validator sets to be able to succinctly prove knowledge of state about Optimistic Rollups (for example), or Polygon. Moreover, custom circuits would needed to be built for different logical rule sets. In the practical examples above, imagine that custom circuits would have to be built for the Governance and [Cross Chain Bridge](#Cross-chain-asset-bridge1) dApps. The latter circuit in particular seems like it would be too gas intensive to be useful, but I think more knowledgeable people about the current state of ZK proofs would know better than me. ### Tradeoff 2: incentivizing the dataworker Another complication with this architecture is mechanism design. The Dataworker must be fairly compensated for running complex logic and taking on implementation risk (a code in their bug could lead to their proposal being successfully challenged) and finalization risk. Without incentives, there is not guaranteed to be one honest Dataworker watching for invalid data modification proposals, putting user funds at risk. Should the app pay out of its revenue or pass on the cost to users? Generally, This architecture ultimately leads the app to replace user input costs with costs born by dataworkers who execute logic offchain and bring its output on chain. I believe the net gas savings of moving logic off chain will bring down the whole cost of the app, but the Dataworker still needs to be incentivized. We should summarize the required economic incentives given to each actor in this architecture to make it work * Dataworker proposal bond: Staked in order to propose a signal to L1. Lost if proposal is disputed and proposer loses the dispute. * Dataworker proposal reward: Compensates dataworker, after successful proposal passes challenge period, for computation cost and assuming finality risk. * Dispute bond: Can be equal to proposal bond. Staked by disputer to dispute proposer and lost if disputer loses the dispute. * Dispute reward: Can be % of proposal bond that is forfeited by loser of dispute to winner. ## Conclusion | ![Building and environment should be in harmony, just like smart contracts and Ethereum](https://i.imgur.com/n7Zt4j0.jpg) | |:--:| | ***Building and environment should be in harmony, just like smart contracts and Ethereum*** | Multilayer architecture might not be as cheap as single layer apps deployed on L2 but it is more secure. It might be less secure than single layer apps deployed on L1 (due to non-zero finalization risk and the one honest watcher assumption) but it will be a lot cheaper. This design pattern will work well for some applications and be over engineering for others. For some use cases like cross chain bridges, it can offer high security speed and cost to users. The app gains good UX and high security by offloading finalization risk to an incentivized third party, the data worker. We can draw on the history of software architecture as a point of confluence for the emergency of this architecture. This architecture is similar to Web 2.0 apps that use fast in memory caches like Redis to quickly store data without having to write into a DB, and eventually copy over its contents to the DB that has better data persistence but slower read and write functions. The key innovation is that this data entry is done in a trust minimized fashion on decentralized blockchains. Ideologically multilayer dapp architecture is more aligned with how ethereum is evolving. The rollup centric construction gives cheap and fast entry points into ethereun with a single layer of security on L1. Rollup data is finalized on L1 after a third party sequencer submits a signal to L1 informing how to transition L1 state, and must prove how this signal was constructed from L2 data. Multilayer dapps also have a single L1 layer of security, and the data on L1 is only modified after a third party dataworker proves (Via optimistic validation) the validity of a signal that they submit to L1. | ![Rollup centrism with a shared security layer](https://i.imgur.com/8sNFUAt.png) | |:--:| | ***Rollup centrism with a shared security layer*** | ## Thank you First of all, I want to thank my [UMA](https://umaproject.org/) teammates for building production applications using this architecture with me. This essay is humbly a summary of the lessons that we've learned building smart contracts in the wild. I would also like to thank my [Archetype](https://www.archetype.fund/) partners for providing me the space and support to put my thoughts into writing. It is very inspiring to work with a team that has such radical opinions about how crypto products should be built. I hope to inspire the next generation of decentralized system engineers with you. Through Archetype, I've met awesome builders like [Charlie](https://twitter.com/cdurbinxyz) and [Will](https://twitter.com/willkantaros) whose conversations with me helped inspire the ideas in this piece.