Smart Contracts on Arweave

K - Oct 26 '23 - - Dev Community

Smart contracts form the nervous system of blockchain networks. They manage onchain assets: permissionless, decentralized, and verifiable. On Ethereum, smart contracts suffer from high gas costs.

While other chains managed to lower gas costs, they still pose an entry barrier for new users since they must buy tokens before sending their first transaction (TX). Arweave solves these issues with its SmartWeave contract standard. As it allows gasless execution, it makes onboarding new users easier and much more cost-effective. This article explains the SmartWeave standard and its different implementations.

If you want to stay updated on Arweave news, follow @onlyarweave on Twitter!

What are SmartWeave Contracts?

SmartWeave is a contract standard on Arweave. With SmartWeave, clients execute SmartWeave contracts, which means no gas costs, as nodes don't need to run anything to provide a service. However, they aren’t free because you have to pay to submit contract TXs to Arweave, but a few services subsidize these costs for small TXs.

Computational demand increases gas costs for TX on Ethereum since the nodes that execute the process require compensation, which means that running complex workloads on traditional smart contracts is prohibitively expensive.

SmartWeave doesn’t have gas costs, so it can deliver computations much cheaper than Ethereum. Also, Arweave onchain storage is much cheaper than other networks (e.g., 10MB on Arweave costs $0.04, but over $600,000 on Ethereum.) These two factors allow SmartWeave to tackle more computation and storage-intensive workloads than smart contracts on other chains.

Also, SmartWeave contracts are completely transparent to Arweave nodes; they handle SmartWeave TXs as any other TX submitted to the network. This opens Arweave to new contract standards in the future, but now, SmartWeave is the dominant one.

How do SmartWeave Contracts Work?

SmartWeave contracts are just multiple Arweave TX stitched together; everything is permanently stored on Arweave and lazily evaluated by the client when someone wants to view the current state. This is an important distinction; these transactions are gasless because nodes don't need to execute any computation.

Usually, three types of TXs are involved:

  1. One TX for the contract source code.
  2. One or more TXs for the initial state and a link to the contract code TX.
  3. Multiple TXs for actions that form the input to the contract. These contain a link to the initial state TX.

Each initial state TX becomes a new contract instance, making code TXs reusable. Figure 1 illustrates this.

Figure 1: SmartWeave Instances

Figure 1: SmartWeave Instances

 

If you’re familiar with domain-driven design, you can think of the actions on Arweave as an event store and the state as a view projection. SmartWeave stores all actions permanently on Arweave and only evaluates the state when someone must update or verify it, allowing SmartWeave computations to be massive and cost-effective.

The Contract Function

The contract implementation is a pure reducer function written in JavaScript or WebAssembly. It takes an initial state and updates it one action TX at a time.

Check out this JavaScript example contract function:

export function handle(state, action) {
  if (action.input.function === "add")
    return {
      lastCaller: action.caller,
      value: state.value + action.input.value,
    }
  if (action.input.function === "subtract")
    return {
      lastCaller: action.caller,
      value: state.value - action.input.value,
    }
  throw new ContractError(`Function "${action.input.function}" is undefined!`)
}
Enter fullscreen mode Exit fullscreen mode

This would be its initial state:

{
  "lastCaller": "<CONTRACT_CREATOR_ADDRESS>",
  "value": 0
}
Enter fullscreen mode Exit fullscreen mode

The function takes the current state and an action as arguments. Then, it creates the next state by returning a new state object. In the first call, the contract uses the initial state.

In the example, the handle function adds and substracts values to a state variable and tracks the last address called it. In the initial state, lastCaller is set to the address that created the initial state.

The Contract Runtime

To call the contract with all actions, you need a SmartWeave runtime, a JavaScript program that loads the contract function, the initial state, and all action TX related to the contract.

Figure 2: SmartWeave Architecture

Figure 2: SmartWeave Architecture

 

A pseudo implementation of such a runtime could look like this:

async function runtime(contractTxId) {
  const contract = await fetch("https://g8way.io/tx/" + contractTxId)

  const contractCode = await import(
    "https://g8way.io/" + contract.tags["Contract-Src"]
  )

  const initialState = await fetch("https://g8way.io/" + contractTxId)

  const actionTxs = await fetch("https://g8way.io/graphql", {
    method: "POST",
    body: "<CONTRACT_ACTION_TXS_QUERY>",
  })
    .then((r) => r.json())
    .transactions.edges.map((e) => e.node)

  return actionTxs.reduce((state, tx) => {
    globalThis.SmartWeave = updateSmartWeaveGlobals(tx)
    return contractCode.handle(state, tx.action)
  }, initialState)
}
Enter fullscreen mode Exit fullscreen mode

Note: This implementation doesn’t work for various reasons; it just illustrates the necessary steps for a SmartWeave execution.

First, the runtime function uses a TX ID of a contract to load a TX from an Arweave gateway. It uses the /tx/ route to get the tags and not the body of the TX. This TX doesn’t include the contract code; it defines the initial state and links it to a contract TX.

Then, it imports the contract code defined in the Contract-Src tag of the contract TX. This code is in the body of the source code TX, so it calls a gateway without the /tx/ path. Since the initial state is also in the body of a TX, the function does the same with the contract TX.

After the function collects everything to start the contract, it loads all action TXs users submitted to Arweave via a gateway's /graphql endpoint. This endpoint allows filtering TXs by tags, so it can define a GraphQL query that only returns the TXs that contain actions for the contract in question.

Finally, the runtime reduces the actionTxs array to the current state by applying the handle function to each action and the previous state. Before it feeds an action to the contract’s handle function, it updates a global SmartWeave object that contains information like current TX, current block, and some utilities.

Which SmartWeave Implementations Exist?

SmartWeave is one standard for smart contracts on Arweave; many builders wrote their own implementation, each with a different focus. This article will focus on the three most popular ones: SmartWeave, Warp Contracts, and the Molecular Execution Machine.

SmartWeave: The Reference Implementation

The reference implementation of the SmartWeave smart contract standard is also called SmartWeave. It’s written in TypeScript and has a CLI tool and an SDK. It’s the most basic implementation but also defines the minimal requirements for a contract and its runtime.

Client-Side Evaluation Only

SmartWeave contracts run completely on the client. You can deploy and evaluate contracts on the command line with the CLI or use the TypeScript SDK to evaluate contracts inside a JavaScript runtime like the browser or Node.js. They both handle the runtime work we explored in the previous section.

A Templated for SmartWeave Implementors

The primary goal of the SmartWeave implementation is to serve as an example for others. So, the target audience isn’t end-users but SmartWeave runtime creators. While you can use this implementation to build fully-fledged smart contracts on Arweave, I wouldn’t recommend it (but continue reading for the high-performance implementations!) The resulting contracts are very slow for two reasons.

First, each action TX is directly submitted to Arweave, which can mean a waiting time of multiple minutes until the TX is available online.

Second, the client reads actions from Arweave nodes, so they aren’t indexed, and it executes them locally, resulting in degraded performance for contracts with a huge number of actions, especially if many are invalid.

Another major downside is that you can only pay for deployments and actions with the AR token, which is harder to come by than ETH.

Warp Contracts: The Enhanced Implementation

Warp Contracts is the most popular production-ready implementation. It’s also written in TypeScript and has a CLI and SDK. In addition, it brings a few quality-of-life improvements, like calls between contracts, key-value (KV) storage, custom extensions, and high-performance infrastructure. Still, you can submit a plain action TX to Arweave to interact with a Warp contract, making it backward compatible in many cases.

Calls Between Contracts

Sending actions from one contract to another allows for more modular implementations since not all logic related to an app has to live in one contract. Without it, the client would have to facilitate inter-contract communication explicitly.

Key-Value Storage

The KV storage improves read and write performance by moving data from the state into LevelDB.

The runtime generates the state at execution time, but the contract must pass around the whole object for each new action and recreate it when it changes.

The KV storage is also created at execution time but persists from the initial state until the last action; its structure lets you read and write a subset of its stored data, making it especially performant in scenarios with a big state.

Custom Extensions

You can create custom extensions, which Warp will attach to the global SmartWeave object. If you have a library that could be useful in multiple contracts, you can create an extension so it won’t bloat your contract code. Instead, the client loads the required extensions via a package manager to ensure it can evaluate the contract correctly.

For example, I created a plugin for the Zod library that allows you to validate your contract inputs with Zod schemas while keeping your contract footprint low.

Warp Infrastructure

Redstone Finance, which created Warp Contracts, hosts infrastructure for Warp Contracts, including servers for sequencing, submitting, and indexing TXs and delegated evaluation environment (DRE) nodes.

This infrastructure enhances read performance drastically. The TXs required for evaluation are indexed by a Warp Gateway so the client can fetch them quicker. Using DRE nodes removes the client-side evaluation completely by evaluating the contract remotely. When using a DRE node, the client can load the finished state.

Submitting actions is faster, too, as Warp bundles them via layer-2 services like Irys, which supports thousands of TXs per second. Irys also subsidizes TXs smaller than 100KB, so users don’t need tokens to get started. If a TX is too big, Irys allows payment with popular tokens like ETH, SOL, or MATIC.

Molecular Execution Machine: The Serverless Implementation

The Molecular Execution Machine (MEM) follows a different approach. It focuses entirely on remote contract evaluation. You can see it like SmartWeave as a service. Clients don’t have to compute potentially large contracts, and the MEM can cache the evaluated state for all clients. Contracts deployed with MEM are serverless functions automatically accessible via HTTP.

Deterministic Fetch

Another nice MEM feature is Deterministic Fetch. Since SmartWeave contracts are lazily evaluated, fetch calls usually happen at evaluation time after a TX was created. If the API changes or goes offline, SmartWeave can’t guarantee the evaluation. With MEM, you can call any HTTP API from inside your smart contract, and the contract stores the response so other users of your contract can replay it when they evaluate its state.

Multi-Chain Authentication

MEM uses Deterministic Fetch in the molecules.sh repository that enables multi-chain authentication with EVM-compatible chains, Solana, Dfinity, and more.

Like Warp Contracts, MEM subsidizes TX costs so that you can build serverless APIs backed by SmartWeave that people can access without a wallet.

If you liked this article and want to dive deeper into the Arweave ecosystem, check out Arweave Hub, where you can find events, podcasts, and learning resources.

Summary

Smart contracts on Arweave have distinct characteristics, many of which you will likely appreciate. While Ethereum uses Solidity and Solana Rust, Arweave went differently with JavaScript and WebAssembly. While it might seem unorthodox in the blockchain space, it has benefits. Since web2 devs know JavaScript and TypeScript, they can leverage their existing skills in an emerging market. With WebAssembly, even Rust developers can start building right away. This includes developers from traditional tech and smart contract engineers from other ecosystems like Solana or Celestia.

Making SmartWeave lazy and executing them on the client execution is a fresh take for smart contract execution. It eliminates the gas cost for remote execution entirely, letting SmartWeave behave more like traditional software. Paired with the low onchain storge, SmartWeave defines a new category of smart contracts.

As the SmartWeave standard was released a few years ago, multiple implementations are already available, so developers have several options depending on interests and needs.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .