Web3 onboarding is terrible. How to make it better with Account Abstraction and Flow.
Tags: web3, tutorial, architecture, ux
Published: 2023-04-19 12:51:24

If you’re a developer working on a web3 app, you know that onboarding mainstream users into web3 is difficult.

Even with the promise of truly owning your data, making near-free worldwide payments, and using a censorship-free system, **the current process of creating and using a digital wallet is just too hard.**

Fortunately, there’s a better way. **Account abstraction** promises to streamline the process, making user onboarding seamless and simple. [Flow](https://flow.com/)—a decentralized, public, layer-one blockchain designed for building web3 experiences for mainstream users—has used account abstraction to combine a [hybrid custody](https://flow.com/post/flow-blockchain-mainstream-adoption-easy-onboarding-wallets) model with **Walletless Onboarding** to create an onboarding that is easy, simple, and as straightforward as traditional apps.

Let's dive into the details to see how Hybrid Custody and Walletless Onboarding work together: the architecture, the code, and what we can expect moving forward. 

## Why web3 onboarding is so difficult

Digital wallets bring all the benefits of web3, including the assurance that no one can control your assets. There are no middlemen such as banks taking a percentage of your money, or third parties dictating what you can do. However, because most wallets are “self custody” (meaning only you have access), you—and only you—are in charge of your keys. There is no customer support if you make a mistake.

This means you must store your keys safely. Don’t store them in clear text on your computer where they can be hacked. And don’t write them down where someone can read them. And don’t lose them, because there is no reset password in web3, so you’ll lose _everything_. You can see the issues. 

In addition to the challenges of self custody, you also need to understand tokens and payments. How do you pay for your transactions? What tokens do you need? How do you move from fiat to tokens? How do you transfer the tokens once you have them? Oh, you made a typo when you sent your tokens? Sorry, they’re lost forever. Again, you can see how difficult this all is.

With all these obstacles, it’s easy to see why many people don’t make it past step one of signing up for your app.

## Custodial wallets: great UX, lacking ownership

Developers tried to solve this issue with **custodial wallets**. These wallets manage the keys for you and have many benefits. 

Signing up is easy. You can use an email and password, as you would for a traditional service, and the wallet provider takes care of the rest. If you lose your login credentials, your keys are still accessible on the provider's servers – you can simply request a password reset. Since the wallet software runs on their servers, providers can even pay your [transaction](https://developers.flow.com/cadence/language/transactions) fees. You don’t have to understand tokens, funding, and transfers. 

But as the saying in web3 goes: “Not your keys, not your coins!” While custodial wallets solved many onboarding issues, the wallet providers, in the end, owned the keys. We’re right back to the problems we wanted to get away from: a centralized entity that can prevent you from using your tokens, can close your account at a whim, or can shut down completely, taking your funds with them.

Wouldn’t it be nice if you could have the best of both worlds?

## Account abstraction to the rescue!

**Account abstraction** solves one of the fundamental issues of blockchain networks: the distinction between externally owned accounts and contract accounts.

An **externally owned account** (EOA) is an account controlled by the user with a private key. These are the traditional web3 wallets that we talked about above. An EOA has a public address and initiates transactions (such as sending tokens) with the blockchain. 

A **contract account** (which is also just a smart contract) is a program deployed on-chain and executed by the network nodes. Contract accounts don’t initiate transactions. Instead they respond to a transaction with an action of their own. They have no private key—they are controlled by their code. This type of account could be said to have **app-custody**.

Account abstraction combines app-custody and self-custody into a new user experience (UX), giving users the benefits of both: control of their wallets, while abstracting away the wallet (and blockchain) altogether.

On Flow, account abstraction is the basis for Walletless Onboarding using Hybrid Custody.

## Hybrid Custody gives you the best of both worlds

**Walletless Onboarding** allows developers to create an onboarding experience for users that is nearly seamless.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wokwlkelyqskelyrediw.png)
           _The walletless user journey (from flow.com)_

The hybrid custody model uses account abstraction to allow **account delegation**—where a child account delegates control to a parent account through a [Capability](https://cadence-lang.org/docs/language/capabilities) (a communicable, unforgeable token of authority), essentially allowing one account to control another.   

Now, the self-custody account can be a parent account, giving permissions to the app account, which is now a child account. With this setup, the accounts work _together_, and your app can work like this:

- Your app creates the wallets for the users. There is no barrier to entry here—no key management, no signing transactions, no understanding of blockchain. Users can use your app, and buy and sell assets, without knowing or caring about the technical details. Your app can even subsidize transaction fees or let them fund their account with fiat such as USD. And it all happens invisibly to the user.

- Once the user comes back with their own wallet-managed account, your app can delegate access to the user’s account. Once linked, the user has hybrid custody – their wallet manages their main (now “parent”) account which secondarily maintains access on the app-created (now “child”) account.

- Since their main account now has access on the app-created account, they can bring app assets anywhere their wallet-managed account goes. They can trade your app’s assets on marketplaces or share them with friends. Yet, at the same time, since the app also has access to the account, it can still facilitate seamless UX that acts on their behalf.

With this hybrid custody model, users get to experience your app right away, with minimal barriers and no risk. And later on, if they choose, they can take control of the self-custody account themselves, enjoying all the real ownership and self-sovereignty it brings.

## Technical details of hybrid custody and account abstraction

**Account Creation**
How does all this work?

On [Ethereum](https://ethereum.org/en/) and many other blockchain networks, an EOA is created from a public key. The key is hashed, and the first 160 bits of this hash become the address of the account controlled by that key. This makes account creation independent from the network but irrevocably ties an account to that key.

Flow uses a particular on-chain function to create account addresses. You can then add any number of keys to a Flow account, adding and removing access based on the custodian of each key. This means a transaction is required to create a new address—so you can’t create accounts offline—but it also makes Flow account ownership much more flexible.

You can see [a full account creation example on GitHub](https://github.com/onflow/flow-go-sdk/blob/master/examples/create_account/main.go), but let’s check out the crucial parts here:

```
[userPrivateKey := examples.RandomPrivateKey()
userPublicKey := flow.NewAccountKey().
  FromPrivateKey(userPrivateKey).
  SetHashAlgo(crypto.SHA3_256).
  SetWeight(flow.AccountKeyWeightThreshold)

serviceAccountAddress, serviceAccountKey, serviceSigner := 
  examples.ServiceAccount(flowClient)

createAccountTx, _ := templates.CreateAccount(
  ]*flow.AccountKey{userPublicKey}, 
  nil, 
  serviceAccountAddress
)


createAccountTx.SetProposalKey(
  serviceAccountAddress,
  serviceAccountKey.Index,
  serviceAccountKey.SequenceNumber
)


createAccountTx.SetReferenceBlockID(
  examples.GetReferenceBlockId(flowClient)
)


createAccountTx.SetPayer(serviceAccountAddress)

_ = createAccountTx.SignEnvelope(
  serviceAccountAddress,
  serviceAccountKey.Index,
  serviceSigner
)
_ = flowClient.SendTransaction(ctx, *createAccountTx)
```


First, the user creates a key pair. Pretty basic stuff. 

Because on Flow, you create accounts by sending an account creation transaction, we need an existing account to send this transaction. This housekeeping is the job of the `ServiceAccount`. 

The `createAccountTx` receives the user’s public key to control the new account later, but the `ServiceAccount`, which signs and pays for the transaction, handles the rest. In the case of your app, you would own this service account so you could sign account creation transactions on behalf of your user, also funding the account’s creation.

## Hybrid Custody

Let’s get back to the hybrid custody model. Your app can create app accounts for your users and link them to a public key that your app controls. The app can then handle your user's transactions and assets and even sponsor their transaction fees, just like any custodial account would do. 

Later, when a user decides to dive deeper into the web3 ecosystem and get their own wallet, you can create and save a link to the app-custodied account’s [AuthAccount Capability](https://cadence-lang.org/docs/language/accounts#authaccount), and delegate control to the user’s wallet-managed account. 

You can see an example of this in the [walletless arcade example](https://github.com/onflow/walletless-arcade-example) where a multisig transaction is executed by the app to link a user's account to a Walletless Onboarding, dapp-custodied account to the signing user’s wallet-managed account. 

## Summary

It’s worth driving the point home that delegating access on the app-created account is done via a linked Capability on the account which is then provided to the user. This mechanism for account delegation is completely new to blockchain accounts, and is only possible because of the account abstraction that’s been native to Flow for years now. This language API account linking feature is a huge advantage to accounts on Flow, in addition to many other features – m-of-n multisig, re-keying accounts, etc. – which are only now beginning to be realized on other chains with their nascent, non-native account abstraction implementations.

Account abstraction is a powerful feature. On [Flow](https://flow.com/), it enables a hybrid custody model that allows users to enjoy the benefits of a custodial account when they sign up, while enabling them to take the account into self-custody at any time. It’s a new – and vastly improved – user experience that hopefully brings millions more users into web3.