Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
179 changes: 102 additions & 77 deletions docs/base-account/improve-ux/sub-accounts.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -7,32 +7,26 @@ import { GithubRepoCard } from "/snippets/GithubRepoCard.mdx"

## What are Sub Accounts?

Base Account's self-custodial design requires a user passkey prompt for each wallet interaction, such as transactions or message signing. While this ensures user awareness and approval of every wallet interaction, it can impact user experience in applications requiring frequent wallet interactions.

To support Base Account with user experiences that need more developer control over wallet interactions, we've built Sub Accounts in conjunction with [ERC-7895](https://eip.tools/eip/7895), a new wallet RPC for creating hierarchical relationships between wallet accounts.

Sub Accounts allow you to provision wallet accounts that are directly embedded in your application for your users. You can control when a Sub Account is created and interact with them just as you would with another wallet via the wallet provider or popular web3 libraries like OnchainKit, wagmi, and viem.
Sub Accounts allow you to provision app-specific wallet accounts for your users that are embedded directly in your application. Once created, you can interact with them just as you would with any other wallet via the wallet provider or popular onchain libraries like OnchainKit, wagmi, and viem.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I got feedback from the community that explanations of how our features work and how they can useful to them are appreciated so if you're able to put more details in this section, specifically:

  • how do sub accounts work under the hood?
  • perhaps even pointing to links to the smart contracts implementation repos

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this conflicts a bit with the feedback i received about the intro being too long and abstract, which is why i extracted the technical details which aren't necessarily relevant to the implementer into a Technical Details section


These Sub Accounts are linked to the end user's Base Account through an onchain relationship. When combined with our [Spend Permission feature](/base-account/improve-ux/spend-permissions), this creates a powerful foundation for provisioning and funding app accounts securely, while giving you ample control over building the user experience that makes the most sense for your application.
<Note>
Looking for a full implementation? Jump to the [Complete Integration Example](/base-account/improve-ux/sub-accounts#complete-integration-example).
</Note>

## Key Benefits

- **Seamless UX**: Reduce user friction by eliminating repeated signing prompts
- **Developer Control**: Manage when and how Sub Accounts are created
- **Secure Relationships**: Onchain linking between main account and sub accounts
- **Spend Permissions**: Control what Sub Accounts can spend and when
- **Easy Integration**: Works with existing web3 libraries and tools
- **Frictionless transactions**: Eliminate repeated signing prompts for high frequency and agentic use cases or take full control of the transaction flow.
- **No funding flows required**: Spend Permissions allow Sub Accounts to spend directly from the universal Base Account's balance.
- **User control**: Users can manage all their sub accounts at [account.base.app](https://account.base.app).

<Note>
If you would like to see a live demo of Sub Accounts in action, check out our [Sub Accounts Demo](https://sub-account-demo.com).
If you would like to see a live demo of Sub Accounts in action, check out our [Sub Accounts Demo](https://sub-accounts-fc.vercel.app).
</Note>

<Tip>
**Pair with Spend Permissions**
**Spend Permissions**

In order to make your UX more seamless, you can pair Sub Accounts with the
[Spend Permissions](/base-account/improve-ux/spend-permissions)
to make transactions on behalf of the user.
Sub Accounts are optimized for use with Spend Permissions to allow your app to take advantage of the user's existing Base Account balances. See the [Spend Permissions](/base-account/improve-ux/spend-permissions) guide for more information about how they work.
</Tip>

## Installation
Expand All @@ -57,11 +51,32 @@ bun add @base-org/account
```
</CodeGroup>

## Basic Setup
## Quickstart

The fastest way to adopt Sub Accounts is to enable automatic Sub Accounts by setting `subAccounts.mode` to `auto` in the SDK configuration.

```tsx
const sdk = createBaseAccountSDK({
// ...
subAccounts: {
mode: 'auto',
}
});
```

This will automatically create a Sub Account for the user when they connect their Base Account and transactions will automatically be sent from the Sub Account unless you specify the `from` parameter in your transaction request to be the universal account address. Spend Permissions will also be automatically requested for the Sub Account as your app needs them.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would great if you're able to show the user flow here: How does it look from a user perspective? A screenshot of the pop up window would do it

<Tip>
We recommend using a [Paymaster](/base-account/improve-ux/sponsor-gas/paymasters) to sponsor gas to ensure the best user experience when integrating Sub Accounts. You can set a paymaster to be used for all transactions by configuring the `paymasterUrls` parameter in the SDK configuration. See the [createBaseAccount](/base-account/reference/core/createBaseAccount#param-paymaster-urls) reference for more information.
</Tip>

## Using Sub Accounts

If you don't want to use automatic Sub Accounts, you can create and manage them manually.

### Initialize the SDK

First, set up the Base Account SDK with Sub Account support:
First, set up the Base Account SDK. Be sure to customize the `appName` and `appLogoUrl` to match your app as this will be displayed in the wallet connection popup and in the account.base.app dashboard. You can also customize the `appChainIds` to be the chains that your app supports.

```tsx
import { createBaseAccountSDK, getCryptoKeyAccount } from '@base-org/account';
Expand All @@ -78,11 +93,16 @@ const sdk = createBaseAccountSDK({
const provider = sdk.getProvider()
```

## Using Sub Accounts
### Create a Sub Account

### Create a New Sub Account
<Tip>
Make sure to authenticate the user with their Base Account before creating a Sub Account.
For that, you can choose one of the following options:
- Follow the [Authenticate users](/base-account/guides/authenticate-users) guide
- Simply use `provider.request({ method: 'eth_requestAccounts' });` for a simple wallet connection
</Tip>

Create a Sub Account for your application using the provider RPC method:
Create a Sub Account for your application using the provider's [wallet_addSubAccount](/base-account/reference/core/provider-rpc-methods/wallet_addSubAccount) RPC method. When no `publicKey` parameter is provided, a non-extractable browser CryptoKey is generated and used to sign on behalf of the Sub Account.

```tsx
// Create sub account
Expand All @@ -100,20 +120,9 @@ const subAccount = await provider.request({
console.log('Sub Account created:', subAccount.address);
```

<Tip>
**Tip:**

Make sure to authenticate the user with their universal account before creating a Sub Account.
For that, you can choose one of the following options:
- Follow the [Authenticate users](/base-account/guides/authenticate-users) guide
- Simply use `provider.request({ method: 'eth_requestAccounts' });` for a simple wallet connection

</Tip>

Create a Sub Account using the SDK convenience method:
Alternatively, you can use the SDK convenience method:

```tsx
// Create new sub account
const subAccount = await sdk.subAccount.create();

console.log('Sub Account created:', subAccount.address);
Expand All @@ -125,38 +134,9 @@ This is what the user will see when prompted to create a Sub Account:
<img src="/images/base-account/SubAccountCreation.png" alt="Sub Account Creation Flow" style={{ width: '300px', height: 'auto' }} />
</div>

### Import an existing Sub Account

If you already have a deployed Smart Contract Account, you can import it as a Sub Account using the provider RPC method:

```tsx
const subAccount = await provider.request({
method: 'wallet_addSubAccount',
params: [
{
account: {
type: 'deployed',
address: '0xYourSmartContractAccountAddress',
chainId: 8453 // the chain the account is deployed on
},
}
],
});

console.log('Sub Account added:', subAccount.address);
```

<Note>

Before the Sub Account is imported, you will need to add the Base Account address as an owner of the Sub Account. This currently needs to be done manually
by calling the [`addOwnerAddress`](https://github.com/coinbase/smart-wallet/blob/a8c6456f3a6d5d2dea08d6336b3be13395cacd42/src/MultiOwnable.sol#L101) or [`addOwnerPublicKey`](https://github.com/coinbase/smart-wallet/blob/a8c6456f3a6d5d2dea08d6336b3be13395cacd42/src/MultiOwnable.sol#L109) functions on the Smart Contract of the Sub Account that was imported and setting the Base Account address as the owner.

Additionally, only Coinbase Smart Wallet contracts are currently supported for importing as a Sub Account into your Base Account.
</Note>

### Get Existing Sub Account

Retrieve an existing Sub Account using the provider RPC method:
Retrieve an existing Sub Account using the provider's [wallet_getSubAccounts](/base-account/reference/core/provider-rpc-methods/wallet_getSubAccounts) RPC method. This will return the Sub Account associated with the app's domain and is useful to check if a Sub Account already exists for the user to determine if one needs to be created.

```tsx
// Get the universal account
Expand All @@ -181,10 +161,9 @@ if (subAccount) {
}
```

Get the Sub Account associated with the current app using the SDK convenience method:
Alternatively, you can use the SDK convenience method:

```tsx
// Get sub account
const subAccount = await sdk.subAccount.get();

console.log('Sub Account:', subAccount);
Expand All @@ -193,29 +172,32 @@ console.log('Sub Account:', subAccount);

To send transactions from the connected sub account you can use EIP-5792 `wallet_sendCalls` or `eth_sendTransaction`. You need to specify the `from` parameter to be the sub account address.



<Tip>
**Tip:**
When the Sub Account is connected, it is the second account in the array returned by `eth_requestAccounts` or `eth_accounts`. `wallet_addSubAccount` needs to be called in each session before the Sub Account can be used. It will not trigger a new Sub Account creation if one already exists.

When the sub account is connected, it is the second account in the array returned by `eth_requestAccounts` or `eth_accounts`.
If you are using `mode: 'auto'`, the Sub Account will be the first account in the array.
</Tip>

First, get all the accounts that are available, of which the sub account will be the second account:

```tsx
// Get the sub account address
const [universalAddress, subAccountAddress] = await provider.request({
method: "eth_requestAccounts", // or "eth_accounts" if already connected
params: []
})
```

Then, send the transaction from the sub account:

**`wallet_sendCalls`**

// wallet_sendCalls
```tsx
const callsId = await provider.request({
method: 'wallet_sendCalls',
params: [{
version: "2.0",
atomicRequired: true,
from: subAccountAddress,
from: subAccountAddress, // Specify the sub account address
calls: [{
to: '0x...',
data: '0x...',
Expand All @@ -229,13 +211,15 @@ const callsId = await provider.request({
})

console.log('Calls sent:', callsId);
```

**`eth_sendTransaction`**

// eth_sendTransaction
```tsx
const tx = await provider.request({
method: 'eth_sendTransaction',
params: [{
from: subAccountAddress,
from: subAccountAddress, // Specify the sub account address
to: '0x...',
data: '0x...',
value: '0x...',
Expand All @@ -245,12 +229,45 @@ const tx = await provider.request({
console.log('Transaction sent:', tx);
```

We recommend using `wallet_sendCalls` in conjunction with a paymaster to ensure the best user experience. See the [Paymasters](/base-account/improve-ux/sponsor-gas/paymasters) guide for more information.

## Advanced Usage

### Import an existing account
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you clarify what is the difference between "importing" a sub account and "getting" an existing sub account?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

have updated the line below it to be more explicit


If you already have a deployed Smart Contract Account, you can import it as a Sub Account using the provider RPC method:

```tsx
const subAccount = await provider.request({
method: 'wallet_addSubAccount',
params: [
{
account: {
type: 'deployed',
address: '0xYourSmartContractAccountAddress',
chainId: 8453 // the chain the account is deployed on
},
}
],
});

console.log('Sub Account added:', subAccount.address);
```

<Note>

Before the Sub Account is imported, you will need to add the Base Account address as an owner of the Sub Account. This currently needs to be done manually
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

where can we find addresses of these contracts on each chain? also, it would be nice to point to the ABI if you have it somewhere (but not necessary)

by calling the [`addOwnerAddress`](https://github.com/coinbase/smart-wallet/blob/a8c6456f3a6d5d2dea08d6336b3be13395cacd42/src/MultiOwnable.sol#L101) or [`addOwnerPublicKey`](https://github.com/coinbase/smart-wallet/blob/a8c6456f3a6d5d2dea08d6336b3be13395cacd42/src/MultiOwnable.sol#L109) functions on the Smart Contract of the Sub Account that was imported and setting the Base Account address as the owner.

Additionally, only Coinbase Smart Wallet contracts are currently supported for importing as a Sub Account into your Base Account.
</Note>


### Add Owner Account

Add an owner to a Sub Account:
Sub Accounts automatically detect when an ownership update is needed when a signature is required and will prompt the user to approve the update before signing. However, you can also add an owner to a Sub Account manually using the SDK convenience method:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this work on the backend side (node)? If not, can you please add a callout to specify that it does not?


```tsx
// Add owner account
const ownerAccount = await sdk.subAccount.addOwner({
address: subAccount?.address,
publicKey: cryptoAccount?.account?.publicKey,
Expand All @@ -260,6 +277,8 @@ const ownerAccount = await sdk.subAccount.addOwner({
console.log('Owner added to Sub Account');
```

This generates a transaction to call the `addOwnerAddress` or `addOwnerPublicKey` functions on the Sub Account's smart contract to add the owner.

## Auto Spend Permissions

Auto Spend Permissions allows Sub Accounts to access funds from their parent Base Account when transaction balances are insufficient. This feature can also establish ongoing spend permissions, enabling future transactions to execute without user approval prompts, reducing friction in your app's transaction flow.
Expand Down Expand Up @@ -301,6 +320,13 @@ Spend permission requests are limited to the first token when multiple transfers
</Warning>


## Technical Details

Base Account's self-custodial design requires a user passkey prompt for each wallet interaction, such as transactions or message signing. While this ensures user awareness and approval of every wallet interaction, it can impact user experience in applications requiring frequent wallet interactions.

To support Base Account with user experiences that need more developer control over wallet interactions, we've built Sub Accounts in conjunction with [ERC-7895](https://eip.tools/eip/7895), a new wallet RPC for creating hierarchical relationships between wallet accounts.

These Sub Accounts are linked to the end user's Base Account through an onchain relationship. When combined with our [Spend Permission feature](/base-account/improve-ux/spend-permissions), this creates a powerful foundation for provisioning and funding app accounts securely, while giving you ample control over building the user experience that makes the most sense for your application.


## Complete Integration Example
Expand Down Expand Up @@ -345,7 +371,6 @@ export default function SubAccountDemo() {
try {
const sdkInstance = createBaseAccountSDK({
appName: "Sub Account Demo",
appLogoUrl: "https://base.org/logo.png",
appChainIds: [baseSepolia.id],
});

Expand Down
Binary file modified docs/images/base-account/SubAccountCreation.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.