Skip to main content

· 3 min read
Taek Lee
Derek Chiang

At ZeroDev, we deploy a lot of contracts. Every release of Kernel or a plugin involves deploying contracts on 10+ networks, including both mainnets and testnets. This is a super tedious process because we have to acquire gas tokens on all these networks; testnet tokens, in particular, can be hard to acquire due to faucet limits.

This issue is not unique to ZeroDev, of course. All multi-chain Web3 projects need to tackle multi-chain deployment. The issue is even more pronounced in larger teams, since the developer deploying contracts may not be the person with access to the company’s treasury, so a complicated reimbursement process follows each deployment, where the developer tries to figure out how much $$$ they should be reimbursed from spending 10+ tokens.

TLDR: multi-chain contract deployment has been a headache — until now. We’ve created Orchestra, a CLI for deploying contracts on multiple chains without needing tokens on each chain. Here you can see it in action:

How it works

How is Orchestra able to deploy contracts on multiple chains without requiring tokens for each chain? You guessed it — ERC-4337 paymasters.

When you set up Orchestra, it creates a Kernel smart account owned by your private key. This smart account is then used to deploy contracts. Since it’s a smart account, we can sponsor gas for it using ERC-4337 paymasters, saving the account itself from having to own any tokens.

As the contract deployer, you would pay for the gas through your paymaster subscription. For instance, if you use ZeroDev as the paymaster, you would sign up at ZeroDev and pay with your credit card. In other words, you can now deploy contracts on any chain with just a credit card.

Limitations

We have successfully employed Orchestra to deploy multiple plugins, cutting the deployment time from hours to minutes. However, the tool can still be improved:

  • Orchestra currently uses only ZeroDev infra, but it can in principle work with any AA infra. We welcome PRs to add support for other infra providers such as StackUp, Pimlico, Alchemy, etc.
  • Orchestra currently only supports deterministic deployment using CREATE2, through the widely-used deterministic deployment proxy which ensures that your contract will have the same address on every chain. For most projects this is exactly what they want, but we welcome PRs for non-deterministic deployments.

Get Started

See instructions here to get started!

· 5 min read
Derek Chiang
Taek Lee

At ZeroDev, we are obsessed with smart contract wallets (SCW), as we believe they are necessary for realizing the full potential of Web3. One practical roadblock towards a wider adoption of SCW and AA, however, is the fact that SCWs cost gas to deploy. In contrast, to create an EOA, it costs nothing — the account simply exists once you generate a private key.

The deployment cost of SCWs is why AA is unlikely to be widely used on L1s in the near term. Luckily, more and more traffic are moving towards L2 anyways, but even then we have come across high-volume use cases where the small costs of creating SCWs add up over time.

Beyond deployment, SCWs also necessarily introduce some overhead for each transaction, since the validation logic is implemented with smart contracts (as opposed to being a part of the protocol itself, which is the case for EOAs). While the overhead is small, they can still add up to a meaningful amount over time.

At the moment, Kernel is the most widely deployed SCW for AA, so we consider it a serious responsibility for us to optimize the gas efficiency of Kernel. But how?

Optimizing Kernel

When we first measured the gas efficiency of Kernel (v2), we were disappointed to find that it lagged behind some other implementations out there. The main reason, we quickly realized, was that there was a tension between performance and modularity. By supporting plugins in Kernel, we also sacrificed some gas efficiency since the code that dispatches to plugins necessarily introduces some gas overhead. That’s why Kernel used more gas than simpler SCW implementations that don’t support plugins.

But we didn’t want to make any excuses, so we went ahead and squeezed more performance out of Kernel. We are now happy to share that Kernel v2.1, the latest version, is now the most optimized AA wallets out there! . This is despite the fact that we are also the most modular. Here are the numbers:

CreationNative transferERC20 transferTotal
SimpleAccount4100619769086754594505
Biconomy29689210078089577487249
Etherspot30576910009189172495032
Kernel v2.036666210680095877569339
Kernel v2.129141310324092289486942
Kernel v2.1-lite2569659733186121440417

So how did we do it? It would be too long to go over every little optimization we did, so let’s highlight the main ones.

Optimizing the Proxy

You might know that most SCWs are actually proxies — lightweight smart contracts that point to an underlying “implementation contract” which contains the actual SCW logic. This is for two reasons:

  • Proxies are cheaper to deploy, since the proxy contract itself contains minimal logic.
  • Proxies are typically upgradable, so the user can switch from one SCW implementation to another.

Since Kernel is also deployed as a proxy, there are two things we can optimize:

  • The bytecode size of the proxy itself.
  • The “dispatch function” — how the proxy dispatches to the underlying implementation contract.

While Kernel originally used OpenZeppelin’s standard ERC-1967 proxy contract (and so did most other SCWs we looked at), we switched to the proxy offered by Solady, an audited set of hyper-optimized contracts. That brought us 118 less gas per UserOp and 82 less bytes per deployment.

However, we realized that we could further optimize on Solady because Solady implements an “admin” feature that allows the user to upgrade the implementation contract for the proxy. However, in the context of Kernel, this is actually unnecessary, because Kernel itself already knows how to handle proxy upgrades. Therefore, we were able to remove the admin feature, which removed another SSTORE that costs around 20000 gas on deployment!

Optimizing ECDSA Signature Validation

Kernel, being a modular wallet, can handle different kinds of signature schemes, but the most commonly used is still ECDSA which replicates the signing behavior of EOA wallets. Therefore, optimizing the ECDSA verification flow can be very impactful.

The first thing we did was to leverage Solady’s ECDSA recovery procedure, which saves 282 gas comparing to OpenZeppelin. But we realized that there’s a much bigger gain — again, since Kernel is so modular, even ECDSA verification is implemented as a module (that way, it can be easily swapped for another signature scheme such as multisig or RSA). However, by far the majority of ZeroDev users use ECDSA for signatures, so they are paying for the plugin dispatch cost for no reason.

That’s why we built Kernel Lite — a version of Kernel that hardcodes the ECDSA validation logic. If you look at the benchmark numbers again, you will see that Kernel Lite has by far the best numbers. We are currently auditing Kernel Lite and will be releasing it to our SDK soon.

Open-sourcing the Benchmark

As much as we love Kernel, we want all SCWs to improve in gas efficiency so users can enjoy cheaper transactions no matter which SCW they use. Therefore, we are happy to open-source our benchmark in the hopes that it will help other SCW implementations improve their performance.

We have taken great care to make the benchmark easy to use. To benchmark a SCW implementation, you just need to create a test class that inherits from TestBase.sol and implement a few functions. If you do so, please open a PR so we can add your SCW to the list. Together, let’s make SCWs more efficient and save gas for everyone!


ZeroDev is hiring! If what we wrote here sounds interesting to you, drop us a note :)

· 3 min read
Derek Chiang

At ZeroDev, we are always looking for more ways to help developers leverage account abstraction (AA). Today we are happy to announce that we have partnered up with Turnkey to enable two new types of AA wallets: passkey wallets and server-side wallets.

Passkey Wallets

Passkeys are becoming an increasingly popular alternative to passwords. With passkeys, users authenticate with their hardware devices. If the device happens to support biometrics, then the user can effectively “sign in” with fingerprints or facial recognition alone.

In the context of ZeroDev, passkeys can be used to authenticate with AA wallets. This enables a powerful experience where your users get to create self-custody AA wallets using biometrics such as Apple’s Face ID or Touch ID, removing the need for safekeeping seed phrases.

Here’s a simple demo of passkey wallets. You can also check out this demo from our partner Goldfinch.

Check out these docs to get started with passkey AA wallets.

Server-Side Wallets

Another common ask from our developers has been a way to “just create a wallet for the user” — that is, creating a wallet for the user without requiring any interaction from the user at all. This is typically useful in a Web 2.5 scenario, where the user is already identified with some Web2 credentials (e.g. usernames or emails), and you simply want to create and associate an AA wallet with the user.

To address this use case, we built “server-side wallets.” To create a wallet for a user, all you need to do is to specify a “user ID” — a unique string that identifies the user, which can be your database ID for example.

const { ECDSAProvider, getCustodialOwner } = require('@zerodev/sdk')

const ecdsaProvider = await ECDSAProvider.init({
projectId,
await getCustodialOwner('any-user-id')
})

Check out these docs to get started with server-side AA wallets.

Partnering with Turnkey

Under the hood, the keys used in passkey and server-side wallets are secured by Turnkey — a leading solution in secure key infrastructure.

Turnkey uses Trusted Execution Environments (TEE) to run its security-critical services, such as verifying passkeys and generating keys. Given its architecture, Turnkey itself is not able to access the raw private key materials — only the users who are able to authenticate themselves are able to access their private keys. In the case of passkey AA wallets, these are the end users themselves. In the case of server-side wallets, these are your backend servers.

Zooming out, this is an interesting case study of decentralized access to centralized infrastructure, which is an often under-explored area of blockchain research. One parallel is validium rollups, which are rollups that store transaction data off-chain on centralized infrastructure, sacrificing some degree of decentralization for lower gas costs. In our case, we store keys on centralized infrastructure, sacrificing some decentralization for better UX. Whereas validium rollups derives security assurance from redundancy and attestations, we derive security assurance from TEEs.

Get Started Today

Passkey wallets and server-side wallets have both received amazing feedback during their private beta, and now we are incredibly excited to release them for general usage. With these new solutions, AA has never been more accessible. Get started with passkey AA wallets and server-side AA wallets today!

· 2 min read
Derek Chiang

Check out this post by Henri Stern (CEO of Privy) about the Privy + ZeroDev integration.

Today, we are incredibly excited to announce our partnership with Privy, a leading Web3 onboarding solution trusted by many well-known projects. The Privy team deeply understands the importance of good UX, and it’s this fascination with UX that led them to explore account abstraction (AA).

Privy is a great solution for onboarding users who have no prior Web3 experience, since it’s able to handle social logins and create an embedded wallet for users with no wallets. However, users want to do things with their wallet, and a user with an empty wallet just can’t do much since they are not able to pay gas.

ZeroDev bridges that gap by bringing account abstraction (AA) to Privy. Users using Privy’s embedded wallet can now easily send transactions without paying gas, if the DApp is willing to sponsor gas for the user. Furthermore, users are able to leverage all the amazing features that AA has to offer, including transaction batching, paying gas in ERC20s, session keys, and more.

Given our shared commitment to good developer experience, Privy and ZeroDev have developed a helper library for developers to seamlessly integrate the two solutions. Get started here with Privy+ZeroDev today!

· 7 min read
Derek Chiang

Ever since releasing Kernel v1, we have seen a flurry of activities from developers building novel plugins on Kernel. However, developers soon ran into significant limitations that exposed some of the shortcomings of Kernel, which prompted us to start working on Kernel v2.

In this blog post, we will dive into some of the issues with Kernel v1 and how we addressed them in v2.

A fair warning: this blog is written for a technical audience who want to understand the inner workings of Kernel, especially plugin developers. Most users of ZeroDev do not need to understand what’s described in this blog.

Issues with Kernel v1

Validation and execution are closely coupled

In Kernel v1, plugins modify how transactions are validated. Once validated, the transactions are executed through a hardcoded execute function.

However, certain use cases turned out to require not just custom validation, but also custom execution. For example, the default execution function allows both call and delegatecall, and the latter makes it very hard to reason about the security of a plugin. Therefore, some plugin developers wanted to disable delegatecall altogether, but that was hard to do with Kernel v1.

Inability to add custom functions

While the default execute function is meant to be flexible enough to execute arbitrary transactions, sometimes there are legit needs to implement custom functions. For example, let’s say there is a new ERC like ERC-1271 that requires the implementation of new functions. With Kernel v1, there’s no way to dynamically extend the contract to implement the new interface.

Inability to change the default validation function

Plugins in Kernel v1 introduce new “paths” for transactions to be validated, but there wasn’t a way to update the “default path” — which validates ECDSA signatures from the wallet owner. In other words, the wallet owner can always execute transactions, regardless of plugins.

While this is normally desired, there are cases where the “default path” needs to be modified or even outright blocked. For example, if you want to build a 2FA account, it’s not enough to build a 2FA plugin — you also need to make sure that the default ECDSA validation function is no longer effective, or it would defeat the point of 2FA.

Overlapping storage between Kernel and plugins

In Kernel v1, plugins are invoked through delegatecall. To prevent storage collision between plugins and the kernel, plugins are required to use unstructured storage, sometimes known as “diamond storage.”

However, this requirement cannot be enforced, and a plugin needs to be carefully audited to ensure that it’s in fact not using any storage outside of its area. This places heavy burden on the user of the plugin as well as auditors.

Design Decisions for Kernel v2

Kernel v2 draws on the lessons we learnt from real-world applications building on Kernel v1. At the core of Kernel v2’s architecture is two key design decisions:

  • Separation of plugin storage from kernel storage.
  • Separation of validation from execution.

Separation of Plugin Storage from Kernel Storage

In Kernel v1, plugins are invoked through delegatecall, which means plugins and Kernel ultimately share the same storage. Therefore, plugin authors need to take care to not “touch” the storage area of the Kernel, by using “diamond storage.” This places burden on the plugin author, the plugin auditor, as well as the user to ensure that the plugin correctly handles storage.

In Kernel v2, validator plugins are invoked through call. Therefore, validator plugins have no access to the Kernel’s storage, vastly reducing the surface of attack.

Separation of Validation from Execution

Whereas there are only “validation plugins” in Kernel v1, there are now two classes of plugins in Kernel v2: validators and executors.

Validators

Validators are plugins that modify how transactions are validated. These plugins are akin to the plugins in Kernel v1.

One notable difference is that in v2, it’s possible to replace the “default” validator. For example, if you want to set up an account as 2FA, you would set the default validator to the 2FA plugin, therefore replacing the default ECDSA plugin. This makes it impossible to send transactions without going through 2FA.

Executors

Executors are plugins that add custom functions to Kernel. In particular, each custom function is tied to a validator, meaning that a call to a custom function is “routed” to a particular validator.

The ability to route each function to a different validator makes it possible to implement ultra-fine-grained security policy. For example, you might want to add a custom function to Kernel, but you ONLY want that function to be called if the user goes through 2FA. With Kernel, you can set up routing so that the custom function (executor) is routed through 2FA (validator).

How Kernel v2 Works

In ERC-4337, a transaction (aka “UserOp”) is processed in two phases: a validation phase and an execution phase. To understand how Kernel v2 works, let’s walk through the lifecycle of a UserOp as processed by Kernel.

Validation Phase

In the validation phase, the EntryPoint calls the validateUserOp function on Kernel. Transactions to Kernel can be executed in one of three "modes," as indicated by the first few bytes of the UserOp's signature field.

  • Sudo mode (0x0) In sudo mode, Kernel's "default validator" is invoked. The default validator is a plugin that determines how transactions are validated by default (that is, if the transaction is not handled by another plugin). In ZeroDev, the default validator is normally set to the ECDSA validator, which approves a transaction if it's signed by the owner through ECDSA -- just like a regular transaction.
  • Plugin mode (0x1) In plugin mode, Kernel "looks up" the validator to use by the function selector from the calldata. The mapping between function selectors and validators are set through the "enable mode," which will be explained later. In any case, once a validator has been looked up, it's used to validate the transaction.
  • Enable mode (0x2) In enable mode, Kernel "enables" a validator, and it does so by associating the current function selector with the validator. The validator's address (keep in mind that plugins are smart contracts) is encoded inside the signature itself. Once enabled, the validator will be used to validate this and every subsequent invocation of the same function in plugin mode.

Execution Phase

In enable mode, Kernel actually associates with the function selector not just the validator, but also the executor. Executors are smart contracts that actually implement the function that corresponds to the selector. That is, when you call the function kernel.someFunction(), the someFunction is actually implemented in an executor, not the kernel itself.

When EntryPoint calls the function, Kernel uses a fallback function to look up the executor associated with the function selector, then delegatecalls the executor to execute the function. If you are familiar with EIP-2535 aka "Diamond Proxies," you can think of executors as "facets."

Next Steps

Today we are happy to announce that Kernel v2 has passed the initial audit and therefore entered public beta. Here are some more resources for learning more about Kernel:

· 6 min read
Derek Chiang

(This post is cross-posted from the Portal blog and jointly authored with Parsa Attari.)

Today, Portal and ZeroDev are excited to announce the development of the first MPC + Account Abstraction (AA) wallet-as-a-service.

If you’ve been following the conversations around web3 UX, you know that MPC and AA are the two solutions most commonly brought up as the “next big thing.” In fact, we’ve seen countless arguments about which one is better.

But, far from being competing technologies, we believe MPC and AA are in fact incredibly synergetic, and when combined can unlock a level of UX that’s impossible to achieve with each technology alone.

What are MPC and Account Abstraction?

Let’s first clear up the concepts — what are MPC and AA exactly?

Multi-Party Computation (MPC)

Multi-Party Computation (MPC) enables users to have multiple key shares across devices instead of a single private key on a single device to manage access to a crypto wallet. MPC protects users from phishing attacks and the risk of losing a seed phrase by removing the single point of failure created by one key on one device.

Portal offers a two key share solution backed by a Threshold Signature Scheme (an application of MPC) to offer companies wallet-as-a-service. Portal wallets have backup and recovery methods to protect users against lost and compromised devices.

Account Abstraction (AA)

Today on Ethereum, every single transaction must be initiated by an Externally Owned Account (EOA) — the kind of account managed by traditional wallets like MetaMask. EOAs are deeply limiting, however, because the rules for validating EOA transactions are hardcoded into the protocol itself and cannot be changed.

The goal of account abstraction is to enable transactions to be sent from Contract Accounts (CA), which can program their own rules for validating transactions. This unlocks the abilities to sponsor gas for users, batch transactions, automate transactions…just to name a few.

ZeroDev offers a framework for quickly and safely developing AA wallets, by providing a headless AA wallet (aka “Kernel”) and the associated “plugins” that enable the aforementioned AA features.

Why do you need AA + MPC?

To understand why MPC and AA are synergetic, we must look at the lifecycle of a transaction.

In short, a transaction’s life starts off-chain and ends on-chain. It’s this duality that makes MPC + AA a complete solution for web3 UX.

MPC signs transactions off-chain

When you send a transaction, the first thing you do is to sign it. A normal wallet such as MetaMask uses your private key stored locally on the device to sign transactions. As previously stated, storing your private key on a single device opens up opportunities for getting your key stolen or losing your key.

With an MPC wallet like Portal, the transaction is signed by multiple devices. Furthermore, if you ever lose any one of the signing devices, another set of devices can coordinate to recover your key share on the lost device.

AA validates transactions on-chain

Once a transaction has been signed, it’s then broadcast to a network of validators, who then submit the transaction on-chain. This is where AA comes in. For a normal transaction, the validator would check that the transaction is, in fact, valid according to the protocol rules. For an AA transaction, the smart contract wallet itself will check and affirm its validity.

For example, for a normal transaction, if the sender has no ETH, the transaction is automatically rejected by the protocol. For an AA transaction, however, the smart contract can effectively say: “well, even though the sender has no ETH, this other account has agreed to pay ETH for this transaction, so I will let it go through anyway.” This is why AA wallets have “superpowers” like gas sponsorship.

How AA and MPC benefit from one another

As the flow above demonstrates, while MPC makes it easy and secure to handle keys, it does not fundamentally change how transactions are validated, which means we don’t get the benefits of programmable transaction rules such as letting someone else pay gas for you.

Meanwhile, while AA makes transaction validation incredibly flexible, it says nothing about how keys are handled. Therefore, in a vanilla AA solution, the user still needs to worry about securely storing and backing up keys.

By combining MPC and AA, you get easy and secure off-chain key management, plus flexible on-chain transaction validation. See this table for a detailed breakdown:

How MPC + AA Works

The clean separation between off-chain transaction signing and on-chain transaction validation means that we were able to combine Portal and ZeroDev in a very elegant way.

Setting up the MPC+AA wallet

As with the core Portal product, we start by generating key shares: one on the user’s device and one on Portal’s backend. Portal manages backup and recovery in the case of a new device or general recovery if a share is lost or leaked. All of this is done off-chain, which means no gas!

Using the key generated by Portal, ZeroDev can now deterministically compute the address of the AA wallet. Note that even though the AA wallet is a smart contract wallet, it’s not actually deployed at this point — which means you are still not paying any gas. Instead, you can already display the address to users and use it to receive assets.

Using the MPC+AA wallet

When you send a transaction from this wallet, ZeroDev formats the transaction in the ERC-4337 format (technically known as a “UserOperation”). This transaction is then submitted to Portal for signing with MPC. Once the transaction is signed, ZeroDev broadcasts the transaction to the ERC-4337 mempool. A network of bundlers then compete to submit the transaction on-chain.

Once on-chain, the transaction is validated by ZeroDev’s wallet contract. From the perspective of the ZeroDev contract, this transaction is no different than if it was signed by a traditional wallet with a ECDSA key. The fact that the transaction was signed in a multi-party fashion is completely transparent to the ZeroDev wallet contract.

Next Steps

While MPC and AA are both ground-breaking technologies poised to transform web3 UX, combining them takes your user experience to the next level by giving your users a smart wallet whose key can be easily and securely managed.

The Portal+ZeroDev smart wallet is in development today. If you are interested in this product, sign up for the beta waitlist. We can’t wait to see what you will build with MPC+AA!

· 9 min read
Derek Chiang

Web3 UX today faces many challenges. High gas costs, long transaction times, and difficulties managing seed phrases are some of the most common issues that many projects, including ZeroDev, strive to fix.

However, there’s one problem that lies at the heart of why Web3 just “doesn’t feel right” to regular users, and yet is rarely discussed. The problem is that authorization is broken on Web3.

Authentication vs Authorization

Before we delve deeper, let's clarify the difference between authentication and authorization.

  • Authentication is the process of proving who you are. For instance, if you arrive at an NFT drop that you've been whitelisted for, how do you prove that you have indeed been whitelisted? Typically, the NFT drop will request you to sign a message. By cryptographically signing this message, you are authenticating to prove ownership of the whitelisted wallet.

  • Authorization, on the other hand, is proving what you can do. When you swap tokens on Uniswap, for example, it asks for your "approval" of the tokens you are swapping. By doing so, you are authorizing Uniswap to swap tokens on your behalf.

Authorization on Web2

In Web2, authorization is usually managed with JSON Web Tokens (JWTs), primarily in the context of OAuth.

Consider when we log into Zoom using Google. An OAuth sequence prompts you to grant certain permissions, such as creating calendar events. Once approved, Google generates a JWT containing these specific authorizations.

The JWT, bearing Google's digital signature for verification, then allows Zoom to request corresponding services from Google's API on your behalf. As a result, Zoom can automate actions for you like calendar event creation, but it can't do anything else, like accessing your emails.

This process is so commonplace that people seldom pause to think about it. However, when newcomers to Web3 start using DApps, they quickly realize a daunting issue — authorization in Web3 is fundamentally flawed.

Authorization is broken on Web3

The central idea of the JWT experience is that on Web2, it's possible to authorize a third-party app (e.g., Zoom) to interact with a service (e.g., Google) on your behalf.

In Web3, however, there’s no common standard for authorization. As a result, each application (or ERC) has to implement its own method for authorization.

For example, for ERC20 tokens, the approve(spender, amount) function authorizes a spender to spend up to amount of your tokens. On the other hand, ERC721 tokens use the setApprovalForAll(operator, approved) function to authorize an operator to transfer all your NFTs in this collection.

More importantly, handling authorization at the contract level is deeply limiting. For example, in the case of an NFT (ERC721) contract, what if you want to authorize a third party to mint NFTs for you too? That would be helpful if you wanna set up a bot that mints when an NFT collection is dropped. Since the setApprovalForAll function only concerns with transferring NFTs, however, you are out of luck.

What we need is a Web3 equivalent of JWTs — a universal standard for authorizing third parties to perform actions on your behalf. This standard should be flexible and widely interoperable, enabling DApps to "speak" this authorization language with minimal modifications.

However, expecting all contracts to conform to the same authorization standard is a significant challenge. As already mentioned, ERC20 and ERC721 contracts handle authorizations differently, and most other contracts don't handle authorizations at all.

Use the wallet, duh

Turns out the best way to make authorization works for all contracts it to not worry about contracts at all — rather, we do it with the wallet.

After all, the most obvious way to authorize someone to do something for you is to, well, give them your seed phrase. That way, they can interact with any contract on your behalf. Problem solved?

Of course, we all know that’s a terrible idea, since there’s no limit to what the third party can do with your seed phrase. This is the equivalent of giving someone key to your house when they ask to use your bathroom — it’s overkill and unsafe.

So what if there was a way to give someone access to your wallet, but in such a way that they could only send a limited set of transactions for you?

Session Keys are the JWTs of Web3

Enter session keys — a feature of ZeroDev AA wallets wherein you can create keys that are scoped to only certain transactions, with an expiration time.

Session keys are the JWTs of Web3. Much like JWTs, session keys are cryptographically signed — in this case, with your master key. Like JWTs, session keys encode "scopes" within themselves that specify the actions they can perform. Also, both JWTs and session keys can be created with an expiration time to limit the consequences of keys being leaked.

While JWTs and session keys share many similarities, session keys are fundamentally more powerful, because they are programmable, whereas JWTs are defined by a standard that by definition doesn’t change. In that sense, session keys can be thought of as programmable JWTs.

The table below summarizes how JWTs and session keys compare across key dimensions.

FeatureJWTs (Web2)Session Keys (Web3)
Granular PermissionsProvides permissions for specific actions on the platform.Provides permissions for specific actions across different contracts, while setting parameters for actions, like a maximum gas amount, maximum transaction volume, etc.
Expiration TimeJWTs have an expiration time, requiring token refresh or user re-authentication.Session keys also have an expiration time, ensuring temporary permissions.
User ExperienceAllows users to stay logged in across sessions and share sessions across devices.Enables users to interact with a DApp within pre-set rules without the need to sign every transaction.
SecurityIf someone gains access to a JWT, they would potentially have the same permissions as the user until the token expires. The impact depends on the scope of the permissions granted by the JWT, and could include unauthorized access to user data or actions performed on their behalf.If a session key is compromised, the potential damage is limited by the specific rules and parameters set for that key. For instance, a malicious actor might be limited in the amount of tokens they can transact, the gas they can spend, or the duration they can interact with a DApp.
AdoptionWidely adopted in Web2.A relatively new concept in Web3, but already used (with mostly proprietary implementations) by several gaming projects including Loot Realms, Briq, Topology, Cartridge, MatchboxDAO, and more.
DecentralizationJWTs are issued and verified by a centralized server.Session keys are issued and verified by the user's wallet, and are entirely decentralized.
InteroperabilityLimited to platforms that support JWTs.Universally compatible with DApps on any blockchain that supports AA wallets
TrustTrust in the central server is required for issuance and verification.Trust is not required as rules are set by the user and signed with the user’s master key

Empowering DApps with session keys

It’s hard to overstate the impact of a flexible and interoperable means of authorization. By taking advantage of session keys, DApps can create experiences that are simply impossible to create otherwise.

In essence, session keys allow transactions that seem "automatic" from the user's perspective. This can occur in two forms:

  • Skipping confirmations while the user is online. This is most useful in social and gaming applications, where frequent, small transactions are common. Each time a user has to sign a transaction, it disrupts the gaming experience. Using session keys, the user can pre-authorize a range of transactions, allowing them to enjoy the game uninterrupted.
  • Executing transactions when the user is offline. Consider a user who wants to ensure that they don't miss out on an NFT drop while they're asleep. The user can create a session key authorizing the purchase of the NFT at the drop time, even if they're not online. As another example, DeFi applications can use session keys to automatically exit risky positions for users so they don’t get liquidated.

Use session keys with ZeroDev today

At ZeroDev, we recognized the potential of session keys early on. After many iterations, we now have what we believe to be the most advanced implementation of session keys in the ecosystem.

We'll save the implementation details for a future blog post, but here's a brief overview of what makes ZeroDev's session keys special:

  • ZeroDev session keys can be created off-chain. This means that creating a new session key does NOT require an on-chain transaction, enabling your applications to generate a large number of session keys without paying any gas.
  • ZeroDev session keys can define the scope of transactions based on various parameters such as contract addresses, function names, and ERC-165 interfaces (e.g., ERC20/ERC721), among others.
  • ZeroDev session keys can be authorized using only a public key. Thus, a client can create a public-private key pair and send just the public key to the master signer for authorization. This approach ensures that the private part of the session key never needs to be shared, minimizing the risk of leakage.

Session keys are available with ZeroDev today. Get started here and build some groundbreaking DApps!

The Road Ahead

The quest to fix Web3 UX is a long one, but session keys are a significant step towards this goal, offering a practical and secure solution to a major issue — the lack of authorizations — that has been largely overlooked until now.

The widespread adoption of session keys will require collaboration across the ecosystem. Wallet providers will need to support account abstraction (AA), and DApps will need to be built to take advantage of session keys. However, the benefits of dramatically improved user experience and security make the effort worthwhile.

As more projects adopt session keys and AA in general, we will see the gap between Web2 and Web3 experiences begin to close. ZeroDev is proud to be a part of this journey, and we hope our contributions will make your users smile!

· 8 min read
Derek Chiang

With the launch of ERC-4337, we are seeing tremendous excitement from Web3 developers to build the next generation of crypto wallets using account abstraction.

Whereas traditional wallets like MetaMask are powered by externally owned accounts (EOA), account abstraction wallets are powered by smart contract accounts (CA). These wallets will be able to sponsor gas for users, batch transactions, support automatic payments (subscriptions)… overall enabling a Web3 experience hitherto unimaginable.

While some wallet developers want to control the entire tech stack end-to-end, most wallet developers we’ve met would rather focus on the end-user experience, by building product features such as DeFi integrations, cross-chain transfers, etc. Actually coding a smart contract account in Solidity, and making sure it’s compatible with ERC-4337 and supports all the essential functionalities such as validating signatures (ERC-1271) and bundling transactions, is time-consuming and hard to get right for most wallet developers.

Introducing Kernel, a minimal smart contract account designed to be extended

Seeing this need, ZeroDev has developed an account abstraction wallet kernel. The term kernel comes from the lingo of operating systems. The Linux kernel, for example, is used by a wide range of operating systems such as Android, Raspberry Pi, etc. The reason why the Linux kernel exists is so that different operations systems do not have to build the basic OS functionalities (e.g. file systems and networking) from scratch. Rather, operating systems builders can focus on building the OS features that make the OS unique, whether it’s great UI, integration with popular apps, or whatnot.

Similarly, the goal of the ZeroDev Kernel is so wallet developers do not have to build the basic wallet functionalities from scratch. Specifically, the kernel includes the following basic features that we consider essential to any AA wallet:

  • Compatibility with ERC-4337
  • Validating signatures with ERC-1271
  • Batching transactions
  • Delegating calls

However, we recognize that wallet developers may also want to build additional on-chain functionalities, and oftentimes these needs cannot even be anticipated when the wallet was first built. For example, you might decide, after launching your wallet, that a lot of your users are using the wallet with Web3 games, so you’d like to support session keys (temporary keys with restricted permissions). It would be very painful if you had to ask your users to upgrade their on-chain smart contract accounts in order to support the new use case.

Therefore, we have built a plugin framework for developers to add functionalities on top of the kernel, without needing to upgrade the account itself.

Kernel plugins — ERC-4337-native Solidity modules that modify validation logic

So what exactly is a plugin? It’s a Solidity contract that the kernel can delegate to to modify the validation logic of the account.

That was a mouthful, so let’s look at an example. Let’s say, as a trivial example, that you want to allow someone to manage your USDC and DAI balances. You can create a contract like this (in pseudo-solidity):

contract StableCoinPlugin {
function validateUserOp(UserOp op) returns bool {
return op.to == USDC_CONTRACT || op.to == DAI_CONTRACT;
}
}

Essentially, this plugin authorizes transactions that interact with the USDC and DAI contracts.

Once this plugin has been deployed, you can sign an off-chain message authorizing this plugin. You can then share the signed message with the person or app that wants to manage your USDC/DAI. They will be able to send transactions on your behalf, but only if those transactions interact with the USDC and DAI contracts.

We will be diving deep into the plugin framework in a future blog post.

Kernel makes it easy for users to migrate between wallets

Most smart contract wallets are deployed as proxies, since proxies are a lot cheaper to deploy than the underlying contract, but also because proxies allow smart contract wallets to be upgraded.

One consequence of smart contract wallets being upgradable is that users are free to switch between account implementations. For example, a user might be onboarded to Web3 with a simple in-game wallet. The user might have accumulated some valuable NFTs in the game, and instead of transferring the NFTs to a real wallet, the user can simply upgrade its wallet implementation to a real wallet, while keeping the same address.

While the idea is very appealing, in practice migrating between wallets can be hard and unsafe. This is due to the issue with storage layouts. Consider these two wallet implementations (in pseudo-solidity):

contract WalletA {
address owner;
uint256 nonce;
// ...more code
}

contract WalletB {
uint256 nonce;
address owner;
// ...more code
}

If a user starts with WalletA, then migrates to WalletB, its storage will be corrupted because the original owner now sits on the slot of the nonce, and vice versa. So in reality, before the migration, the user would need to wipe its own storage, which is tricky and hard to get right.

ZeroDev Kernel is designed with migration in mind. To that end, Kernel uses diamond storage — a technique that ensures that one wallet’s data storage won’t collide with another wallet’s. Therefore, it’s perfectly safe to migrate either from or to a wallet built on the kernel.

Note that Diamond storage is different than Diamond proxies (ERC-2535), which is a much more ambitious and complex design. Here, we simply ensure that storage layouts between different wallets don’t collide.

The fact that ZeroDev is perfectly migrate-able makes it perfect for building onboarding wallets. Your users can onboard with ZeroDev and rest assured that when they find an AA wallet they like, they can seamlessly migrate to that wallet without having to change address. This is a 100x improvement over the status quo of “exporting seed phrases,” which involves trusting some centralized server to NOT store a copy of your seed phrase and hoping that the seed phrase isn’t leaked somewhere along the process.

Kernel vs Safe

During beta testing, the most common question we got was understandably this: why are you building a new smart contract account when you can just use Gnosis Safe?

In fact, ZeroDev started with Safe. We contributed heavily to the reference Safe 4337 implementation, and used it all throughout our beta. However, we ran into some major issues with Safe that blocked us from achieving our product objectives:

  • Safe is complex. By all accounts, Safe is one of the largest smart contract codebases ever. Many features have been added over the years to satisfy a variety of organizational needs, and truthfully most of them are completely irrelevant for the kind of single-user AA use case that ZeroDev sets out to address.
  • Safe is inefficient as an ERC-4337 wallet. As the reference implementation shows, the only way to make Safe compatible with ERC-4337 was to do it through Safe’s “fallbacks” and “modules” mechanisms. This leads to a large amount of context switching and therefore high gas costs for even the most simple operations.

Ultimately, Safe was designed for a different use case — organizational multisig. This is about as far from the single-user, single-sig use case that ZeroDev is designed for. Therefore, we ultimately decided to bite the bullet and implement a smart contract account optimized for retail AA users.

With Kernel, we now have a much simpler, much more efficient, and highly extensible smart contract account, and our users couldn’t be happier.

Kernel, ERC-6900, and Interoperability

One main goal with Kernel was to foster a thriving plugin ecosystem, but some may be concerned that a plugin developed for Kernel will only work with Kernel.

As if anticipating that concern, our friends at Alchemy recently drafted ERC-6900, titled “Modular Smart Contract Accounts and Plugins.” The goal of the EIP is to define a common interface between smart contract accounts (e.g. Kernel) and plugins.

We are very happy to see this development and we will be contributing to the ERC. We are also glad to see that we’ve made many of the same design decisions that the ERC authors did. As of today, Kernel is the closest thing to an implementation of ERC-6900 that we know of, and we will be making Kernel fully compatible with ERC-6900 once it’s finalized. That way, plugin and wallet developers building on ZeroDev can rest assured that they are building on top of an open standard and will enjoy great interoperability for their products.

Start building on Kernel now

Today, we are excited to announce that Kernel has been open-sourced and audited, and it’s now available for anyone to use. Being an open-source project, Kernel is free for anyone to fork and extend. You can use ZeroDev to quickly spin up Kernel-based AA wallets, and then extend the wallet’s functionalities using our plugin framework. We are already building some of the most commonly asked-for plugins including session keys, which we will dive into in a future blog post.

We are confident that Kernel will dramatically lower the barrier for building wallets powered by account abstraction. We can’t wait to see what you build with Kernel!

· 5 min read
Derek Chiang

Unless you check new ERCs everyday (in which case, good for you), you probably haven’t noticed this new ERC known as ERC-6492, innocuously named "Signature Validation for Predeploy Contracts.” As this post is going to argue, ERC-6492 is critical to the wide adoption of account abstraction and smart contract wallets in general.

We will now explain the issue that ERC-6492 addresses, briefly touch on the technicalities of how ERC-6492 solves the problem, and end by explaining why it’s critical for key ecosystem projects such as Ethers and SIWE to adopt this new standard.

Terminology

For brevity, I will be using the following pairs of terms interchangeably, even though it’s not technically accurate:

  • “AA” and “ERC-4337”
  • “AA wallets” and “smart contract wallets”
  • “Wallets” and “accounts”

The Context

As I explained in a previous post, AA wallets are mostly compatible with existing DApps since AA transactions look no different than normal transactions from the perspective of the DApps, except for some edge cases.

Signature validation is a different story, however. As you know, many DApps require some form of signing; OpenSea for instance requires the user to sign a message before they can “log into” the DApp.

Since smart contract wallets have the flexibility to support different signing schemes, there isn’t a universal way to validate signatures by a smart contract wallet. Instead, there’s a standard ERC-1271 which defines a standard function isValidSignature on a smart contract wallet so that the verifier (e.g. OpenSea) can call the function to validate the signature, without needing to know specifically what signing scheme the wallet uses.

This is all fine and good, and in fact ERC-1271 as a standard enjoys wide adoption. Most popular DApps today, including OpenSea, already support it.

The Issue

With the rise of ERC-4337, smart contract wallets are becoming increasingly commonplace. One key optimization that ERC-4337 implements is counterfactual deployment — namely, that we can compute the address of the account before the underlying smart contract is actually deployed. As a result, a user can “create” a ERC-4337 wallet without paying the deployment cost, so they can start receiving assets, signing into DApps, etc. Only when the user sends their first transaction that the contract is actually deployed.

While counterfactual deployment is normally very desirable, it becomes an issue when the user needs to sign messages. To understand why, recall that in order to validate a signature from a smart contract wallet, the verifier needs to call isValidSignature on the wallet contract. However, since the wallet contract is not actually deployed, it’s impossible to call that function! As a result, an attempt to validate that signature will fail.

Consequences

So what does this mean for the users? It means that it’s impossible to validate signatures from ERC-4337 wallets until they are deployed. Therefore, for a new ERC-4337 wallet that has not sent any transactions, it’s impossible to, say, sign into OpenSea or any DApp that uses SIWE.

This is very bad because users who are new to Web3 want to sign into DApps and look around before they spend any money on gas. Forcing a user to pay some gas to deploy their wallets before they can see a DApp would be a major step backwards comparing to the EOA experience today, where you can sign into DApps even from an empty account.

Solution

ZeroDev first encountered this problem when we were developing our WalletConnect integration and realized that we couldn’t sign into OpenSea until we deployed the wallet, which led to a lengthy discussion with many smart people in the 4337 ecosystem. Eventually, Ivo from Ambire came up with a great solution that turned into ERC-6492.

On a high level, ERC-6492 works by using a UniversalSigValidator contract that validates a signature as such:

  • Check if the signature ends with a sequence of magic bytes, which indicate that the signature is for a not-yet-deployed contract.
    • If so, the signature itself contains all the data necessary for deploying the contract, which comes down to an account factory address and the calldata for the factory.
    • UniversalSigValidator would then proceed to deploy the contract and calls isValidSignature on it to validate the signature.
  • If the magic bytes are not detected, then proceed as normal, which means:
    • Check if there’s contract code at the address. If so, proceed with ERC-1271.
    • Otherwise, assume that the account is an EOA and perform a ecrecover.

But wait! You might say. The signature verifier has to deploy the contract if it doesn’t exist? Isn’t that incurring a lot of cost for the verifier?

The answer is no because the verifier will be using eth_call, which essentially simulates the transaction without actually executing it on-chain.

Next Steps

So who needs to implement ERC-6492? In short, it’s whoever that needs to verify signatures, which is mostly DApps.

However, DApps don’t write everything from scratch. In fact, there are a few libraries that most DApps use for handling signatures, so if these libraries adopt ERC-6492, DApps would get to support ERC-6492 “for free.” Some of these key libraries are:

If you want to see the space move towards AA and smart contract wallets, there are a few things you can do:

  • Upvote these PRs
  • Make your own PRs to libraries that validate signatures
  • And of course, if you are building a DApp, make sure that it can handle SCW signatures! DApps that work seamlessly with SCW will have an inherent advantage comparing to those that don’t, since more and more traffic are moving to SCW everyday.

· 8 min read
Derek Chiang

At ZeroDev, it’s our job to help devs learn and adopt AA, so naturally we have come across a lot of questions, concerns, and objections.

In this post, I’d like to summarize some common pushbacks against ERC-4337 and AA in general, and I will group them into three categories:

  • Misconceptions: things that are just not true.
  • Yes and no: somewhat true, but the reality is nuanced.
  • Valid concerns: real issues that need to be addressed.

Let’s dive in!

Misconceptions

AA is no big deal because SCW has been around for years

Without AA, smart contract wallets like Safe/Argent are not “first class citizens” on the blockchain, meaning that you cannot initiate transactions directly from them. Rather, you have to do one of the following:

  • Call the SCW from a EOA, so you STILL have to own a dumb wallet before you can own a smart one.
  • Rely on a centralized relaying service to relay your transactions, which exposes you to risks like censorship, downtime, etc.

With AA however, you can directly send transactions from a SCW, the same way you can directly send transactions from MetaMask. This makes it possible to use a SCW as your only wallet, which is precisely what the next billion Web3 users will do.

You still need a EOA to own an AA wallet

This common point of confusion stems from the fact that most AA wallets today are owned by a private key (just like EOA wallets), but it’s misguided because:

  • Private key ≠ EOA. While it’s true that each private key has a corresponding EOA, the key itself is just that — a key that can sign things. A typical AA wallet will store and safeguard a private key just like MetaMask does, and use the key to sign transactions for the smart contract account. The corresponding EOA, to the extent that it exists, is only used as a public key for validating signatures.
  • Since AA enables transactions to be validated with a smart contract, the validation logic can be arbitrary, so you don’t technically even need a private key. Here’s a proof of concept using fingerprints instead of private keys.

We don’t need AA if we have MPC

The best way to think about MPC vs AA is:

  • MPC improves the key management experience.
  • AA improves the transaction experience.

With MPC, you effectively have a “virtual private key” without ever having to store it somewhere, which is a huge improvement over the status quo of having to write down a 12-word seed phrase.

AA is about what happens when you send a transaction — who pays gas? What tokens are used to pay for gas? Who signs the transaction? All of these can be abstracted away with AA.

As you can see, MPC and AA actually complement each other nicely — MPC saves the user from having to deal with keys, while AA makes transactions smooth. In fact, it’s precisely by combining MPC with AA that we are able to offer social AA wallets.

Yes and No

AA transactions are more expensive

Since AA uses smart contract wallets, each transaction necessarily has some overheads comparing to the equivalent EOA transaction. There’s also the cost of deploying the smart contract wallet on-chain.

However, multiple factors lower the transaction cost in AA’s favor:

  • SCW has the ability to batch transactions, so what normally takes multiple transactions with EOA, may only take one transaction with a SCW. A classic example is when you interact with a DeFi protocol, where each action typically involves multiple transactions (e.g. approve → swap → deposit). In AA, all these can be done in one atomic transaction, thus saving gas.
  • ERC-4337 supports signature aggregation, so that multiple AA transactions can effectively “share” a signature, thus lowering the cost for each transaction. Here are some numbers from Vitalik.
  • ERC-4337 does not deploy the smart contract account until the user’s first transaction. Before then, the account exists “counterfactually” — it has an address even though it’s not really deployed. So your users can receive assets even without paying any deployment cost.

As a result, whether AA transactions or normal transactions cost more gas actually depend on the workload. For some applications (notably DeFi), AA transactions might wound up being cheaper!

AA is not ready for production

There’s no doubt that anyone building something on AA/ERC-4337 today is a trailblazer — there are not many prior examples to look to or patterns to borrow from. In that sense, building something on AA certainly involves more technical risks than building a classic DApp.

However, everything you need to build a full AA application, notably ERC-4337 itself, is already running in production/mainnet. We are at an inflection point where you are either building one of the last non-AA applications, or one of the first AA applications. The choice is yours.

AA is not compatible with existing DApps

Before AA, there was “meta transactions” that could remove gas (or pay gas in ERC20s) by using relayers that submit transactions on users’ behalf. The main problem, however, was that DApp contracts had to use a helper function like _msgSender() instead of the more intuitive msg.sender to get the address of the transaction sender. Needless to say, most DApps did not do that, so the compatibility of meta transactions were severely limited.

AA does not have this problem, however, which makes it compatible with the vast majority of DApps. Where the compatibility breaks down, however, is when the DApp asks the wallet to sign a message. It turns out that EOA signatures and smart contract wallet signatures cannot be verified the same way, so there’s a standard ERC-1271 that DApps are supposed to implement to be compatible with smart contract wallets. Here’s an incomplete and likely outdated list of DApps grouped by whether they support ERC-1271.

If a DApp requires message signing but doesn’t support ERC-1271, then AA indeed won’t work with the DApp. Fortunately, the space is completely aligned that ERC-1271 needs to be supported, and new DApps being written today typically support ERC-1271 by default if they use libraries like OpenZeppelin.

ERC-4337 is not real AA. We should wait for real AA

When someone says that ERC-4337 is not “real” AA, they are typically referring to the fact that ERC-4337 is NOT integrated into the blockchain protocol itself. In contrast, new networks like zksync and StarkNet have “enshrined” AA as a part of their protocols.

The reason why Ethereum and most other EVM chains have not enshrined AA is two-fold:

  • Enshrining AA will be protocol-breaking, and therefore require a hard fork.
  • There’s no consensus over the best approach to implement AA, so it’s not even clear what we should be enshrining.

Enshrining AA into the protocol itself also means that every EVM chain has to implement this breaking change, which can take a very very long time. In contrast, since ERC-4337 is implemented as smart contracts, deploying to a new chain is literally a matter of deploying a few smart contracts. That’s why ERC-4337 is already running on all EVM chains today.

In any case, the distinction between “real AA” and “ERC-4337 AA” matters little to the end users. From their perspective, their transactions “just work” either way. Therefore, given the level of community buy-in for ERC-4337, it’s our best hope for achieving AA on EVM blockchains in the near term.

Valid Concerns

ERC-4337 is fairly centralized right now

In theory, ERC-4337 is designed such that anyone can spin up relayers (aka “bundlers”), unlike previous relayer networks that are typically run by a single entity.

In practice, however, most bundler implementations except for StackUp are not production-ready, so most ERC-4337 traffic is going through StackUp today. This is not unlike how most Ethereum traffic is going through Geth. Hopefully, this will change as other bundlers go into production.

ERC-4337 may still change

While ERC-4337 has been deployed on mainnet, it’s not technically finalized. The EIP is still in draft status, and the core team has acknowledged that the EIP and the smart contracts could still change.

Fortunately, it’s expected that any changes to the EIP and core smart contracts won’t affect the core account interface, so wallets that are compatible with ERC-4337 today will most likely still be compatible with ERC-4337 in the future.

ERC-4337 has not been formally verified

While ERC-4337 has been audited, it has NOT been formally verified, so one cannot completely rule out the possibility that there are some critical security issues.

Fortunately, there are teams working on the formal verification of ERC-4337 (with our very own taek being a major contributor). When ERC-4337 has been formally verified, that’s when we expect to see it finalized.

The Bottom Line

Anyone building on AA today is a trailblazer taking on technical risks, no doubt about it. But with risk comes reward — if properly executed, your project will dwarf your competition in terms of usability.

At ZeroDev, we’ve developed an AA framework that dramatically shortens the time — and reduces the risks — for devs to build wallets and DApps on AA. Check out ZeroDev and start building the future of Web3 today!