Hybrid Custody

In the world of consumer blockchain, there are two common models for blockchain key management: user custody (or self custody) and application custody.

User custody is considered the default and best option by the web3 community. The user keeps their own key(s) on a device in their control (typically a laptop or phone), and access to that key is managed by software called “a wallet”. Applications that provide blockchain services to the user make requests to the wallet, and the wallet examines and presents the request to the user for approval before executing. This has the advantage of being self-sovereign and it gives the user absolute control over their assets, while providing them full protection from lost assets due to failed or attacked service providers.

For users new to web3, setting up self-custody is confusing and creates friction. The user is expected to install and configure their wallet software before they have even used the application that requires it. If you’ve been in the web3 space for a while, you might not remember the confusion of being asked to remember twelve random words and then authorizing “full access to all websites” to a bit of software you’ve never heard of before. This is even worse on mobile, where users are accustomed to getting started without any kind of account creation (especially not one that requires a second application to be installed!). Dapper Wallet and others have done their best to streamline this process, but data shows that it’s still a major drop-off point for user on-boarding.

In a desire to remove this friction, a large number of application developers are providing custody for their users. “App custody” gives users a very natural, web2-style on-boarding experience, and allows the application vendor to sell their digital products and services using traditional payment methods (e.g. credit card on web or native in-app purchases on mobile). This approach is especially attractive for mobile and for companies that already have a customer-base with account login (like Netflix or Spotify).

App custody has another benefit: The application doesn’t need to pester the user for permission via the wallet. The app software can manage the contents of the account they created for the user directly. For example, if a user buys an on-chain item using in-app purchases (IAP)in a mobile game, they don’t want to have to jump back to their wallet to approve every interaction with that item.

As valuable as app custody is in creating a great on-boarding experience and streamlining interactions with games and other application services, it causes each application to be an isolated island. The power of blockchain assets is that users can take them out of the experience that originally created them and use them in other, compatible apps, like peer-to-peer trading marketplaces or in new products that extend existing experiences.

One way of trying to bridge this gap is to ask users to start with app custody, and when they’re experienced enough in the web3 ecosystem to get themselves a self-custody wallet, they can transfer their assets out of the app-managed account into their private wallet.

This introduces its own difficulties. The world inside the app (the app-custody account) and the world outside the app (the user-custody account) are disconnected. The app world doesn’t see – and can’t even request access to – the assets in the user-custody account. And the user can’t use the world of web3 applications without removing them from the app-custody account, where they are presumably quite useful! And worst of all, if an app provider shuts down the user can lose all assets in the app-custody account, with no possibility of recovery.

Fortunately, there’s an approach that gives us the best of both worlds.

By using account abstraction – specifically, account delegation – one blockchain account (the “child account”) can delegate control to another account (the “parent account”). The child account can be accessed using cryptographic keys, as per usual, but the owner of the parent account can directly access the assets of the child, as well. Critically, the account granting the delegation (the “child”) must explicitly provide this access, and can revoke this access at any time for any reason.

This connection lets us create a hybrid of app custody and self custody that provides the full benefits of both. A new user, without a self-custody account, can sign up for a new account without having any wallet software. But when they are ready for self custody, they can delegate control of the app-custody account to their self-custody account. Using their own keys, on their own devices, they can access all of the assets in both accounts, while still allowing the application to manage the assets in the child account without having to bring up the wallet software.

Since this delegation relationship is tracked on-chain, the application can see the connection to the parent account, and can see the assets in that account and other children accounts associated with it. It can request temporary access to those assets from the wallet software (using a standard transaction-signing request), or even propose a moving those assets into the app-custody account to allow for continuous access.

11 Likes

Do you plan to introduce account abstraction in Flow blockchain design ?

It’s already available, hybrid custody would build on that

2 Likes

This has many benefits, I was a bit hesitant in the beginning about security side of things, but after reading about use cases and motivation, I think it can be implemented securely without losing any of the benefits.

Some benefits I see:

  • This will be great for developers: Capability model was too confusing to many of the newcomer developers, actually it was even complex for all ( me included )

  • Can provide great isolation between dapps.

  • Not requiring signing and allowing dapp to run action on behalf of the user without interaction is great UX. This can even make FCL obsolete.

  • it is great for onboarding but also makes dapps to have no excuse to have walled gardens anymore, so this has great benefit for users.

  • Wallets even auto create temp accounts when a dapp connected, link to main account, and add dapps public key ( so dapp and wallet can use the account at the same time )

  • Another good benefit is: now there is a misconception about each account is a user and each tx is user interacted, I think this will help to clear that misconception.

  • If we can somehow tag this child accounts to dapps, I think it can be a valuable metric about dapp usage etc.

I think here “Irrevocable access” can be also equally true. If dapp ( let’s say TopShot) will onboard me to a child account, I would like my parent account ( let’s say blocto ) should have irrevocable access to that child account ( unless I want to break the relation )

In general I am really excited about things we can do with this child accounts.

1 Like

All of the features called “Account Abstraction” in the Ethereum community were available from day 1 as part of Flow’s original design.

There is a FLIP currently being proposed that streamlines account delegation specifically to make it easier to use – and easier to audit – via AuthAccount Capabilities. This FLIP isn’t necessary for us to implement Hybrid Accounts, but I think it makes Hybrid Accounts much cleaner and would prefer we build Hybrid Accounts in this way before wallets and apps start supporting other methods. (It’d suck to have multiple competing mechanisms for Hybrid Accounts.)

1 Like

I don’t anticipate that it would make FCL entirely unnecessary.

For use-cases like marketplaces, you want to be able to access all of the assets in all connected accounts, and it totally makes sense to use the classic “app proposes and wallet approves” transaction flow that we are used to in web3.

I can totally imagine that applications like games could be built without ever interacting with a wallet via FCL, however!

I disagree. I mostly disagree because it adds complexity, but it doesn’t really add security, either.

If the controller of the child account becomes untrustworthy, they can always just move the assets out of the account; blocking them from revoking the delegation doesn’t really prevent malicious behaviour.

A new user, without a self-custody account, can sign up for a new account without having any wallet software. But when they are ready for self custody, they can delegate control of the app-custody account to their self-custody account.

Maybe I’m missing something, but does the app-custody account (aka the child account/new account) have an associated private key?

If the app-custody account has a private key, then who has access to it? The problem is premised on the idea that the new user can’t handle a private key yet, so does the application backend store it and not the user? If that’s the case, then it seems like a restricted child account in this use case doesn’t make sense, since the application has root access to the child account already through being able to create digital signatures for it. I understand that when the user “graduates”, they’ll have their own self-custody account with a separate private key that the application knows nothing about that will be the new parent of the app-custody account, but what’s happening with the app-custody account private key?

I imagined a bit different, app-custody account is dapp’s and created for user as a sandbox, later when I have my account, then I can move stuff from there (with authaccount capability)

Technically I can add my own key and remove dapp’s key from the account too. ( if I am adventurous )

This sub account model will resolve a number of plain point in web3 space. Although, app custody can provide a more seamless experience for the user. But it also means there is more work to do which was belonged to the wallet. For example, dApp how to generate and store private key is secure. And even they might need to create the flow account for the user. At the meantime, it could require high UX from both wallet side.

Some Questions:

  1. Does wallet need query ( all children accounts ) under parent account to find all assets belong the to user or only parent account is enough ?

  2. Let’s say a children parent is a game dApp account, I wanna purchase some game item but child account has insufficient balance, it possible to auto-delegate this payment to parent account or it still need transfer funds from parent account to child account first then proceed the payment.

  3. It will be nice to see some best practice examples to educate dApp developers how to manage App custody private key for their projects, such as like the Google KMS, Mobile Secure Enclave. Or I like Bluesign’s idea even wallet can provide temporary account generate function to the dApp.

  4. On flow account creation need submit a transaction to the chain. For now, there is no public RPC to create a main net account easily (Since, there is actually cost on it). Hence, even dApp know how to create the private key, they might still need build their own service for account creation.

  5. Is it possible to use account delegation to transfer an parent account to an new parent account ? Or by adding new key is more recommended ? For example, I wanna transfer my one wallet account (eg. Blocto) to my new wallet account (eg. Dapper self-custody).

And yes, I’m also really excited about child account is coming on flow !!

The best way to think about the child account is being subject to “joint custody”. The user (through the delegation) and the app (through the key) both have unrestricted access the contents of the child account.

Of course, if you don’t trust the app, then you should move the assets out of the child account and into the parent account (where only the user has access). The app can still see those assets in the parent account (through FCL), and can still propose transactions that need to be signed by the wallet managing the parent account in order to use them. This lets each user optimize between security and convenience.

You could do this, but as per Kate’s point just above, remember that the app has unrestricted access to the child account. If the child account has some kind of capability in it that can draw funds from the parent account, there’s a chance the app could steal those funds.

Again, I would suggest that the right tradeoff for most people would be to have some small amount of funds that they are willing to trust to the app developer in the child account, and then the app can use wallet authorization to refill the child account when it gets below a threshold.

Absolutely! The responsibility for managing the on-chain account falls to the app developer in this case. Fortunately, there’s already an open source project that makes this quite easy, complete with support for GCP and AWS key-management services. (It’s marked as “not maintained”, but I believe it works as expected, and is used by several live projects.)

I would expect that a wallet would want to show a user all assets in all accounts available.

This certainly feels like a solid iterative step in order to bridge app level custody and self custody. However, it doesn’t really take you to a long-term place where the user no longer has to see and sign for transactions, because it’s making the assumption that the parent app can have unlimited access into the child. That’s not really a state that I’d ever want to be in, even if I trusted the parent app. Maybe I’m misunderstanding things here, but it seems like this will be what happens for most apps and accounts:

  1. I sign up for a new app - let’s call it “Hybrid App A”. They give me a child account (“Child Account A”).

  2. I take over custody of the account “Child Account A”, but “Hybrid App A” retains access to this account since it’s under hybrid custody. Note: I understand that I can remove the access for “Hybrid App A”.

  3. Now, “Hybrid App A” can sign for anything on this account, even collections that are not from the original app or anything. I can query things, transfer them, and really anything.

To compare this to Web2, this would be like if I signed up with Google, the app I signed up to had full access to look at information I signed up for other apps with. And the way to get out of that is to remove Google-Sign in and use standard username/password (which is metaphorically going to full self-custody, with no delegated access).

That doesn’t seem great - let’s assume the parent app (“Hybrid App A”) is well-intentioned, still even simple bugs can transfer the wrong assets out of the accounts. I’d rather be in a world where I can onboard with app custody, add in a self-custody wallet later and then move items from app custody into self-custody.

@anir-niftory I can appreciate the concern around custodial risk, though I think something about the construction isn’t clear. When you assume control of the child account, both you and the app maintain control of the account, yes. However, your main account is still partitioned such that only you maintain access to it. Hybrid App A in your example only ever has access to that which is in Child Account A which would be used for actions you take within the Hybrid App A. So the flow would be this:

  1. I sign up for a new app and the app creates an account - ChildAccountA
  2. After some time, I connect my wallet, assuming shared control of ChildAccountA. Note - you now have two accounts: the main account you connected and ChildAccountA which your main account maintains a capability on
  3. Yes, Hybrid App A can sign for anything on the account. The point being that only that which is pertinent to the app is ever in the account, but you can also transfer assets from that account to your self-custody account which the app doesn’t have access to.

This allows for the situation where you onboard with app custody, as you mentioned. Once you connect your wallet & create the parent-child link, you can transfer those assets from the hybrid account to your self custody account even without needing to interface with the app if you choose to do so. Again because your main account now has a capability to Child Account A

Now imagine a world without hybrid custody where you have any number of apps that custody your assets and there’s no notion of linking them. It’s entirely up to you to manage them, remember which apps hold your assets, transfer them from the app account to your self-custody account, etc. because there’s no notion of on-chain relationships between your main and app accounts for a wallet to allow you to manage. That’s entirely unmanageable for any of us let alone a mainstream user.

With hybrid custody, you could leave the app-related assets in their respective hybrid app accounts, partitioned by where they’re useful (yes, with some custodial risk). But because of the on-chain relationship between your main (AKA parent) account and each child account, you can move assets among your network of accounts as you see fit and any wallet or dapp would be able to present you with a dashboard of that network of accounts and collective assets.

Does that make sense?

2 Likes

Is there a reason why we would need key-management services? I could be confused, but I’m still thinking that the child account shouldn’t have their own keys because all the control is coming from another account at every point.

Screenshot 2023-01-27 at 3.37.38 PM

I’m imagining that the flow is:

  1. app admin account exists
  2. new user starts using the app, app admin creates a new child account (with no keys associated) and makes its own account a parent
  3. The user “graduates” and decides to create a self-custody account.
  4. They make this self-custody account another parent of the child account. At this point, there are two parents. Both the app and the user have complete control of the child account.
  5. The user might optionally decide to revoke all access from the application.

@kate_sills there are a few advantages for an account to sign as its own:

  • you can see the transactions for an account; as every state modification will be a transaction.

  • normally dapp doesn’t need AuthAccount capability as they have the key. So transactions will not need capabilities. ( you can have a multisign transaction for multi-player transactions )
    Otherwise, you need to keep those capabilities and make a big map for User->Capability. It can be problematic.
    This means if dapp is hacked, all resources are gone. ( with this User->Capability map )

  • If dapp lets you to graduate, you can revoke dapp key later. ( or re-add when you want to use it again )

PS: Bigger problem is dapps will be wallet providers ( crypto asset holders on behalf of clients )

1 Like

I took a shot at visualizing this concept. I think this hybrid model solves a couple point points I’ve experienced multiple times. Can anyone comment if this looks right?

(The users’ full names are Mainstream Maddie and Early Market Earl)

4 Likes

this is very helpful.

i feel like for Maddie’s account, there would be a multiple party, multiple signer scenario (two keys, 1000 weight each)

the 2 keys for Maddie where the app uses Key #1 to sign and Key #2 is an escape hatch for Maddie to revoke which can take the form of an emailed “recovery kit”

1 Like

Thanks, updated to “app key mgmt” to reflect it’s whatever the app decides.

Also wrote a short post on how it solves challenges I’ve faced trying to grow within this ecosystem: The Custody Chasm - by Brian Astrove - Brian’s Bulletin

1 Like

This is a great full-context diagram, and a solid read. The “Custody Chasm” is the perfect way to put the problem Hybrid Custody solves.

In your diagram, it looks like account access to Earl’s app account is delegated via keys, and this does enable shared access to the app-custody wallet. The downside here is that dApps would have to rely on wallet providers to maintain and provide knowledge of an authenticated user’s linked accounts - the 3rd party marketplace in this example. Additionally, re-keying the main account would require re-keying all linked accounts. Relying on wallet providers to enable linked-account management poses the risk of a lack of interoperability, fragmented user experience, and a requirement that they buy in to the business case for this feature to begin with.

IMHO the smoothest road to Flow-wide portable hybrid custody is with an agreed upon contract-level standard that relies on AuthAccount Capability linking over key-based delegation. Full-disclosure, I have a smart contract engineer’s bias so no surprise I think the contract hammer can fix the problem. I believe a standardized set of contract defined resources solves the problem of portable linked accounts and relying on AuthAccount Capablities over keys means that users can re-key their main account without risking loss of their linked accounts. It also means that interoperability across wallets & dApps is as simple as leveraging scripts & transactions against the standardized resource, preventing wallet lock-in for the sake of this powerful feature.

I’ll take this opportunity to call out the recently submitted FLIP going over this construction - would really appreciate feedback on this idea and prototype!

Just to try to drive home the idea, it’s really a small change to the diagram you posted (forgive the quick hand drawing!). The difference is that the app maintains the key for Earl’s child account & Earl accesses the account via a stored AuthAccount Capability - more on that linking process here

As a sidenote, you mentioned the problem of airdrops in your article - this model could be useful as a new airdrop mechanism. You create and custody keys for accounts users can access with their Web2 identities. When they want to migrate outside of your airdrop app, they can link their wallet-custodied account and either transfer or simply maintain access for later use. You can still airdrop additional tokens to the account your app created & custodied knowing they can access assets as they wish.

3 Likes

@brianastrove that was a great read, I envy people like you can write in such flow.

I think situation is a bit different in reality though, nobody wants to give up custody voluntarily, neither brands nor market places or dapps. So I think situation is more like: how to convince Maddie to give us control despite Earl working at IT at Maddie’s company defending ‘not your keys not your money’

I mean maybe we are sugar coating it a bit, we are saying she doesn’t want to, instead of she isn’t capable of etc. or we are saying you can move your stuff anytime, but we will punish you for it :slight_smile:

Anyway I stopped myself for writing next fight club manifesto :slight_smile:

2 Likes

Wouldn’t it be preferable to restrict app custody to certain permissions only? Or to make app custody and transactions conditional on the users authorizing use of their account (“just-in-time” access without a web3 wallet)?

As it may sound abstract, I’ll provide an example I have in mind. LocalCryptos has provided non-custodial wallets with web2 on-boarding starting in 2017. The user would sign in to the app with a password and 2FA, and then the user could execute blockchain transactions on the web2 app with the app as a relay. For example, funding an escrow for a sale of crypto required no web3 wallet interaction, only clicking on a button. In summary, the app utilized the Web Cryptography API to enable cryptographic operations in the browser (web3 wallet interactions as frontend interactions) and had some centralized components (e.g., the relay). The app never had custody of funds or knowledge of user keys, but had an encrypted “AccountKeyMaster” key which was stored in the browser after user authentication.

I’m not familiar with Flow’s technical architecture to provide more insight, but I just wanted to throw this here and advocate for apps to be as non-custodial as practicable.