-
Notifications
You must be signed in to change notification settings - Fork 519
chore: update sub accounts docs #370
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,38 +1,32 @@ | ||
| --- | ||
| title: 'Use Sub Accounts' | ||
| description: 'Learn how to create and manage Sub Accounts using Base Account SDK' | ||
| description: 'Learn how to create and use Sub Accounts using Base Account SDK' | ||
| --- | ||
|
|
||
| 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. | ||
|
|
||
| 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 | ||
|
|
@@ -57,11 +51,38 @@ 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. | ||
|
|
||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
||
| This is what the user will see when they connect their Base Account and automatic Sub Accounts are enabled: | ||
|
|
||
| <div style={{ display: 'flex', justifyContent: 'center'}}> | ||
| <img src="/images/base-account/SubAccountCreationConnect.png" alt="Sub Account Creation Flow" style={{ width: '300px', height: 'auto' }} /> | ||
| </div> | ||
|
|
||
| <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'; | ||
|
|
@@ -78,11 +99,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 | ||
|
|
@@ -100,20 +126,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); | ||
|
|
@@ -125,38 +140,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 | ||
|
|
@@ -181,10 +167,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); | ||
|
|
@@ -193,29 +178,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...', | ||
|
|
@@ -229,13 +217,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...', | ||
|
|
@@ -245,12 +235,47 @@ 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 | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 and would like to turn it into a Sub Account of the connected Base 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 | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. | ||
|
|
||
| The Coinbase Smart Wallet contract ABI can be found on [GitHub](https://github.com/base/account-sdk/blob/master/packages/account-sdk/src/sign/base-account/utils/constants.ts#L8). | ||
| </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: | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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, | ||
|
|
@@ -260,6 +285,14 @@ 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. | ||
|
|
||
| <Note> | ||
| Ownership changes are expected if the user signs in to your app on a new device or browser. | ||
|
|
||
| Ensure you do not lose your app's Sub Account signer keys when using the SDK on the server (e.g. Node.js) as updating the owner requires a signature from the user, which cannot be requested from server contexts. | ||
| </Note> | ||
|
|
||
| ## 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. | ||
|
|
@@ -301,6 +334,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 | ||
|
|
@@ -345,7 +385,6 @@ export default function SubAccountDemo() { | |
| try { | ||
| const sdkInstance = createBaseAccountSDK({ | ||
| appName: "Sub Account Demo", | ||
| appLogoUrl: "https://base.org/logo.png", | ||
| appChainIds: [baseSepolia.id], | ||
| }); | ||
|
|
||
|
|
||
There was a problem hiding this comment.
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:
There was a problem hiding this comment.
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