diff --git a/.gitignore b/.gitignore
index c69f085f2..7501f94a2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -45,3 +45,4 @@ next-env.d.ts
*.py
.mintlify-latest
+/.idea/
diff --git a/docs/base-account/basenames/basenames-faq.mdx b/docs/base-account/basenames/basenames-faq.mdx
index 4976f2d8c..efc0da055 100644
--- a/docs/base-account/basenames/basenames-faq.mdx
+++ b/docs/base-account/basenames/basenames-faq.mdx
@@ -122,7 +122,3 @@ Basenames are built using the Ethereum Name Service (ENS) protocol, leveraging i
Yes, your Name will work on any chain as long as the app is ENSIP-10 compliant. Note that when sending money or interacting across different chains, you should ensure the receiving platform supports ENS.
-
-import PolicyBanner from "/snippets/PolicyBanner.mdx";
-
-
\ No newline at end of file
diff --git a/docs/base-account/basenames/basenames-onchainkit-tutorial.mdx b/docs/base-account/basenames/basenames-onchainkit-tutorial.mdx
index ad474a93a..e9ecfbd97 100644
--- a/docs/base-account/basenames/basenames-onchainkit-tutorial.mdx
+++ b/docs/base-account/basenames/basenames-onchainkit-tutorial.mdx
@@ -89,7 +89,3 @@ declare module 'wagmi' {
This configuration sets up the wagmi project to connect to the Base and BaseSepolia networks, utilizing Coinbase Wallet and other connectors.
-
-import PolicyBanner from "/snippets/PolicyBanner.mdx";
-
-
\ No newline at end of file
diff --git a/docs/base-account/basenames/basenames-wagmi-tutorial.mdx b/docs/base-account/basenames/basenames-wagmi-tutorial.mdx
index 8ec570e08..ca0667d3d 100644
--- a/docs/base-account/basenames/basenames-wagmi-tutorial.mdx
+++ b/docs/base-account/basenames/basenames-wagmi-tutorial.mdx
@@ -222,7 +222,3 @@ Congratulations! You've successfully integrated Basenames into your project. By
[basenames.tsx]: https://gist.github.com/hughescoin/95b680619d602782396fa954e981adae
[OnchainKit repo]: https://github.com/coinbase/onchainkit/blob/main/src/identity/utils/convertReverseNodeToBytes.ts
-
-import PolicyBanner from "/snippets/PolicyBanner.mdx";
-
-
\ No newline at end of file
diff --git a/docs/base-account/contribute/contribute-to-base-account-docs.mdx b/docs/base-account/contribute/contribute-to-base-account-docs.mdx
index 5ef6d414a..11c92bf58 100644
--- a/docs/base-account/contribute/contribute-to-base-account-docs.mdx
+++ b/docs/base-account/contribute/contribute-to-base-account-docs.mdx
@@ -176,7 +176,3 @@ If you can't answer yes to all of these questions, you need to rewrite the conte
4. Address feedback and make necessary revisions
5. Once approved, the PR will be merged and published
-
-import PolicyBanner from "/snippets/PolicyBanner.mdx";
-
-
\ No newline at end of file
diff --git a/docs/base-account/contribute/security-and-bug-bounty.mdx b/docs/base-account/contribute/security-and-bug-bounty.mdx
index c98ceb01a..c85a54d32 100644
--- a/docs/base-account/contribute/security-and-bug-bounty.mdx
+++ b/docs/base-account/contribute/security-and-bug-bounty.mdx
@@ -16,7 +16,3 @@ for responsibly reporting vulnerabilities in Base's smart contracts and infrastr
To report a bug, please follow the instructions in
the [HackerOne Bug Bounty Program](https://hackerone.com/coinbase?type=team).
-
-import PolicyBanner from "/snippets/PolicyBanner.mdx";
-
-
\ No newline at end of file
diff --git a/docs/base-account/examples/coin-a-joke-app.mdx b/docs/base-account/examples/coin-a-joke-app.mdx
index 1c0d41a42..d0c727f79 100644
--- a/docs/base-account/examples/coin-a-joke-app.mdx
+++ b/docs/base-account/examples/coin-a-joke-app.mdx
@@ -230,7 +230,3 @@ This example demonstrates how to build a full-featured onchain application that
- Transaction management
The modular architecture makes it easy to extend or modify for other use cases beyond joke tokenization.
-
-import PolicyBanner from "/snippets/PolicyBanner.mdx";
-
-
\ No newline at end of file
diff --git a/docs/base-account/examples/onchain-vibes-store-with-profiles.mdx b/docs/base-account/examples/onchain-vibes-store-with-profiles.mdx
index 9b4be7d6f..ce69d7f4e 100644
--- a/docs/base-account/examples/onchain-vibes-store-with-profiles.mdx
+++ b/docs/base-account/examples/onchain-vibes-store-with-profiles.mdx
@@ -121,11 +121,8 @@ export async function POST(request: NextRequest) {
if (Object.keys(errors).length > 0) {
return NextResponse.json({ errors });
}
- return NextResponse.json({
- calls: requestData.calls,
- chainId: requestData.chainId,
- capabilities: requestData.capabilities
- });
+ // Return requestData wrapped in request object
+ return NextResponse.json({ request: requestData });
} catch (error) {
return NextResponse.json({ errors: { server: "Server error validating data" } });
}
@@ -133,7 +130,7 @@ export async function POST(request: NextRequest) {
```
- The API receives the user's data, validates it, and returns errors if needed.
-- If validation passes, it must return the original `calls`, `chainId`, and `capabilities`.
+- If validation passes, it must return the request data wrapped in a `request` object.
- If errors are returned, the wallet prompts the user to correct their info.
---
@@ -187,7 +184,3 @@ function getCallbackURL() {
- [Profiles Reference](/smart-wallet/technical-reference/profiles-reference)
- [Base Account Docs](/smart-wallet/concepts/what-is-smart-wallet)
- [Wagmi Docs](https://wagmi.sh/)
-
-import PolicyBanner from "/snippets/PolicyBanner.mdx";
-
-
\ No newline at end of file
diff --git a/docs/base-account/framework-integrations/nextjs-with-dynamic.mdx b/docs/base-account/framework-integrations/nextjs-with-dynamic.mdx
index 635df130d..f25433b41 100644
--- a/docs/base-account/framework-integrations/nextjs-with-dynamic.mdx
+++ b/docs/base-account/framework-integrations/nextjs-with-dynamic.mdx
@@ -1,5 +1,5 @@
---
-title: "NextJS with Dynamic"
+title: "Dynamic"
description: "Integrate Base Account with Dynamic"
---
@@ -11,7 +11,3 @@ In the meantime, you can use the connector from the [Wagmi guide](/base-account/
with [Dynamic+Wagmi](https://www.dynamic.xyz/docs/react-sdk/using-wagmi) as a workaround.
-
-import PolicyBanner from "/snippets/PolicyBanner.mdx";
-
-
\ No newline at end of file
diff --git a/docs/base-account/framework-integrations/nextjs-with-privy.mdx b/docs/base-account/framework-integrations/nextjs-with-privy.mdx
deleted file mode 100644
index f49c8505c..000000000
--- a/docs/base-account/framework-integrations/nextjs-with-privy.mdx
+++ /dev/null
@@ -1,17 +0,0 @@
----
-title: "NextJS with Privy"
-description: "Integrate Base Account with Privy"
----
-
-
-We are working with [Privy](https://www.privy.io/) to integrate Base Account with their SDK.
-
-A full guide and example will be available soon.
-In the meantime, you can use the connector from the [Wagmi guide](/base-account/framework-integrations/nextjs-with-wagmi)
-with [Privy+Wagmi](https://docs.privy.io/wallets/connectors/ethereum/integrations/wagmi) as a workaround.
-
-
-
-import PolicyBanner from "/snippets/PolicyBanner.mdx";
-
-
\ No newline at end of file
diff --git a/docs/base-account/framework-integrations/nextjs-with-wagmi.mdx b/docs/base-account/framework-integrations/nextjs-with-wagmi.mdx
deleted file mode 100644
index 45e676348..000000000
--- a/docs/base-account/framework-integrations/nextjs-with-wagmi.mdx
+++ /dev/null
@@ -1,323 +0,0 @@
----
-title: "NextJS with Wagmi"
----
-
-Learn how to integrate Base Account seamlessly with Wagmi for React applications, enabling Base Account SDK functionality with familiar React hooks.
-
-## Overview
-
-[Wagmi](https://wagmi.sh/) is a collection of React hooks for Ethereum Virtual Machine (EVM) compatible networks that makes it easy to work with wallets, contracts, transactions, and signing. Base Account integrates perfectly with Wagmi, allowing you to use all your familiar hooks.
-
-### What you'll achieve
-
-By the end of this guide, you will:
-
-- Set up Wagmi with Base Account connector
-- Use standard Wagmi hooks with Base Account
-- Implement advanced Base Account features through Wagmi
-- Work with Base Account-specific capabilities
-
-## Installation
-
-After [creating a new Next.js project](https://nextjs.org/docs/app/getting-started/installation), install the required dependencies:
-
-
-```bash npm
-npm install wagmi viem @base-org/account
-```
-
-```bash pnpm
-pnpm add wagmi viem @base-org/account
-```
-
-```bash yarn
-yarn add wagmi viem @base-org/account
-```
-
-```bash bun
-bun add wagmi viem @base-org/account
-```
-
-
-
-You can also use the command line `npm create wagmi@latest` to create a new full Wagmi project.
-
-
-## Basic Setup
-
-### 1. Configure Wagmi with Base Account
-
-Create your Wagmi configuration with the Base Account connector configured for Base Account:
-
-```typescript
-// config/wagmi.ts
-import { http, createConfig } from 'wagmi'
-import type { CreateConnectorFn } from 'wagmi'
-import { base } from 'wagmi/chains'
-import { baseAccountConnector } from '@base-org/account-sdk'
-
-
-export const config = createConfig({
- chains: [base],
- connectors: [
- baseAccountConnector({
- appName: 'Base App',
- }) as CreateConnectorFn
- ],
- transports: {
- [base.id]: http()
- },
-})
-```
-
-### 2. Wrap Your App
-
-Wrap your application with the Wagmi provider:
-
-```tsx
-// app/layout.tsx or pages/_app.tsx
-import { WagmiProvider } from 'wagmi'
-import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
-import { config } from './config/wagmi'
-
-const queryClient = new QueryClient()
-
-export default function App({ children }: { children: React.ReactNode }) {
- return (
-
-
- {children}
-
-
- )
-}
-```
-
-## Basic Usage
-
-### Connection Component
-
-Create a component to handle wallet connection:
-
-```tsx
-// components/ConnectWallet.tsx
-import { useAccount, useConnect, useDisconnect } from 'wagmi'
-
-export function ConnectWallet() {
- const { address, isConnected } = useAccount()
- const { connect, connectors, isPending } = useConnect()
- const { disconnect } = useDisconnect()
-
- if (isConnected) {
- return (
-
+ )
+}
+```
+
+## Testing Your Setup
+
+Create a simple page to test your Privy + Base Account integration:
+
+```tsx
+// app/page.tsx or pages/index.tsx
+import { AuthButton } from '../components/AuthButton'
+import { WalletManager } from '../components/WalletManager'
+
+export default function HomePage() {
+ return (
+
+
Privy + Base Account Demo
+
+
+
+
+
+ )
+}
+```
+
+## Next Steps
+
+Now that you have Privy configured with Base Account, you can:
+
+- [Implement Sign in with Base](/base-account/framework-integrations/privy/sign-in-with-base)
+- [Create and manage Sub Accounts](/base-account/framework-integrations/privy/sub-accounts)
+
+## Additional Configuration
+
+### Customizing Login Methods
+
+You can customize which login methods are available:
+
+```tsx
+const privyConfig = {
+ loginMethods: ['email', 'wallet', 'google', 'twitter', 'discord'],
+ // ... other config
+}
+```
+
+### Styling and Branding
+
+Customize the appearance to match your brand:
+
+```tsx
+const privyConfig = {
+ appearance: {
+ theme: 'light', // or 'dark'
+ accentColor: '#0052FF',
+ logo: 'https://your-logo.com/logo.png',
+ landingHeader: 'Welcome to Your App',
+ showWalletLoginFirst: true,
+ },
+ // ... other config
+}
+```
diff --git a/docs/base-account/framework-integrations/privy/sub-accounts.mdx b/docs/base-account/framework-integrations/privy/sub-accounts.mdx
new file mode 100644
index 000000000..ee99cfe5e
--- /dev/null
+++ b/docs/base-account/framework-integrations/privy/sub-accounts.mdx
@@ -0,0 +1,395 @@
+---
+title: "Using Sub Accounts"
+description: "Learn how to create and manage Base Sub Accounts with Privy for streamlined user experience"
+---
+
+Base Sub Accounts are a feature of the Base Account that allow you to streamline the user experience of using a Base Account in your app. Follow this guide to learn how to use Base Sub Accounts with Privy.
+
+## Overview
+
+By default, when a user uses their Base Account within your app, the user must authorize every signature and transaction via a passkey prompt. This may be interruptive for your app's user experience, particularly for use cases that require a high-volume of signatures or transactions, such as gaming.
+
+Sub Accounts enable you to create an Ethereum account derived from the parent Base Account that is _specific to your app_, with its own address, signing capabilities, and transaction history. This Sub Account is owned by another wallet, such as an embedded wallet or a local account, and can be configured to _not_ require an explicit (passkey) confirmation from the user on every signature and transaction.
+
+Sub accounts can even transact with the balance of the parent account using Spend Permissions, allowing users to spend this balance without explicit passkey prompts.
+
+## Prerequisites
+
+Make sure you have [set up Privy with Base Account](/base-account/framework-integrations/privy/setup) before following this guide.
+
+## Implementation
+
+### 1. Set up your React app
+
+First, ensure your app is configured to:
+- Show the Base Account as one of the external wallet options that users can use to connect to your application
+- Create embedded wallets automatically on login by setting `config.embedded.ethereum.createOnLogin` to `'all-users'`
+
+```tsx
+// app/layout.tsx
+
+ {children}
+
+```
+
+This ensures that when users connect or login to your application, they have the option to use their Base Account.
+
+### 2. Create or get a Sub Account
+
+After the user logs in, create a new Sub Account or get the existing Sub Account for the user that is tied to your app's domain.
+
+First, get the connected wallet instances for your user's embedded wallet and Base Account:
+
+```tsx
+import { useWallets } from '@privy-io/react-auth';
+
+const { wallets } = useWallets();
+const embeddedWallet = wallets.find((wallet) => wallet.walletClientType === 'privy');
+const baseAccount = wallets.find((wallet) => wallet.walletClientType === 'base_account');
+// `embeddedWallet` and `baseAccount` must be defined for users to use Sub Accounts
+```
+
+Next, switch the network of the Base Account to Base or Base Sepolia, and get the wallet's EIP-1193 provider:
+
+```tsx
+// Switching to Base Sepolia
+await baseAccount.switchChain(84532);
+const provider = await baseAccount.getEthereumProvider();
+```
+
+Then, check if the user has an existing Sub Account using the `wallet_getSubAccounts` RPC method. If the user does not have an existing Sub Account, create a new one for them using the `wallet_addSubAccount` RPC:
+
+```tsx
+// Get existing Sub Account if it exists
+const {
+ subAccounts: [existingSubAccount]
+} = await provider.request({
+ method: 'wallet_getSubAccounts',
+ params: [
+ {
+ account: baseAccount.address as `0x${string}`, // The address of your user's Base Account
+ domain: window.location.origin // The domain of your app
+ }
+ ]
+});
+
+// Use the existing Sub Account if it exists, otherwise create a new sub account
+const subaccount = existingSubAccount
+ ? existingSubAccount
+ : await provider.request({
+ method: 'wallet_addSubAccount',
+ params: [
+ {
+ version: '1',
+ account: {
+ type: 'create',
+ keys: [
+ {
+ type: 'address',
+ publicKey: embeddedWallet.address as Hex // Pass your user's embedded wallet address
+ }
+ ]
+ }
+ }
+ ]
+ });
+```
+
+### 3. Configure the SDK to use the embedded wallet for Sub Account operations
+
+Configure the Base Account SDK to use the embedded wallet to control Sub Account operations. This allows the embedded wallet to sign messages and transactions on behalf of the Sub Account, avoiding the need for a separate passkey prompt.
+
+Use the `useBaseAccountSdk` hook from Privy's React SDK to access the instance of the Base Account SDK directly, and use the SDK's `subAccount.setToOwnerAccount` method to configure the embedded wallet to sign on behalf of the Sub Account's operations:
+
+```tsx
+import { useBaseAccountSdk, toViemAccount } from '@privy-io/react-auth';
+
+// ...
+
+const { baseAccountSdk } = useBaseAccountSdk();
+const toOwnerAccount = async () => {
+ const account = await toViemAccount({ wallet: embeddedWallet });
+ return { account };
+}
+baseAccountSdk.subAccount.setToOwnerAccount(toOwnerAccount);
+```
+
+### 4. Complete Sub Account Setup
+
+Here's the complete code that showcases how to create or get an existing Sub Account for your user, and set the embedded wallet as the Sub Account's owner:
+
+```tsx
+import { useWallets, useBaseAccountSdk, toViemAccount } from '@privy-io/react-auth';
+
+// In your React component
+const { wallets } = useWallets();
+const { baseAccountSdk } = useBaseAccountSdk();
+const embeddedWallet = wallets.find((wallet) => wallet.walletClientType === 'privy');
+const baseAccount = wallets.find((wallet) => wallet.walletClientType === 'base_account');
+
+// Call this function when needed, e.g. in a button's `onClick` handler
+const createOrGetSubAccount = async () => {
+ if (!embeddedWallet) throw new Error('User does not have an embedded wallet');
+ if (!baseAccount) throw new Error('User has not connected a Base Account');
+ if (!baseAccountSdk) throw new Error('Base Account SDK not initialized');
+
+ await baseAccount.switchChain(84532); // Use 8453 for Base Mainnet
+ const provider = await baseAccount.getEthereumProvider();
+
+ // Get existing Sub Account
+ const {
+ subAccounts: [existingSubAccount]
+ } = await provider.request({
+ method: 'wallet_getSubAccounts',
+ params: [{
+ account: baseAccount.address,
+ domain: window.location.origin
+ }]
+ });
+
+ // Create new Sub Account if one does not exist
+ const subaccount = existingSubAccount
+ ? existingSubAccount
+ : await provider.request({
+ method: 'wallet_addSubAccount',
+ params: [{
+ version: '1',
+ account: {
+ type: 'create',
+ keys: [{
+ type: 'address',
+ publicKey: embeddedWallet.address
+ }]
+ }
+ }]
+ });
+
+ // Configure embedded wallet as owner
+ const toOwnerAccount = async () => {
+ const account = await toViemAccount({ wallet: embeddedWallet });
+ return { account };
+ };
+ baseAccountSdk.subAccount.setToOwnerAccount(toOwnerAccount);
+};
+```
+
+### 5. Sign messages and send transactions with the Sub Account
+
+Once configured, you can sign and send transactions with the Sub Account using the Base Account's EIP1193 provider. To ensure that signatures and transactions come from the Sub Account, for each of the following RPCs:
+
+- `personal_sign`: pass the Sub Account's address, not the parent Base Account's address, as the second parameter
+- `eth_signTypedData_v4`: pass the Sub Account's address, not the parent Base Account's address, as the first parameter
+- `eth_sendTransaction`: set `from` in the transaction object to the Sub Account's address, not the parent Base Account's address
+
+When these methods are invoked, the embedded wallet will sign on behalf of the Sub Account, avoiding the need for an explicit passkey prompt from the user.
+
+#### Sign a message
+
+```tsx
+import { toHex } from 'viem';
+
+const message = 'Hello world';
+const signature = await baseProvider.request({
+ method: 'personal_sign',
+ params: [toHex(message), subaccount.address] // Pass the Sub Account, not parent Base Account address
+});
+```
+
+#### Send a transaction
+
+```tsx
+const txHash = await baseProvider.request({
+ method: 'eth_sendTransaction',
+ params: [{
+ from: subaccount.address, // Sub Account address
+ to: '0x...',
+ value: '0x...',
+ data: '0x...'
+ }]
+});
+```
+
+#### Send a batch of transactions
+
+```tsx
+const batchTxHash = await baseProvider.request({
+ method: 'wallet_sendCalls',
+ params: [{
+ from: subaccount.address, // Sub Account address
+ calls: [
+ {
+ to: '0x...',
+ value: '0x...',
+ data: '0x...'
+ },
+ {
+ to: '0x...',
+ value: '0x...',
+ data: '0x...'
+ }
+ ]
+ }]
+});
+```
+
+## Complete Example Component
+
+Here's a complete React component that demonstrates Sub Account creation and usage:
+
+```tsx
+import React, { useState } from 'react';
+import { useWallets, useBaseAccountSdk, toViemAccount } from '@privy-io/react-auth';
+import { toHex } from 'viem';
+
+export function SubAccountDemo() {
+ const [subAccount, setSubAccount] = useState(null);
+ const [isLoading, setIsLoading] = useState(false);
+ const [message, setMessage] = useState('');
+ const [signature, setSignature] = useState('');
+
+ const { wallets } = useWallets();
+ const { baseAccountSdk } = useBaseAccountSdk();
+
+ const embeddedWallet = wallets.find((wallet) => wallet.walletClientType === 'privy');
+ const baseAccount = wallets.find((wallet) => wallet.walletClientType === 'base_account');
+
+ const setupSubAccount = async () => {
+ if (!embeddedWallet || !baseAccount || !baseAccountSdk) return;
+
+ setIsLoading(true);
+ try {
+ await baseAccount.switchChain(84532); // Base Sepolia
+ const provider = await baseAccount.getEthereumProvider();
+
+ // Get or create Sub Account
+ const { subAccounts: [existingSubAccount] } = await provider.request({
+ method: 'wallet_getSubAccounts',
+ params: [{
+ account: baseAccount.address,
+ domain: window.location.origin
+ }]
+ });
+
+ const subaccount = existingSubAccount || await provider.request({
+ method: 'wallet_addSubAccount',
+ params: [{
+ version: '1',
+ account: {
+ type: 'create',
+ keys: [{
+ type: 'address',
+ publicKey: embeddedWallet.address
+ }]
+ }
+ }]
+ });
+
+ // Configure embedded wallet as owner
+ const toOwnerAccount = async () => {
+ const account = await toViemAccount({ wallet: embeddedWallet });
+ return { account };
+ };
+ baseAccountSdk.subAccount.setToOwnerAccount(toOwnerAccount);
+
+ setSubAccount(subaccount);
+ } catch (error) {
+ console.error('Sub Account setup failed:', error);
+ } finally {
+ setIsLoading(false);
+ }
+ };
+
+ const signMessage = async () => {
+ if (!baseAccount || !subAccount) return;
+
+ try {
+ const provider = await baseAccount.getEthereumProvider();
+ const sig = await provider.request({
+ method: 'personal_sign',
+ params: [toHex(message), subAccount.address]
+ });
+ setSignature(sig);
+ } catch (error) {
+ console.error('Signing failed:', error);
+ }
+ };
+
+ return (
+
+ );
+}
+```
+
+## Benefits of Sub Accounts
+
+- **Reduced Friction**: Users don't need to approve every transaction with a passkey
+- **Better UX**: Seamless interactions for gaming and high-frequency use cases
+- **Security**: Sub Accounts are still secured by the parent Base Account
+- **Spend Permissions**: Sub Accounts can transact with the parent account's balance
+
+## Next Steps
+
+You can combine Sub Accounts with Spend Permissions to allow the Sub Account to spend from the balance of the parent Base Account in `eth_sendTransaction` requests.
+
+For more advanced Sub Account features and Spend Permissions, refer to the [Base Account Sub Accounts guide](/base-account/improve-ux/sub-accounts).
diff --git a/docs/base-account/framework-integrations/wagmi/base-pay.mdx b/docs/base-account/framework-integrations/wagmi/base-pay.mdx
new file mode 100644
index 000000000..55b02114c
--- /dev/null
+++ b/docs/base-account/framework-integrations/wagmi/base-pay.mdx
@@ -0,0 +1,60 @@
+---
+title: "Base Pay"
+description: "Accept USDC payments with Base Pay in your Wagmi-powered React application"
+---
+
+Base Pay works the same way in Wagmi applications as it does anywhere else - it operates independently of wallet connections and uses the Base Account SDK directly.
+
+## Implementation
+
+Base Pay doesn't require any special Wagmi integration. Simply follow the [Accept Payments guide](/base-account/guides/accept-payments) - all the code examples work exactly the same in your Wagmi app.
+
+The key points:
+
+- **No wallet connection needed** - Base Pay handles everything through the SDK
+- **Same API** - Use `pay()` and `getPaymentStatus()` exactly as shown in the main guide
+- **Works alongside Wagmi** - You can display the user's connected address from `useAccount()` but it's not required for payments
+
+## Quick Example
+
+```tsx
+import { pay } from '@base-org/account'
+import { useAccount } from 'wagmi' // Optional - just for display
+
+export function CheckoutButton() {
+ const { address } = useAccount() // Optional
+
+ const handlePayment = async () => {
+ try {
+ const payment = await pay({
+ amount: '5.00',
+ to: '0xYourAddress',
+ testnet: true
+ })
+ console.log('Payment sent:', payment.id)
+ } catch (error) {
+ console.error('Payment failed:', error)
+ }
+ }
+
+ return (
+
+ {address &&
Connected: {address}
}
+
+
+ )
+}
+```
+
+
+**Please Follow the Brand Guidelines**
+
+If you intend on using the `BasePayButton`, please follow the [Brand Guidelines](/base-account/reference/ui-elements/brand-guidelines) to ensure consistency across your application.
+
+
+
+## Learn More
+
+For complete implementation details, examples, and advanced features like collecting user information, see the main [Accept Payments guide](/base-account/guides/accept-payments).
diff --git a/docs/base-account/framework-integrations/wagmi/other-use-cases.mdx b/docs/base-account/framework-integrations/wagmi/other-use-cases.mdx
new file mode 100644
index 000000000..bf57aab28
--- /dev/null
+++ b/docs/base-account/framework-integrations/wagmi/other-use-cases.mdx
@@ -0,0 +1,114 @@
+---
+title: "Other Use Cases"
+description: "Access the Base Account provider from Wagmi for advanced functionality like Sub Accounts, Spend Permissions, and more"
+---
+
+Learn how to access the Base Account provider through Wagmi to unlock advanced Base Account features beyond basic authentication and payments.
+
+## Prerequisites
+
+Make sure you have [set up Wagmi with Base Account](/base-account/framework-integrations/wagmi/setup) before following this guide.
+
+## Getting the Provider
+
+The key to accessing advanced Base Account functionality is getting the provider from your Wagmi connector. Once you have the provider, you can use any Base Account RPC method.
+
+
+```tsx Hook
+// hooks/useBaseAccountProvider.ts
+import { useConnections } from 'wagmi'
+import { useEffect, useState } from 'react'
+import { EIP1193Provider } from 'viem'
+
+export function useBaseAccountProvider() {
+ const connections = useConnections()
+ const [provider, setProvider] = useState(null)
+
+ useEffect(() => {
+ const connection = connections[0]
+
+ if (!connection) {
+ setProvider(null)
+ return
+ }
+
+ connection.connector.getProvider().then((provider) => {
+ setProvider(provider as EIP1193Provider)
+ })
+ }, [connections])
+
+ return provider
+}
+```
+```tsx Component
+// components/BaseAccountFeatures.tsx
+import { useBaseAccountProvider } from '../hooks/useBaseAccountProvider'
+import { useAccount } from 'wagmi'
+
+export function BaseAccountFeatures() {
+ const { address, isConnected } = useAccount()
+ const provider = useBaseAccountProvider()
+
+ const callProviderMethod = async (method: string, params: any[]) => {
+ if (!provider) {
+ console.error('Provider not available')
+ return
+ }
+
+ try {
+ const result = await provider.request({
+ method,
+ params
+ })
+ console.log(`${method} result:`, result)
+ return result
+ } catch (error) {
+ console.error(`${method} error:`, error)
+ throw error
+ }
+ }
+
+ if (!isConnected) {
+ return
Please connect your wallet to access Base Account features
+ }
+
+ return (
+
+
Base Account Features
+
+ Connected with Base Account provider. You can now access advanced features.
+
+
+ )
+}
+```
+
+
+## Available Use Cases
+
+Once you have the provider, you can access all Base Account functionality:
+
+### Sub Accounts
+Create and manage child accounts for improved UX.
+
+**Learn more:** [Sub Accounts Guide](/base-account/improve-ux/sub-accounts) | [Sub Accounts RPC Method](/base-account/reference/core/provider-rpc-methods/wallet_addSubAccount)
+
+### Spend Permissions
+Allow apps to spend on behalf of users with predefined limits.
+
+**Learn more:** [Spend Permissions Guide](/base-account/improve-ux/spend-permissions) | [Spend Permissions Reference](/base-account/reference/spend-permission-utilities/requestSpendPermission)
+
+### Batch Transactions
+Execute multiple transactions in a single user confirmation.
+
+**Learn more:** [Batch Transactions Guide](/base-account/improve-ux/batch-transactions) | [`wallet_sendCalls` Reference](/base-account/reference/core/provider-rpc-methods/wallet_sendCalls)
+
+### Gasless Transactions
+Sponsor gas fees for your users.
+
+**Learn more:** [Gasless Transactions Guide](/base-account/improve-ux/sponsor-gas/paymaster) | [Coinbase Developer Platform Paymaster](https://docs.cdp.coinbase.com/paymaster/introduction/welcome)
+
+### Full list of provider methods and capabilities
+Access the full list of Base Account provider methods and capabilities.
+
+**Learn more:** [Provider RPC Methods](/base-account/reference/core/provider-rpc-methods/request-overview) | [Capabilities](/base-account/reference/core/capabilities/overview)
\ No newline at end of file
diff --git a/docs/base-account/framework-integrations/wagmi/setup.mdx b/docs/base-account/framework-integrations/wagmi/setup.mdx
new file mode 100644
index 000000000..028767486
--- /dev/null
+++ b/docs/base-account/framework-integrations/wagmi/setup.mdx
@@ -0,0 +1,94 @@
+---
+title: "Setup"
+description: "Configure Wagmi with Base Account connector for your React application"
+---
+
+Learn how to set up Wagmi with Base Account to enable Base Account SDK functionality with familiar React hooks.
+
+## Overview
+
+[Wagmi](https://wagmi.sh/) is a collection of React hooks for Ethereum Virtual Machine (EVM) compatible networks that makes it easy to work with wallets, contracts, transactions, and signing. Base Account integrates perfectly with Wagmi, allowing you to use all your familiar hooks.
+
+## Installation
+
+If you start [a new wagmi project](https://wagmi.sh/react/getting-started), you can skip the installation step.
+
+If you already have a project, you can install the dependencies with your package manager of choice:
+
+
+```bash npm
+npm install wagmi viem @base-org/account
+```
+
+```bash pnpm
+pnpm add wagmi viem @base-org/account
+```
+
+```bash yarn
+yarn add wagmi viem @base-org/account
+```
+
+```bash bun
+bun add wagmi viem @base-org/account
+```
+
+
+
+To create a new wagmi project, you can use the command line `npm create wagmi@latest`.
+
+
+## Configuration
+
+### 1. Configure Wagmi with Base Account
+
+Create your Wagmi configuration with the Base Account connector configured for Base Account:
+
+```typescript
+// config/wagmi.ts
+import { http, createConfig } from 'wagmi'
+import { base } from 'wagmi/chains'
+import { baseAccount } from 'wagmi/connectors'
+
+
+export const config = createConfig({
+ chains: [base],
+ connectors: [
+ baseAccount({
+ appName: 'Base App',
+ })
+ ],
+ transports: {
+ [base.id]: http()
+ },
+})
+```
+
+### 2. Wrap Your App
+
+Wrap your application with the Wagmi provider:
+
+```tsx
+// app/layout.tsx or pages/_app.tsx
+import { WagmiProvider } from 'wagmi'
+import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
+import { config } from './config/wagmi'
+
+const queryClient = new QueryClient()
+
+export default function App({ children }: { children: React.ReactNode }) {
+ return (
+
+
+ {children}
+
+
+ )
+}
+```
+
+## Next Steps
+
+Now that you have Wagmi configured with Base Account, you can:
+
+- [Connect users with Sign in with Base](/base-account/framework-integrations/wagmi/sign-in-with-base)
+- [Access the Base Account provider](/base-account/framework-integrations/wagmi/other-use-cases)
\ No newline at end of file
diff --git a/docs/base-account/framework-integrations/wagmi/sign-in-with-base.mdx b/docs/base-account/framework-integrations/wagmi/sign-in-with-base.mdx
new file mode 100644
index 000000000..49afe706f
--- /dev/null
+++ b/docs/base-account/framework-integrations/wagmi/sign-in-with-base.mdx
@@ -0,0 +1,199 @@
+---
+title: "Sign in with Base"
+description: "Implement Base Account authentication using the proper SIWE flow with Wagmi"
+---
+
+Learn how to implement Sign in with Base using Wagmi by accessing the Base Account provider and following the proper SIWE (Sign-In With Ethereum) authentication flow.
+
+## Prerequisites
+
+Make sure you have [set up Wagmi with Base Account](/base-account/framework-integrations/wagmi/setup) before following this guide.
+
+## Overview
+
+To implement Sign in with Base with Wagmi, you need to:
+
+1. Get the Base Account connector from Wagmi
+2. Access the underlying provider from the connector
+3. Use `wallet_connect` with `signInWithEthereum` capabilities
+4. Verify the signature on your backend
+
+This follows the same flow as shown in the [authenticate users guide](/base-account/guides/authenticate-users), but integrates with Wagmi's connector system.
+
+## Implementation
+
+### Code Snippets
+
+
+```ts Browser (Wagmi + SDK)
+import { useState } from 'react'
+import { useConnect, useAccount, useDisconnect } from 'wagmi'
+import { baseAccount } from 'wagmi/connectors'
+
+export function SignInWithBase() {
+ const [isLoading, setIsLoading] = useState(false)
+ const [error, setError] = useState(null)
+ const { isConnected, address } = useAccount()
+ const { connectAsync, connectors } = useConnect()
+ const { disconnect } = useDisconnect()
+
+ // Find the Base Account connector
+ const baseAccountConnector = connectors.find(
+ connector => connector.id === 'baseAccountWallet'
+ )
+
+ const handleSignIn = async () => {
+ if (!baseAccountConnector) {
+ setError('Base Account connector not found')
+ return
+ }
+
+ setIsLoading(true)
+ setError(null)
+
+ try {
+ // 1 — get a fresh nonce (generate locally or prefetch from backend)
+ const nonce = window.crypto.randomUUID().replace(/-/g, '')
+ // OR prefetch from server
+ // const nonce = await fetch('/auth/nonce').then(r => r.text())
+
+ // 2 — connect and get the provider
+ await connectAsync({ connector: baseAccountConnector })
+ const provider = baseAccountConnector.provider
+
+ // 3 — authenticate with wallet_connect
+ const authResult = await provider.request({
+ method: 'wallet_connect',
+ params: [{
+ version: '1',
+ capabilities: {
+ signInWithEthereum: {
+ nonce,
+ chainId: '0x2105' // Base Mainnet - 8453
+ }
+ }
+ }]
+ })
+
+ const { accounts } = authResult
+ const { address, capabilities } = accounts[0]
+ const { message, signature } = capabilities.signInWithEthereum
+
+ // 4 — verify on backend
+ await fetch('/auth/verify', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ address, message, signature })
+ })
+ } catch (err: any) {
+ console.error(`err ${err}`)
+ setError(err.message || 'Sign in failed')
+ } finally {
+ setIsLoading(false)
+ }
+ }
+
+ if (isConnected) {
+ return (
+
+ {address}
+
+
+ )
+ }
+
+ return (
+
+ )
+}
+```
+```ts Backend (Viem)
+import { createPublicClient, http } from 'viem';
+import { base } from 'viem/chains';
+
+const client = createPublicClient({ chain: base, transport: http() });
+
+export async function verifySig(req, res) {
+ const { address, message, signature } = req.body;
+ const valid = await client.verifyMessage({ address, message, signature });
+ if (!valid) return res.status(401).json({ error: 'Invalid signature' });
+ // create session / JWT
+ res.json({ ok: true });
+}
+```
+
+
+### 3. Using the Pre-built Button Component
+
+You can also use the official [Sign In With Base](/base-account/reference/ui-elements/sign-in-with-base-button) button component:
+
+```tsx
+// components/SignInButton.tsx
+import { SignInWithBaseButton } from '@base-org/account-ui/react'
+import { useConnect } from 'wagmi'
+
+export function SignInButton() {
+ const { connectAsync, connectors } = useConnect()
+
+ const handleSignIn = async () => {
+ const baseAccountConnector = connectors.find(
+ connector => connector.id === 'baseAccountWallet'
+ )
+
+ if (!baseAccountConnector) return
+
+ try {
+ // Generate nonce
+ const nonce = window.crypto.randomUUID().replace(/-/g, '')
+
+ // Connect and get provider
+ await connectAsync({ connector: baseAccountConnector })
+ const provider = baseAccountConnector.provider
+
+ // Perform SIWE authentication
+ const authResult = await provider.request({
+ method: 'wallet_connect',
+ params: [{
+ version: '1',
+ capabilities: {
+ signInWithEthereum: {
+ nonce,
+ chainId: '0x2105'
+ }
+ }
+ }]
+ })
+
+ // Extract and verify signature
+ const { accounts } = authResult
+ const { address, capabilities } = accounts[0]
+ const { message, signature } = capabilities.signInWithEthereum
+
+ // Send to backend for verification
+ await fetch('/auth/verify', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ address, message, signature })
+ })
+ } catch (error) {
+ console.error('Authentication failed:', error)
+ }
+ }
+
+ return (
+
+ )
+}
+```
+
+
+**Please Follow the Brand Guidelines**
+
+If you intend on using the `SignInWithBaseButton`, please follow the [Brand Guidelines](/base-account/reference/ui-elements/brand-guidelines) to ensure consistency across your application.
+
+
\ No newline at end of file
diff --git a/docs/base-account/framework-integrations/wagmi/sub-accounts.mdx b/docs/base-account/framework-integrations/wagmi/sub-accounts.mdx
new file mode 100644
index 000000000..1179eab2b
--- /dev/null
+++ b/docs/base-account/framework-integrations/wagmi/sub-accounts.mdx
@@ -0,0 +1,366 @@
+---
+title: "Using Sub Accounts"
+description: "Implement Base Account Sub Accounts using Wagmi"
+---
+
+Learn how to create and manage Sub Accounts using Wagmi hooks and Base Account provider methods.
+
+## Prerequisites
+
+Make sure you have:
+- [Set up Wagmi with Base Account](/base-account/framework-integrations/wagmi/setup)
+- [Implemented Sign in with Base](/base-account/framework-integrations/wagmi/sign-in-with-base)
+
+## Overview
+
+Sub Accounts allow you to create child accounts that can spend from the parent account's balance using [Spend Permissions](/base-account/improve-ux/spend-permissions). This reduces the need for frequent user signatures and improves the user experience.
+
+## Creating Sub Accounts
+
+Use the Base Account provider to create Sub Accounts:
+
+```tsx
+import { useAccount, useConnector } from 'wagmi'
+import { useState } from 'react'
+
+export function CreateSubAccount() {
+ const { address, isConnected } = useAccount()
+ const connector = useConnector()
+ const [subAccount, setSubAccount] = useState(null)
+ const [isCreating, setIsCreating] = useState(false)
+
+ const createSubAccount = async () => {
+ if (!connector || !isConnected) return
+
+ setIsCreating(true)
+ try {
+ const provider = connector.provider
+
+ // Create a new Sub Account
+ const result = await provider?.request({
+ method: 'wallet_addSubAccount',
+ params: [{
+ version: '1.0',
+ chainId: `0x${Number(8453).toString(16)}`, // Base mainnet
+ from: address,
+ }]
+ })
+
+ if (result?.subAccount) {
+ setSubAccount(result.subAccount)
+ console.log('Sub Account created:', result.subAccount)
+ }
+ } catch (error) {
+ console.error('Failed to create Sub Account:', error)
+ } finally {
+ setIsCreating(false)
+ }
+ }
+
+ return (
+
+ )
+}
+```
+
+## Learn More
+
+- [Sub Accounts overview](/base-account/improve-ux/sub-accounts)
+- [Spend Permissions](/base-account/improve-ux/spend-permissions)
+- [Base Account RPC methods](/base-account/reference/core/provider-rpc-methods/wallet_addSubAccount)
diff --git a/docs/base-account/guides/accept-payments.mdx b/docs/base-account/guides/accept-payments.mdx
index 140bde214..588390ad0 100644
--- a/docs/base-account/guides/accept-payments.mdx
+++ b/docs/base-account/guides/accept-payments.mdx
@@ -2,6 +2,8 @@
title: "Accept Payments"
description: "Add one-tap USDC payments to your app with the pay() helper and Base Pay Button."
---
+import {BasePayButton} from "/snippets/BasePayButton.mdx"
+import {SignInWithBaseButton} from "/snippets/SignInWithBaseButton.mdx"
## Why Base Pay?
@@ -10,32 +12,54 @@ USDC on Base is a fully-backed digital dollar that settles in seconds and costs
* **Any user can pay** – works with every Base Account (smart-wallet) out of the box.
* **USDC, not gas** – you charge in dollars; gas sponsorship is handled automatically.
* **Fast** – most payments confirm in <2 seconds on Base.
+* **Funded accounts** – users pay with USDC from their Base Account or Coinbase Account.
* **No extra fees** – you receive the full amount.
+
+**Please Follow the Brand Guidelines**
+
+If you intend on using the BasePayButton, please follow the [Brand Guidelines](/base-account/reference/ui-elements/brand-guidelines) to ensure consistency across your application.
+
+
## Client-side (Browser SDK)
+
+**Interactive Playground:** Try out the `pay()` and `getPaymentStatus()` functions in our [Base Pay SDK Playground](https://base.github.io/account-sdk/pay-playground) before integrating them into your app.
+
+
```ts Browser (SDK)
import { pay, getPaymentStatus } from '@base-org/account';
// Trigger a payment – user will see a popup from their wallet service
-const { id } = await pay({
- amount: '1.00', // USD amount (we quote USDC internally)
- to: '0xRecipient', // your address
- testnet: true // set false for Mainnet
-});
-
-// Option 1: Poll until mined
-const { status } = await getPaymentStatus({ id });
-if (status === 'completed') console.log('🎉 payment settled');
+try {
+ const payment = await pay({
+ amount: '1.00', // USD amount (USDC used internally)
+ to: '0xRecipient', // your address
+ testnet: true // set false for Mainnet
+ });
+
+ // Option 1: Poll until mined
+ const { status } = await getPaymentStatus({
+ id: payment.id,
+ testnet: true // MUST match the testnet setting used in pay()
+ });
+ if (status === 'completed') console.log('🎉 payment settled');
+
+} catch (error) {
+ console.error(`Payment failed: ${error.message}`);
+}
```
+
+**Important:** The `testnet` parameter in `getPaymentStatus()` must match the value used in the original `pay()` call. If you initiated a payment on testnet with `testnet: true`, you must also pass `testnet: true` when checking its status.
+
This is what the user will see when prompted to pay:
-
+
### Collect user information (optional)
@@ -43,17 +67,39 @@ This is what the user will see when prompted to pay:
Need an email, phone, or shipping address at checkout? Pass a payerInfo object:
```ts
-await pay({
- amount: '25.00',
- to: '0xRecipient',
- payerInfo: {
- requests: [
- { type: 'email' },
- { type: 'phoneNumber', optional: true }
- ],
- callbackURL: 'https://your-api.com/validate' // Optional - for server-side validation
+try {
+ const payment = await pay({
+ amount: '25.00',
+ to: '0xRecipient',
+ payerInfo: {
+ requests: [
+ { type: 'email' },
+ { type: 'phoneNumber', optional: true },
+ { type: 'physicalAddress', optional: true }
+ ],
+ callbackURL: 'https://your-api.com/validate' // Optional - for server-side validation
+ }
+ });
+
+ console.log(`Payment sent! Transaction ID: ${payment.id}`);
+
+ // Log the collected user information
+ if (payment.payerInfoResponses) {
+ if (payment.payerInfoResponses.email) {
+ console.log(`Email: ${payment.payerInfoResponses.email}`);
+ }
+ if (payment.payerInfoResponses.phoneNumber) {
+ console.log(`Phone: ${payment.payerInfoResponses.phoneNumber.number}`);
+ console.log(`Country: ${payment.payerInfoResponses.phoneNumber.country}`);
+ }
+ if (payment.payerInfoResponses.physicalAddress) {
+ const address = payment.payerInfoResponses.physicalAddress;
+ console.log(`Shipping Address: ${address.name.firstName} ${address.name.familyName}, ${address.address1}, ${address.city}, ${address.state} ${address.postalCode}`);
+ }
}
-});
+} catch (error) {
+ console.error(`Payment failed: ${error.message}`);
+}
```
Supported request types:
@@ -63,7 +109,7 @@ Supported request types:
| email | string |
| name | { firstName, familyName } |
| phoneNumber | { number, country } |
-| physicalAddress | full address object |
+| physicalAddress | [full address object](/base-account/reference/core/capabilities/datacallback#physical-address-object) |
| onchainAddress | string |
Required by default — set optional: true to avoid aborting the payment if the user declines.
@@ -73,7 +119,7 @@ Supported request types:
You can use the `callbackURL` to validate the user's information on the server side.
-Learn more about this in the [callbackURL reference](/base-account/reference/base-pay/Datacallbackurl).
+Learn more about this in the [callbackURL reference](/base-account/reference/core/capabilities/datacallback).
## Polling example
@@ -81,8 +127,11 @@ Learn more about this in the [callbackURL reference](/base-account/reference/bas
```ts Backend (SDK)
import { getPaymentStatus } from '@base-org/account';
-export async function checkPayment(txId) {
- const status = await getPaymentStatus({ id: txId });
+export async function checkPayment(txId, testnet = false) {
+ const status = await getPaymentStatus({
+ id: txId,
+ testnet // Must match the testnet setting from the original pay() call
+ });
if (status.status === 'completed') {
// fulfil order
}
@@ -98,25 +147,36 @@ import { BasePayButton } from '@base-org/account-ui/react';
import { pay } from '@base-org/account';
export function Checkout() {
+ const handlePayment = async () => {
+ try {
+ const payment = await pay({ amount: '5.00', to: '0xRecipient' });
+ console.log(`Payment sent! Transaction ID: ${payment.id}`);
+ } catch (error) {
+ console.error(`Payment failed: ${error.message}`);
+ }
+ };
+
return (
pay({ amount: '5.00', to: '0xRecipient' })}
+ onClick={handlePayment}
/>
);
}
```
-
See full props and theming options in the [Button Reference](/base-account/reference/ui-elements/base-pay-button) and [Brand Guidelines](/base-account/reference/ui-elements/brand-guidelines).
-## Test on Base Sepolia
+
+**Please Follow the Brand Guidelines**
-1. Get test USDC from the Circle Faucet (select “Base Sepolia”).
-2. Pass testnet: true in your pay() call.
-3. Use Sepolia BaseScan to watch the transaction.
+If you intend on using the BasePayButton, please follow the [Brand Guidelines](/base-account/reference/ui-elements/brand-guidelines) to ensure consistency across your application.
+
+## Test on Base Sepolia
-import PolicyBanner from "/snippets/PolicyBanner.mdx";
+1. Get test USDC from the Circle Faucet (select "Base Sepolia").
+2. Pass testnet: true in your pay() and getPaymentStatus() calls.
+3. Use Sepolia BaseScan to watch the transaction.
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/base-account/guides/authenticate-users.mdx b/docs/base-account/guides/authenticate-users.mdx
index bcb581324..755918628 100644
--- a/docs/base-account/guides/authenticate-users.mdx
+++ b/docs/base-account/guides/authenticate-users.mdx
@@ -3,6 +3,8 @@ title: "Authenticate Users"
description: "Let a user click “Sign in with Base,” prove ownership of their onchain account, and give your server everything it needs to create a session – using open standards and no passwords"
---
+import {BasePayButton} from "/snippets/BasePayButton.mdx"
+import {SignInWithBaseButton} from "/snippets/SignInWithBaseButton.mdx"
## Why wallet signatures instead of passwords?
@@ -12,6 +14,13 @@ description: "Let a user click “Sign in with Base,” prove ownership of their
Base Accounts build on those standards so you can reuse any SIWE tooling – while still benefiting from passkeys, session keys, and smart-wallet security.
+
+**Please Follow the Brand Guidelines**
+
+If you intend on using the `SignInWithBaseButton`, please follow the [Brand Guidelines](/base-account/reference/ui-elements/brand-guidelines) to ensure consistency across your application.
+
+
+
## High-level flow
```mermaid
@@ -22,10 +31,14 @@ sequenceDiagram
participant SDK
participant Account
- User->>Browser: Click "Sign in with Base"
- Browser->>AppServer: GET /auth/nonce
- AppServer-->>Browser: nonce
+ alt Generate locally
+ Browser->>Browser: randomNonce()
+ else Prefetch
+ Browser->>AppServer: GET /auth/nonce (on page load)
+ AppServer-->>Browser: nonce
+ end
+ User->>Browser: Click "Sign in with Base"
Browser->>SDK: wallet_connect(signInWithEthereum {nonce})
SDK->>Account: wallet_connect(...)
User->>Account: Approve connection
@@ -47,12 +60,15 @@ sequenceDiagram
```ts Browser (SDK)
import { createBaseAccountSDK } from "@base-org/account";
+import crypto from 'crypto';
-// Initialise the SDK (no config needed for defaults)
+// Initialize the SDK (no config needed for defaults)
const provider = createBaseAccountSDK().getProvider();
-// 1 — fetch a nonce from your backend
-const nonce = await fetch('/auth/nonce').then(r => r.text());
+// 1 — get a fresh nonce (generate locally or prefetch from backend)
+const nonce = window.crypto.randomUUID().replace(/-/g, '');
+// OR prefetch from server
+// const nonce = await fetch('/auth/nonce').then(r => r.text());
// 2 — connect and authenticate
try {
@@ -61,7 +77,10 @@ try {
params: [{
version: '1',
capabilities: {
- signInWithEthereum: { nonce, chainId: '0x1' }
+ signInWithEthereum: {
+ nonce,
+ chainId: '0x2105' // Base Mainnet - 8453
+ }
}
}]
});
@@ -93,12 +112,18 @@ export async function verifySig(req, res) {
-If using the above code beyond Base Account, note that not every wallet supports the new [wallet_connect method](https://eips.ethereum.org/EIPS/eip-7846) yet. If the call throws method_not_supported, fall back to using eth_requestAccounts and personal_sign.
+If using the above code beyond Base Account, note that not every wallet supports the new [wallet_connect method](/base-account/reference/core/provider-rpc-methods/wallet_connect) yet. If the call throws [method_not_supported], fall back to using eth_requestAccounts and personal_sign.
+
+To avoid [popup blockers](/base-account/more/troubleshooting/usage-details/popups#default-blocking-behavior), fetch or generate the nonce before the user presses "Sign in with Base" (for example on page load). For security, the only requirement is that your backend keeps track of every nonce and refuses any that are reused – regardless of where it originated.
+
+
+{/*
+TODO: Link Wagmi Sign in with Base guide
For a full React example see the React + Wagmi guide.
-
+ */}
### Example Express Server
@@ -146,10 +171,10 @@ app.listen(3001, () => console.log('Auth server listening on :3001'));
Use the pre-built component for a native look-and-feel:
-```tsx title="Checkout.tsx"
+```tsx title="App.tsx"
import { SignInWithBaseButton } from '@base-org/account-ui/react';
-export function Checkout() {
+export function App() {
return (
+**Please Follow the Brand Guidelines**
+
+If you intend on using the `SignInWithBaseButton`, please follow the [Brand Guidelines](/base-account/reference/ui-elements/brand-guidelines) to ensure consistency across your application.
-
\ No newline at end of file
+
diff --git a/docs/base-account/guides/migration-guide.mdx b/docs/base-account/guides/migration-guide.mdx
index b7ac92583..5916286f2 100644
--- a/docs/base-account/guides/migration-guide.mdx
+++ b/docs/base-account/guides/migration-guide.mdx
@@ -1,6 +1,6 @@
---
title: "Migrate from Coinbase Wallet SDK"
-description: "A guide to migrating from the Coinbase Wallet SDK to the Base Account."
+description: "A guide to migrating from the Coinbase Wallet SDK to the Base Account SDK"
---
## Overview
@@ -62,7 +62,4 @@ If you're using a third party library, you can follow the [Wagmi](/base-account/
We will have a more complete migration guide in the near future.
-import PolicyBanner from "/snippets/PolicyBanner.mdx";
-
-
diff --git a/docs/base-account/guides/sign-and-verify-typed-data.mdx b/docs/base-account/guides/sign-and-verify-typed-data.mdx
new file mode 100644
index 000000000..180fb15ec
--- /dev/null
+++ b/docs/base-account/guides/sign-and-verify-typed-data.mdx
@@ -0,0 +1,282 @@
+---
+title: "Sign and Verify Typed Data"
+description: "EIP-712 structured data signing and verification for Base Account"
+---
+
+## Overview
+
+Base Account uses [Smart Wallet contracts](https://github.com/coinbase/smart-wallet) under the hood. Smart contract wallets introduce a few differences in how messages are signed compared to traditional Externally Owned Accounts (EOAs). This guide explains how to properly implement message signing using Base Account, covering both standard messages and typed data signatures, as well as some edge cases.
+
+## Introduction
+
+Before walking through the details of how to sign and verify messages using Base Account, it's important to understand some of the use cases of signing messages with wallets, as well as the key differences between EOAs and smart contracts when it comes to signing messages.
+
+### Use Cases for Wallet Signatures
+
+Blockchain-based apps use wallet signatures for two main categories:
+
+1. **Signatures for offchain verification**: Used for authenticating users in onchain apps (e.g., Sign-In with Ethereum) to avoid spoofing. The signature is not used for any onchain action.
+
+2. **Signatures for onchain verification**: Used for signing onchain permissions (e.g., [Permit2](https://github.com/Uniswap/permit2)) or batching transactions. The signature is usually stored for future transactions.
+
+### Smart Contract Wallet Differences
+
+Smart contract wallets handle signatures differently from EOAs in several ways:
+
+- The contract itself doesn't produce signatures - instead, the owner (e.g., passkey) signs messages
+- Verification happens through the `isValidSignature` function defined in [EIP-1271](https://eips.ethereum.org/EIPS/eip-1271)
+- Smart contract wallet addresses are often deterministic, allowing signature support before deployment via [ERC-6492](https://eips.ethereum.org/EIPS/eip-6492)
+
+## High-level flow
+
+In this guide, we'll walk through the high-level flow of signing and verifying messages using Base Account.
+
+```mermaid
+sequenceDiagram
+ participant User
+ participant Browser
+ participant AppServer as "App Server"
+ participant SDK
+ participant Account
+
+ User->>Browser: Trigger signing action
+ Browser->>AppServer: GET /typed-data/prepare
+ AppServer-->>Browser: EIP-712 payload
+
+ Browser->>SDK: eth_signTypedData_v4
+ SDK->>Account: eth_signTypedData_v4(payload)
+ User->>Account: Review and approve signature
+ Account-->>SDK: signature
+ SDK-->>Browser: signature
+
+ Browser-->>AppServer: POST /typed-data/verify {payload, signature}
+ AppServer-->>Browser: verification result
+```
+
+## Implementation
+
+For the purposes of this guide, we'll use a simple example of a typed data payload that contains a permission
+to spend user's funds (see [Spend Permissions](/base-account/improve-ux/spend-permissions))
+
+### Code Snippets
+
+
+```ts Browser (SDK)
+import { createBaseAccountSDK } from "@base-org/account";
+
+// Initialize the SDK
+const provider = createBaseAccountSDK().getProvider();
+
+// 1 — Prepare the typed data payload
+const typedData = {
+ domain: {
+ name: 'Spend Permission Manager',
+ version: '1',
+ chainId: 8453, // or any other supported chain
+ verifyingContract: SPEND_PERMISSION_MANAGER_ADDRESS,
+ },
+ types: {
+ SpendPermission: [
+ { name: 'account', type: 'address' },
+ { name: 'spender', type: 'address' },
+ { name: 'token', type: 'address' },
+ { name: 'allowance', type: 'uint160' },
+ { name: 'period', type: 'uint48' },
+ { name: 'start', type: 'uint48' },
+ { name: 'end', type: 'uint48' },
+ { name: 'salt', type: 'uint256' },
+ { name: 'extraData', type: 'bytes' },
+ ],
+ },
+ primaryType: 'SpendPermission',
+ message: spendPermissionData,
+};
+
+// 2 — Request signature from user
+try {
+ const accounts = await provider.request({
+ method: 'eth_requestAccounts'
+ });
+
+ const signature = await provider.request({
+ method: 'eth_signTypedData_v4',
+ params: [accounts[0], JSON.stringify(typedData)]
+ });
+
+ // 3 — Send to backend for verification
+ const response = await fetch('/typed-data/verify', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({
+ typedData,
+ signature,
+ address: accounts[0]
+ })
+ });
+
+ const result = await response.json();
+ console.log('Verification result:', result);
+} catch (err) {
+ console.error('Signing failed:', err);
+}
+```
+
+```ts Backend (Viem)
+import { createPublicClient, http } from 'viem';
+import { base } from 'viem/chains';
+
+const client = createPublicClient({
+ chain: base,
+ transport: http()
+});
+
+export async function verifyTypedData(req, res) {
+ const { typedData, signature, address } = req.body;
+
+ try {
+ // Verify the typed data signature
+ const valid = await client.verifyTypedData({
+ address,
+ domain: typedData.domain,
+ types: typedData.types,
+ primaryType: typedData.primaryType,
+ message: typedData.message,
+ signature
+ });
+
+ if (!valid) {
+ return res.status(401).json({ error: 'Invalid signature' });
+ }
+
+ // Additional validation logic here
+ // e.g., check expiry, nonce, permissions, etc.
+ const now = Math.floor(Date.now() / 1000);
+ if (typedData.message.expiry < now) {
+ return res.status(401).json({ error: 'Signature expired' });
+ }
+
+ // Process the verified typed data
+ res.json({
+ valid: true,
+ message: 'Signature verified successfully',
+ data: typedData.message
+ });
+ } catch (error) {
+ console.error('Verification error:', error);
+ res.status(500).json({ error: 'Verification failed' });
+ }
+}
+```
+
+
+## Example Express Server
+
+```ts title="server/typed-data.ts"
+import express from 'express';
+import { createPublicClient, http } from 'viem';
+import { base } from 'viem/chains';
+
+const app = express();
+app.use(express.json());
+
+const client = createPublicClient({
+ chain: base,
+ transport: http()
+});
+
+// Simple nonce store (use Redis/DB in production)
+const usedNonces = new Set();
+
+app.get('/typed-data/prepare', (req, res) => {
+ const { userAddress, action, resource } = req.query;
+
+ const nonce = Math.floor(Math.random() * 1000000);
+ const expiry = Math.floor(Date.now() / 1000) + 3600; // 1 hour
+
+ const typedData = {
+ // YOUR TYPED DATA HERE
+ }
+
+ res.json(typedData);
+});
+
+app.post('/typed-data/verify', async (req, res) => {
+ const { typedData, signature, address } = req.body;
+
+ try {
+ // 1. Check nonce hasn't been reused
+ const nonceKey = `${address}-${typedData.message.nonce}`;
+ if (usedNonces.has(nonceKey)) {
+ return res.status(400).json({ error: 'Nonce already used' });
+ }
+
+ // 2. Check expiry
+ const now = Math.floor(Date.now() / 1000);
+ if (typedData.message.expiry < now) {
+ return res.status(400).json({ error: 'Signature expired' });
+ }
+
+ // 3. Verify signature
+ const valid = await client.verifyTypedData({
+ address,
+ domain: typedData.domain,
+ types: typedData.types,
+ primaryType: typedData.primaryType,
+ message: typedData.message,
+ signature
+ });
+
+ if (!valid) {
+ return res.status(401).json({ error: 'Invalid signature' });
+ }
+
+ // 4. Mark nonce as used
+ usedNonces.add(nonceKey);
+
+ // 5. Process the verified action
+ res.json({
+ valid: true,
+ message: 'Typed data verified successfully',
+ action: typedData.message.action,
+ resource: typedData.message.resource
+ });
+ } catch (error) {
+ console.error('Verification error:', error);
+ res.status(500).json({ error: 'Verification failed' });
+ }
+});
+
+app.listen(3001, () => console.log('Typed data server listening on :3001'));
+```
+
+## Best Practices
+
+### Domain Separation
+Always use unique domain parameters to prevent signature replay across different applications:
+
+```tsx
+const domain = {
+ name: 'Your App Name', // Unique app identifier
+ version: '1', // Version your types
+ chainId: 8453, // Network-specific
+ verifyingContract: contractAddr // Contract that will verify
+};
+```
+
+### Nonce Management
+Include nonces to prevent replay attacks:
+
+```tsx
+// Generate unique nonces
+const nonce = crypto.randomBytes(16).toString('hex');
+
+// Store and validate nonces server-side
+const usedNonces = new Set(); // Use Redis/DB in production
+```
+
+### Expiry Times
+Always include expiry timestamps for time-bound signatures:
+
+```tsx
+const expiry = Math.floor(Date.now() / 1000) + 3600; // 1 hour
+```
diff --git a/docs/base-account/guides/sub-accounts/add-sub-accounts-to-onchainkit.mdx b/docs/base-account/guides/sub-accounts/add-sub-accounts-to-onchainkit.mdx
deleted file mode 100644
index 421790e7a..000000000
--- a/docs/base-account/guides/sub-accounts/add-sub-accounts-to-onchainkit.mdx
+++ /dev/null
@@ -1,265 +0,0 @@
----
-title: "Add Sub Accounts to Onchainkit/Minikit"
----
-
-import { GithubRepoCard } from "/snippets/GithubRepoCard.mdx"
-
-
-
-This guide will show you how to add Sub Accounts to your existing [OnchainKit](/onchainkit/getting-started) or [MiniKit](/wallet-app/build-with-minikit/quickstart) project.
-
-Sub Accounts allow you to create a seamless user experience by skipping transaction confirmations,
-you can read more about it in the [Sub Accounts Intro Page](/smart-wallet/guides/sub-accounts).
-
-Before you start, make sure you have checked the following guides:
-
-- [OnchainKit Quickstart](/onchainkit/getting-started)
-- [MiniKit Quickstart](/wallet-app/build-with-minikit/quickstart)
-- [OnchainKit Custom Providers](/onchainkit/config/supplemental-providers)
-- [Sub Accounts Intro Page](/smart-wallet/guides/sub-accounts)
-
-
-**Base Account compatibility with Mini Apps**
-
-The Coinbase Wallet team is working on adding Base Account support to Mini Apps.
-
-For now, you can only use Sub Accounts with OnchainKit/MiniKit outside of the social feed.
-
-
-## Skip ahead
-
-If you want to skip ahead and just get the final code, you can find it here:
-
-
-
-
-**About this codebase**
-
-The codebase uses MiniKit, but the same code can be used for OnchainKit with minimal changes.
-
-The difference is detailed in the [Providers](#setting-up-the-onchainkitminikit-providers-config) section.
-
-
-## Add Sub Accounts to your OnchainKit/MiniKit project
-
-### Override the default Coinbase Wallet SDK version
-
-Currently, Sub Accounts are only available in the Base Account development environment (Canary version).
-
-To override the default Coinbase Wallet SDK version, you can run the following command:
-
-```bash
-npm @coinbase/wallet-sdk@canary install
-```
-
-
-**Re-install the dependencies**
-
-Make sure to delete the `node_modules` folder and `package-lock.json` file before re-installing the dependencies after running the command above.
-
-
-```bash npm
-rm -rf node_modules package-lock.json
-npm install
-```
-
-```bash pnpm
-rm -rf node_modules pnpm-lock.yaml
-pnpm install
-```
-
-```bash yarn
-rm -rf node_modules yarn.lock
-yarn install
-```
-
-```bash bun
-rm -rf node_modules bun.lockb
-bun install
-```
-
-
-
-
-**Sub Accounts are coming to prod soon !**
-
-This is a temporary solution to get Sub Accounts working.
-
-Once the new version of the Coinbase Wallet SDK is released, you can remove this step.
-
-
-### Setting up the Wagmi config
-
-Make sure to update your `wagmi.ts` file to include the following:
-
-```ts wagmi.ts
-import { http, cookieStorage, createConfig, createStorage } from "wagmi";
-import { baseSepolia, base } from "wagmi/chains";
-import { coinbaseWallet } from "wagmi/connectors";
-import { parseEther, toHex } from "viem";
-
-export function getConfig() {
- return createConfig({
- chains: [baseSepolia, base],
- connectors: [
- coinbaseWallet({
- appName: "My Sub Account Mini App Demo",
- preference: {
- keysUrl: "https://keys-dev.coinbase.com/connect",
- options: "smartWalletOnly",
- },
- subAccounts: {
- enableAutoSubAccounts: true,
- defaultSpendLimits: {
- 84532: [ // Base Sepolia Chain ID
- {
- token: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
- allowance: toHex(parseEther('0.01')), // 0.01 ETH
- period: 86400, // 24h
- },
- ],
- },
- },
- }),
- ],
- storage: createStorage({
- storage: cookieStorage,
- }),
- ssr: true,
- transports: {
- [baseSepolia.id]: http(),
- [base.id]: http(),
- },
- });
-}
-
-declare module "wagmi" {
- interface Register {
- config: ReturnType;
- }
-}
-```
-
-Let's break down the key preference parameters:
-
-- `keysUrl`: Points to the development environment for Base Account testing
-- `options: 'smartWalletOnly'`: Ensures only Base Account mode is used
-- `enableAutoSubAccounts: true`: When set to true, automatically creates a Sub Account at connection
-- `defaultSpendLimits`: Configures Spend Limits for Sub Account for a network (eg. Base Sepolia `84532`), including:
-
- - Token address (In this case, `0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE` represents the native ETH)
- - Allowance WEI amount (in Hex)
- - Time period for the allowance (in seconds, e.g., 86400 for 24 hours)
-
-
-**About `keysUrl`**
-
-Sub Accounts are currently only available in the Base Account development environment.
-To use this environment, you need to set the `keysUrl` to `https://keys-dev.coinbase.com/connect` in your configuration.
-
-
-### Setting up the OnchainKit/MiniKit providers config
-
-Once the `wagmi.ts` file is updated, you can update your `providers.tsx` file to include the following:
-
-
-```ts providers.tsx (MiniKit)
-"use client";
-
-import { type ReactNode, useState } from "react";
-import { baseSepolia } from "wagmi/chains";
-import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
-import { MiniKitProvider } from "@coinbase/onchainkit/minikit";
-import { type State, WagmiProvider } from "wagmi";
-import { getConfig } from "@/wagmi";
-
-export function Providers(props: {
- children: ReactNode;
- initialState?: State;
-}) {
- const [config] = useState(() => getConfig());
- const [queryClient] = useState(() => new QueryClient());
-
- return (
-
-
-
- {props.children}
-
-
-
- );
-}
-```
-
-```ts providers.tsx (OnchainKit)
-"use client";
-
-import { type ReactNode, useState } from "react";
-import { baseSepolia } from "wagmi/chains";
-import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
-import { MiniKitProvider } from "@coinbase/onchainkit/minikit";
-import { type State, WagmiProvider } from "wagmi";
-import { getConfig } from "@/wagmi";
-
-export function Providers(props: {
- children: ReactNode;
- initialState?: State;
-}) {
- const [config] = useState(() => getConfig());
- const [queryClient] = useState(() => new QueryClient());
-
- return (
-
-
-
- {props.children}
-
-
-
- );
-}
-```
-
-
-
-**For OnchainKit**
-
-For OnchainKit, use `OnchainKitProvider` instead of `MiniKitProvider` as it's detailed in the
-[OnchainKit Custom Providers page](/onchainkit/config/supplemental-providers).
-
-
-## Run your app
-
-After installing the dependencies as described [above](#override-the-default-coinbase-wallet-sdk-version),
-make sure your environment variables are up to date as per
-the [OnchainKit Quickstart](/onchainkit/getting-started)or
-[MiniKit Quickstart](/wallet-app/build-with-minikit/quickstart).
-
-Then, you can run the app with the following command:
-
-```bash
-npm run dev
-```
-
-Congratulations! You've successfully added Sub Accounts to your OnchainKit/MiniKit project.
-
-If you have any questions, join the _#smart-wallet_ channel on [Discord](https://discord.gg/buildonbase).
-
-import PolicyBanner from "/snippets/PolicyBanner.mdx";
-
-
diff --git a/docs/base-account/guides/sub-accounts/overview.mdx b/docs/base-account/guides/sub-accounts/overview.mdx
deleted file mode 100644
index b0655de9e..000000000
--- a/docs/base-account/guides/sub-accounts/overview.mdx
+++ /dev/null
@@ -1,289 +0,0 @@
----
-title: 'Overview'
-description: 'Overview of Sub Accounts and how to use them with Base Account'
----
-
-import {GithubRepoCard} from "/snippets/GithubRepoCard.mdx"
-
-# Sub Accounts Overview
-
-## What are Sub Accounts?
-
-Base Account's self-custodial design requires a user passkey prompt for each wallet interaction that the user is presented with, such as a transaction or message signing. While this helps ensure the user is aware and required to approve of every interaction with their wallet, this also impacts user experience when using applications that may require frequent wallet interactions.
-
-To support using Base Account with user experiences that require more developer control over the 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 for your user, and can interact with them just as you would with another wallet via the wallet provider, or other popular web3 libraries like OnchainKit, wagmi, viem, etc.
-
-These Sub Accounts are linked to the end user's Base Account through an onchain relationship. When combined with our [Spend Permissions feature](/base-account/improve-ux/spend-permissions), this creates a powerful foundation for provisioning and funding app accounts, securely, while giving you, the developer, ample control over building the user experience that makes the most sense for your application.
-
-
-**Sub Account scope**
-
-Sub Accounts are currently scoped to an application's fully qualified domain name (FQDN).
-This means that a user has one sub account for your application,
-and it cannot be used on other applications on separate domains.
-
-
-## Creating Sub Accounts with wallet_addSubAccount
-
-The `wallet_addSubAccount` RPC method allows you to programmatically create Sub Accounts. Here are some practical examples of how to use it:
-
-### Basic Sub Account Creation
-
-```javascript
-// Create a new Sub Account using the RPC method
-const provider = window.ethereum; // or your preferred provider
-
-async function createSubAccount() {
- try {
- const result = await provider.request({
- method: 'wallet_addSubAccount',
- params: [{
- account: {
- type: 'create',
- keys: [{
- type: 'p256',
- publicKey: '0x0123456789abcdef...' // Your public key
- }]
- }
- }]
- });
-
- console.log('Sub Account created:', result);
- console.log('Address:', result.address);
- console.log('Chain ID:', result.chainId);
-
- return result;
- } catch (error) {
- console.error('Failed to create Sub Account:', error);
- }
-}
-```
-
-### Sub Account Creation with Coinbase Wallet SDK
-
-```javascript
-import { createCoinbaseWalletSDK } from '@coinbase/wallet-sdk';
-
-// Initialize the SDK
-const sdk = createCoinbaseWalletSDK({
- appName: 'My Sub Account App',
- appLogoUrl: 'https://example.com/logo.png',
- appChainIds: [84532], // Base Sepolia
-});
-
-const provider = sdk.getProvider();
-
-async function createSubAccountWithSDK() {
- try {
- // First connect the user's main account
- await provider.request({ method: 'eth_requestAccounts' });
-
- // Create a Sub Account
- const subAccount = await provider.request({
- method: 'wallet_addSubAccount',
- params: [{
- account: {
- type: 'create',
- keys: [{
- type: 'webauthn-p256',
- publicKey: generatePublicKey() // Your key generation logic
- }]
- }
- }]
- });
-
- console.log('Sub Account created with SDK:', subAccount);
- return subAccount;
- } catch (error) {
- console.error('Sub Account creation failed:', error);
- }
-}
-
-function generatePublicKey() {
- // Implement your public key generation logic
- // This would typically involve WebAuthn or similar
- return '0x...'; // placeholder
-}
-```
-
-### Using Sub Accounts for Transactions
-
-Once you have a Sub Account, you can use it for transactions:
-
-```javascript
-async function sendTransactionFromSubAccount(subAccountAddress) {
- try {
- const txHash = await provider.request({
- method: 'eth_sendTransaction',
- params: [{
- from: subAccountAddress,
- to: '0xRecipientAddress...',
- value: '0x16345785d8a0000', // 0.1 ETH in wei
- gas: '0x5208', // 21000 gas
- }]
- });
-
- console.log('Transaction sent from Sub Account:', txHash);
- return txHash;
- } catch (error) {
- console.error('Transaction failed:', error);
- }
-}
-```
-
-### React Hook for Sub Account Management
-
-```javascript
-import { useState, useCallback } from 'react';
-
-function useSubAccount() {
- const [subAccount, setSubAccount] = useState(null);
- const [loading, setLoading] = useState(false);
- const [error, setError] = useState(null);
-
- const createSubAccount = useCallback(async () => {
- setLoading(true);
- setError(null);
-
- try {
- const provider = window.ethereum;
- const result = await provider.request({
- method: 'wallet_addSubAccount',
- params: [{
- account: {
- type: 'create',
- keys: [{
- type: 'p256',
- publicKey: await generateUserPublicKey()
- }]
- }
- }]
- });
-
- setSubAccount(result);
- return result;
- } catch (err) {
- setError(err.message);
- throw err;
- } finally {
- setLoading(false);
- }
- }, []);
-
- return {
- subAccount,
- createSubAccount,
- loading,
- error
- };
-}
-
-async function generateUserPublicKey() {
- // Your key generation implementation
- return '0x...';
-}
-```
-
-## Integration Examples
-
-### Framework-Agnostic Example
-
-```javascript
-class SubAccountManager {
- constructor(provider) {
- this.provider = provider;
- this.subAccounts = new Map();
- }
-
- async createSubAccount(userId) {
- try {
- const result = await this.provider.request({
- method: 'wallet_addSubAccount',
- params: [{
- account: {
- type: 'create',
- keys: [{
- type: 'p256',
- publicKey: await this.generateKeyForUser(userId)
- }]
- }
- }]
- });
-
- this.subAccounts.set(userId, result);
- return result;
- } catch (error) {
- console.error(`Failed to create Sub Account for user ${userId}:`, error);
- throw error;
- }
- }
-
- getSubAccount(userId) {
- return this.subAccounts.get(userId);
- }
-
- async generateKeyForUser(userId) {
- // Implement user-specific key generation
- return `0x${userId}...`; // placeholder
- }
-}
-
-// Usage
-const manager = new SubAccountManager(window.ethereum);
-const subAccount = await manager.createSubAccount('user123');
-```
-
-### Next.js API Route Example
-
-```javascript
-// pages/api/create-sub-account.js
-export default async function handler(req, res) {
- if (req.method !== 'POST') {
- return res.status(405).json({ error: 'Method not allowed' });
- }
-
- try {
- const { publicKey } = req.body;
-
- // This would typically be done on the client side
- // but showing structure for reference
- const subAccountData = {
- account: {
- type: 'create',
- keys: [{
- type: 'p256',
- publicKey: publicKey
- }]
- }
- };
-
- // Return the payload for client-side execution
- res.status(200).json({
- success: true,
- payload: subAccountData
- });
- } catch (error) {
- res.status(500).json({ error: 'Failed to prepare Sub Account creation' });
- }
-}
-```
-
-## Available Guides
-
-For comprehensive implementation guides with specific frameworks:
-
-1. **[Sub Accounts with Wagmi](/base-account/improve-ux/sub-accounts/setup)** - Complete guide for implementing Sub Accounts using Wagmi hooks with NextJS, including project setup, configuration, and transaction handling.
-
-2. **[Add Sub Accounts to OnchainKit](/base-account/guides/sub-accounts/add-sub-accounts-to-onchainkit)** - Learn how to integrate Sub Accounts with OnchainKit for enhanced functionality.
-
-## Demo and Resources
-
-- **Live Demo**: [Sub Accounts Demo](https://sub-account-demo.com)
-- **Complete Code**: [Sub Account Starter Template](https://github.com/base/demos/tree/master/smart-wallet/sub-accounts-demo)
-- **Support**: Reach out on the #smart-wallet channel on [Discord](https://discord.com/invite/buildonbase)
-
-import PolicyBanner from "/snippets/PolicyBanner.mdx";
-
-
\ No newline at end of file
diff --git a/docs/base-account/guides/tips/inspect-txn-simulation.mdx b/docs/base-account/guides/tips/inspect-txn-simulation.mdx
index cb9894707..0c5b4829d 100644
--- a/docs/base-account/guides/tips/inspect-txn-simulation.mdx
+++ b/docs/base-account/guides/tips/inspect-txn-simulation.mdx
@@ -14,7 +14,3 @@ There is a hidden feature which enables you to easily copy transaction simulatio
-
-import PolicyBanner from "/snippets/PolicyBanner.mdx";
-
-
\ No newline at end of file
diff --git a/docs/base-account/guides/tips/popup-tips.mdx b/docs/base-account/guides/tips/popup-tips.mdx
index e796eebaa..576aa36c6 100644
--- a/docs/base-account/guides/tips/popup-tips.mdx
+++ b/docs/base-account/guides/tips/popup-tips.mdx
@@ -35,7 +35,4 @@ Normally, browsers block these programmatic requests to open popups.
- To address this, after the Base Account popup responds to a request, it will linger for 200ms to listen for another incoming request before closing.
- If a request is received *during* this 200ms window, it will be received and handled within the same popup window.
- If a request is received *after* the 200ms window and the popup has closed, opening the Base Account popup will be blocked by the browser.
-
-import PolicyBanner from "/snippets/PolicyBanner.mdx";
-
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/base-account/improve-ux/batch-transactions.mdx b/docs/base-account/improve-ux/batch-transactions.mdx
index dbe1fbadd..88e1fe7c3 100644
--- a/docs/base-account/improve-ux/batch-transactions.mdx
+++ b/docs/base-account/improve-ux/batch-transactions.mdx
@@ -280,7 +280,3 @@ async function sendBatchWithErrorHandling(calls: any[]) {
}
}
```
-
-import PolicyBanner from "/snippets/PolicyBanner.mdx";
-
-
\ No newline at end of file
diff --git a/docs/base-account/improve-ux/magic-spend.mdx b/docs/base-account/improve-ux/magic-spend.mdx
index e9e000e5f..7607ebe07 100644
--- a/docs/base-account/improve-ux/magic-spend.mdx
+++ b/docs/base-account/improve-ux/magic-spend.mdx
@@ -1,5 +1,6 @@
---
-title: "MagicSpend"
+title: "Use Coinbase Balances Onchain"
+description: "How to use Coinbase balances onchain with Base Account"
---
With MagicSpend, Base Account users can use their Coinbase balances onchain. This means users can easily start using onchain apps without needing to onramp funds into their wallet.
@@ -12,9 +13,6 @@ If your app supports Base Account, it should not assume it knows the full balanc
MagicSpend makes onboarding smoother by letting users pay gas or send funds even when their onchain wallet balance is **zero**. Your interface should therefore _never_ disable an action just because the onchain balance is insufficient.
----
-
-##
1. Ensure you have the user’s `address` stored in your component state (from your wallet connection flow).
@@ -76,6 +74,13 @@ export function SendButton({ address }: Props) {
3. Reads `auxiliaryFunds.supported` for the Base chain (`8453`).
4. Enables the button when MagicSpend is available; otherwise shows “Insufficient Balance”.
+## Base Pay integrates Magic Spend by default
+
+Thanks to [Magic Spend](/base-account/improve-ux/magic-spend), [Base Pay](/base-account/guides/accept-payments) allows users to pay with their USDC balance on Coinbase by default.
+
+
+
+
---
## Next steps
@@ -83,6 +88,3 @@ export function SendButton({ address }: Props) {
- Handle loading/error states if you need fine-grained UX
- Combine this check with your existing onchain balance logic for fallback flows
-import PolicyBanner from "/snippets/PolicyBanner.mdx";
-
-
diff --git a/docs/base-account/improve-ux/spend-permissions.mdx b/docs/base-account/improve-ux/spend-permissions.mdx
index f07a8b0fa..8b0dd9909 100644
--- a/docs/base-account/improve-ux/spend-permissions.mdx
+++ b/docs/base-account/improve-ux/spend-permissions.mdx
@@ -1,5 +1,6 @@
---
-title: "Spend Permissions"
+title: "Use Spend Permissions"
+description: "Learn how to use Spend Permissions to allow a trusted spender to spend user assets"
---
## Overview
@@ -10,127 +11,310 @@ After the user signs the permission, the `spender` can initiate transfers within
Read more about the Spend Permission Manager contract and supported chains on [GitHub](https://github.com/coinbase/spend-permissions).
+
+ Spend Permissions for Base App Mini Apps are coming soon and will be supported in a future update.
+
+
+
+If you're using Sub Accounts, learn how Base Account can automatically fund Sub Accounts and optionally skip approval prompts using [Auto Spend Permissions](/base-account/improve-ux/sub-accounts#auto-spend-permissions).
+
## Usage
### Request a Spend Permission
-You create an EIP-712 payload that describes the permission and ask the user to sign it. Store the resulting signature along with the permission data so you can register the permission on-chain later.
+You create an EIP-712 payload that describes the permission and ask the user to sign it. Store the resulting signature along with the permission data so you can register the permission on-chain later. The SDK helper below handles construction and signing for you.
-| Field Name | Type | Description |
-|------------|------|-------------|
-| `account` | `address` | Smart account this spend permission is valid for |
-| `spender` | `address` | Entity that can spend `account`'s tokens |
-| `token` | `address` | Token address (ERC-7528 native token or ERC-20 contract) |
-| `allowance` | `uint160` | Maximum allowed value to spend within each `period` |
-| `period` | `uint48` | Time duration for resetting used `allowance` on a recurring basis (seconds) |
-| `start` | `uint48` | Timestamp this spend permission is valid starting at (inclusive, unix seconds) |
-| `end` | `uint48` | Timestamp this spend permission is valid until (exclusive, unix seconds) |
-| `salt` | `uint256` | Arbitrary data to differentiate unique spend permissions with otherwise identical fields |
-| `extraData` | `bytes` | Arbitrary data to attach to a spend permission which may be consumed by the `spender` |
+| Field Name | Type | Description |
+| ----------- | --------- | ---------------------------------------------------------------------------------------- |
+| `account` | `address` | Smart account this spend permission is valid for |
+| `spender` | `address` | Entity that can spend `account`'s tokens |
+| `token` | `address` | Token address (ERC-7528 native token or ERC-20 contract) |
+| `allowance` | `uint160` | Maximum allowed value to spend within each `period` |
+| `period` | `uint48` | Time duration for resetting used `allowance` on a recurring basis (seconds) |
+| `start` | `uint48` | Timestamp this spend permission is valid starting at (inclusive, unix seconds) |
+| `end` | `uint48` | Timestamp this spend permission is valid until (exclusive, unix seconds) |
+| `salt` | `uint256` | Arbitrary data to differentiate unique spend permissions with otherwise identical fields |
+| `extraData` | `bytes` | Arbitrary data to attach to a spend permission which may be consumed by the `spender` |
```tsx
-const SPEND_PERMISSION_MANAGER_ADDRESS = '0xf85210B21cC50302F477BA56686d2019dC9b67Ad' as const;
-
-const spendPermissionData = {
- account: '0x...',
- spender: '0x...',
- token: '0x...',
- allowance: '0x...',
- period: '0x...',
- start: new Date().getTime() / 1000,
- end: new Date().getTime() / 1000 + 60 * 60 * 24 * 30, // 30 days
- salt: '0x',
- extraData: '0x',
-}
+import { requestSpendPermission } from "@base-org/account/spend-permission";
+import { createBaseAccountSDK } from "@base-org/account";
+import { base } from "viem/chains";
-const eip712Data = {
- domain: {
- name: 'Spend Permission Manager',
- version: '1',
- chainId: 8453, // or any other supported chain
- verifyingContract: SPEND_PERMISSION_MANAGER_ADDRESS,
- },
- types: {
- SpendPermission: [
- { name: 'account', type: 'address' },
- { name: 'spender', type: 'address' },
- { name: 'token', type: 'address' },
- { name: 'allowance', type: 'uint160' },
- { name: 'period', type: 'uint48' },
- { name: 'start', type: 'uint48' },
- { name: 'end', type: 'uint48' },
- { name: 'salt', type: 'uint256' },
- { name: 'extraData', type: 'bytes' },
- ],
- },
- primaryType: 'SpendPermission',
- message: spendPermissionData,
-};
-
-// Request a signature from the user
-const signature = await provider.request({
- method: 'eth_signTypedData_v4',
- params: [account, JSON.stringify(eip712Data)],
+const sdk = createBaseAccountSDK({
+ appName: 'Base Account SDK Demo',
+ appLogoUrl: 'https://base.org/logo.png',
+ appChainIds: [base.id],
});
-console.log('Spend Permission signature:', signature);
+const permission = await requestSpendPermission({
+ account: "0x...",
+ spender: "0x...",
+ token: "0x...",
+ chainId: 8453, // or any other supported chain
+ allowance: 1_000_000n,
+ periodInDays: 30,
+ provider: sdk.getProvider(),
+});
+
+console.log("Spend Permission:", permission);
```
### Use the Spend Permission
-Using a permission is a two-step flow:
+Using a permission is 2 steps:
+
+1. **Prepare the calls** — Call `prepareSpendCallData` with the permission and the requested `amount`.
+2. **Submit the calls** — Submit the calls using your app's spender account.
+
+`prepareSpendCallData` returns an array of calls needed to spend the tokens:
+
+- `approveWithSignature` — When the permission is not yet registered onchain, this call would be prepended to the `spend` call.
+- `spend` — The call to spend the tokens from the user's Base Account.
+
+```tsx
+import { prepareSpendCallData } from "@base-org/account/spend-permission";
+
+// returns [approveWithSignatureCall, spendCall]
+const spendCalls = await prepareSpendCallData({
+ permission,
+ amount, // optional; omit to spend the remaining allowance
+});
+
+// If your app spender account supports wallet_sendCalls, submit them in batch using wallet_sendCalls
+// this is an example on how to do it using wallet_sendCalls in provider interface
+await provider.request({
+ method: "wallet_sendCalls",
+ params: [
+ {
+ version: "2.0",
+ atomicRequired: true,
+ from: spender,
+ calls: spendCalls,
+ },
+ ],
+});
+
+// If your app spender account doesn't support wallet_sendCalls, submit them in order using eth_sendTransaction
+// this is an example on how to do it using eth_sendTransaction in provider interface
+await Promise.all(
+ spendCalls.map((call) =>
+ provider.request({
+ method: "eth_sendTransaction",
+ params: [
+ {
+ ...call,
+ from: spender,
+ },
+ ],
+ })
+ )
+);
+```
-1. **Register the permission** — Call `approveWithSignature` with the signed payload to store the permission on-chain.
-2. **Spend tokens** — From the `spender` address, call `spend` with the same payload and an `amount`. The contract validates the permission and transfers tokens from the user's account to the `spender`.
+
+**About the `spendCalls` array**
-You can batch the two calls into a single transaction (see the example below), or send them separately. Registration only needs to happen once per permission.
+This array has 2 calls when submitting the permission onchain for *the first time*.
+When the permission is already registered onchain, this array has only 1 call (the `spend` call).
-The easiest way to encode these function calls is to use a tool such as [viem](https://viem.sh/docs/contract/encodeFunctionData#encodefunctiondata).
+For most use cases, you don't need to worry about this.
+
-Find the ABI for the Spend Permission Manager contract [here](https://github.com/ilikesymmetry/spend-permissions-quickstart/blob/main/lib/abi/SpendPermissionManager.ts) or on [Basescan](https://basescan.org/address/0xf85210B21cC50302F477BA56686d2019dC9b67Ad#code).
+### Revoke a Spend Permission
+You can revoke a permission in two ways:
+
+- Request user approval via request to user's Base Account using `requestRevoke`.
+- Revoke silently from your app's spender by submitting the call returned from `prepareRevokeCallData`.
```tsx
-const SPEND_PERMISSION_MANAGER_ADDRESS = '0xf85210B21cC50302F477BA56686d2019dC9b67Ad' as const;
-const spendPermissionManagerAbi = [...]; // See the ABI file above
-
-// Approval call
-const approvalCallData = encodeFunctionData({
- abi: spendPermissionManagerAbi,
- functionName: 'approveWithSignature',
- args: [spendPermissionData, signature],
+import {
+ requestRevoke,
+ prepareRevokeCallData,
+} from "@base-org/account/spend-permission";
+
+// Option A: User-initiated revoke (wallet popup)
+try {
+ const hash = await requestRevoke(permission);
+ console.log("Revoke succeeded", hash);
+} catch {
+ console.warn("Revoke was rejected or failed");
+}
+
+// Option B: Silent revoke by your app's spender account
+const revokeCall = await prepareRevokeCallData(permission);
+
+// Submit the revoke call using your app's spender account
+// this is an example on how to do it using wallet_sendCalls in provider interface
+await provider.request({
+ method: "wallet_sendCalls",
+ params: [
+ {
+ version: "2.0",
+ atomicRequired: true,
+ from: spender,
+ calls: [revokeCall],
+ },
+ ],
});
-// Spend call
-const spendCallData = encodeFunctionData({
- abi: spendPermissionManagerAbi,
- functionName: 'spend',
- args: [spendPermissionData, amount],
+// If your app spender account doesn't support wallet_sendCalls, submit the revoke call using eth_sendTransaction
+// this is an example on how to do it using eth_sendTransaction in provider interface
+await provider.request({
+ method: "eth_sendTransaction",
+ params: [
+ {
+ ...revokeCall,
+ from: spender,
+ },
+ ],
});
+```
-// Send the calls from the spender wallet using an EIP-1193 wallet provider
-// const provider = ...
+## API Reference
-// This combines the approval and spend calls into a single transaction
-// but you can also send them separately and the approval only needs to be done once
-const callsId = await provider.request({
- method: 'wallet_sendCalls',
+- [requestSpendPermission](/base-account/reference/spend-permission-utilities/requestSpendPermission)
+- [prepareSpendCallData](/base-account/reference/spend-permission-utilities/prepareSpendCallData)
+- [requestRevoke](/base-account/reference/spend-permission-utilities/requestRevoke)
+- [prepareRevokeCallData](/base-account/reference/spend-permission-utilities/prepareRevokeCallData)
+- [fetchPermissions](/base-account/reference/spend-permission-utilities/fetchPermissions)
+- [getPermissionStatus](/base-account/reference/spend-permission-utilities/getPermissionStatus)
+
+## Complete Integration Example
+
+```typescript
+import {
+ fetchPermissions,
+ getPermissionStatus,
+ prepareSpendCallData,
+ requestSpendPermission,
+ requestRevoke,
+ prepareRevokeCallData,
+} from "@base-org/account/spend-permission";
+
+import { createBaseAccountSDK } from "@base-org/account";
+import { base } from "viem/chains";
+
+const sdk = createBaseAccountSDK({
+ appName: 'Base Account SDK Demo',
+ appLogoUrl: 'https://base.org/logo.png',
+ appChainIds: [base.id],
+});
+
+const spender = "0xAppSpenderAddress";
+
+// 1) Fetch available permissions
+const permissions = await fetchPermissions({
+ account: "0xUserBaseAccountAddress",
+ chainId: 84532,
+ spender,
+ provider: sdk.getProvider(),
+});
+
+// ========================================
+// When there IS an existing permission
+// ========================================
+
+// 2. find the permission to use, potentially filtering by token
+const permission = permissions.at(0);
+
+// 3. check the status of permission
+try {
+ const { isActive, remainingSpend } = await getPermissionStatus(permission);
+ const amount = 1000n;
+
+ if (!isActive || remainingSpend < amount) {
+ throw new Error("No spend permission available");
+ }
+} catch {
+ throw new Error("No spend permission available");
+}
+
+// 4. prepare the calls
+const [approveCall, spendCall] = await prepareSpendCallData({
+ permission,
+ amount,
+});
+
+// 5. execute the calls using your app's spender account
+// this is an example using wallet_sendCalls, in production it could be using eth_sendTransaction.
+await provider.request({
+ method: "wallet_sendCalls",
params: [
{
version: "2.0",
atomicRequired: true,
from: spender,
- calls: [
- { to: SPEND_PERMISSION_MANAGER_ADDRESS, data: approvalCallData, value: '0x0' },
- { to: SPEND_PERMISSION_MANAGER_ADDRESS, data: spendCallData, value: '0x0' },
- ],
- }
+ calls: [approveCall, spendCall],
+ },
],
-})
-```
+});
+// ========================================
+// When there is NOT an existing permission
+// ========================================
-import PolicyBanner from "/snippets/PolicyBanner.mdx";
+// 2. request a spend permission to use
+const newPermission = await requestSpendPermission({
+ account: "0xUserBaseAccountAddress",
+ spender,
+ token: "0xTokenContractAddress",
+ chainId: 84532,
+ allowance: 1_000_000n,
+ periodInDays: 30,
+ provider: sdk.getProvider(),
+});
+
+// 3. prepare the calls
+const spendCalls = await prepareSpendCallData({
+ permission: newPermission,
+ amount: 1_000n,
+});
+
+// 4. execute the calls using your app's spender account
+// this is an example using eth_sendTransaction. If your app account supports wallet_sendCalls, use wallet_sendCalls to batch the calls instead.
+await Promise.all(
+ spendCalls.map((call) =>
+ provider.request({
+ method: "eth_sendTransaction",
+ params: [
+ {
+ ...call,
+ from: spender,
+ },
+ ],
+ })
+ )
+);
+
+// ========================================
+// Request user to revoke spend permission
+// ========================================
+
+try {
+ const hash = await requestRevoke(permission);
+ console.log("Revoke succeeded", hash);
+} catch {
+ throw new Error("Revoke failed");
+}
+
+// ========================================
+// Revoke spend permission in the background
+// ========================================
-
\ No newline at end of file
+const revokeCall = await prepareRevokeCallData(permission);
+
+await provider.request({
+ method: "wallet_sendCalls",
+ params: [
+ {
+ version: "2.0",
+ atomicRequired: true,
+ from: spender,
+ calls: [revokeCall],
+ },
+ ],
+});
+```
diff --git a/docs/base-account/improve-ux/sponsor-gas/erc20-paymasters.mdx b/docs/base-account/improve-ux/sponsor-gas/erc20-paymasters.mdx
index da183b0c3..6d525fb99 100644
--- a/docs/base-account/improve-ux/sponsor-gas/erc20-paymasters.mdx
+++ b/docs/base-account/improve-ux/sponsor-gas/erc20-paymasters.mdx
@@ -1,5 +1,6 @@
---
-title: "Sponsor Transactions (with any token)"
+title: "Pay Gas in ERC20 tokens"
+description: "Base Account enables users to pay for gas in ERC20 tokens"
---
@@ -186,7 +187,3 @@ The request contains the entrypoint and the chain id with optional context.
}
}
```
-
-import PolicyBanner from "/snippets/PolicyBanner.mdx";
-
-
\ No newline at end of file
diff --git a/docs/base-account/improve-ux/sponsor-gas/paymasters.mdx b/docs/base-account/improve-ux/sponsor-gas/paymasters.mdx
index bc973548f..9073365e7 100644
--- a/docs/base-account/improve-ux/sponsor-gas/paymasters.mdx
+++ b/docs/base-account/improve-ux/sponsor-gas/paymasters.mdx
@@ -1,5 +1,6 @@
---
-title: "Sponsor Transactions (with ETH)"
+title: "Sponsor Gas"
+description: "Use Paymasters to sponsor your users' transactions"
---
One of the biggest UX enhancements unlocked by Base Account is the ability for app developers to sponsor their users' transactions.
@@ -21,7 +22,7 @@ the [Base Go Gasless page](/cookbook/go-gasless).
**ERC-7677-Compliant Paymaster Providers**
- If you choose to use a different Paymaster service provider, ensure they are [ERC-7677-compliant](https://www.erc7677.xyz/ecosystem/Paymasters).
+ If you choose to use a different Paymaster service provider, ensure they are [ERC-7677-compliant](https://www.erc7677.xyz/ecosystem/paymasters).
@@ -298,7 +299,3 @@ the [Base Go Gasless page](/cookbook/go-gasless).
-
-import PolicyBanner from "/snippets/PolicyBanner.mdx";
-
-
\ No newline at end of file
diff --git a/docs/base-account/improve-ux/sub-accounts.mdx b/docs/base-account/improve-ux/sub-accounts.mdx
index c21d680f6..a897745dc 100644
--- a/docs/base-account/improve-ux/sub-accounts.mdx
+++ b/docs/base-account/improve-ux/sub-accounts.mdx
@@ -1,5 +1,5 @@
---
-title: 'Sub Accounts'
+title: 'Use Sub Accounts'
description: 'Learn how to create and manage Sub Accounts using Base Account SDK'
---
@@ -125,6 +125,34 @@ This is what the user will see when prompted to create a Sub Account:
+### 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);
+```
+
+
+
+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.
+
### Get Existing Sub Account
@@ -232,6 +260,49 @@ const ownerAccount = await sdk.subAccount.addOwner({
console.log('Owner added to Sub Account');
```
+## 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.
+
+### Configuration
+
+Auto Spend Permissions is only available in SDK version `2.0.2-canary.20250822164845` or later. This feature is **enabled by default** when using Sub Accounts.
+
+To disable Auto Spend Permissions when using Sub Accounts, set `unstable_enableAutoSpendPermissions` to `false` in your SDK configuration:
+
+```tsx
+const sdk = createBaseAccountSDK({
+ appName: 'Base Account SDK Demo',
+ appLogoUrl: 'https://base.org/logo.png',
+ appChainIds: [base.id],
+ subAccounts: {
+ unstable_enableAutoSpendPermissions: false, // Disable auto spend permissions
+ }
+});
+```
+
+### How it works
+
+**First-time transaction flow:**
+When a Sub Account attempts its first transaction, Base Account displays a popup for user approval. During this approval process, Base Account:
+
+- Automatically detects any missing tokens (native or ERC-20) needed for the transaction
+- Requests a transfer of the required funds from the parent Base Account to the Sub Account to fulfill the current transaction
+- Allows the user to optionally grant ongoing spend permissions for future transactions in that token
+
+**Subsequent transactions:**
+If the user granted spend permissions, future transactions follow this priority:
+
+1. First, attempt using existing Sub Account balances and granted spend permissions
+2. If insufficient, prompt the user to authorize additional transfers and/or spend permissions from their Base Account
+
+
+Spend permission requests are limited to the first token when multiple transfers are needed for a single transaction. Additional tokens require separate approvals.
+
+
+
+
+
## Complete Integration Example
Here's a full React component that demonstrates Sub Account creation and usage:
@@ -559,7 +630,4 @@ export default function SubAccountDemo() {
);
}
-```
-import PolicyBanner from "/snippets/PolicyBanner.mdx";
-
-
\ No newline at end of file
+```
\ No newline at end of file
diff --git a/docs/base-account/llms-full.txt b/docs/base-account/llms-full.txt
new file mode 100644
index 000000000..ded4ac1fb
--- /dev/null
+++ b/docs/base-account/llms-full.txt
@@ -0,0 +1,226 @@
+# https://docs.base.org/base-account/llms-full.txt
+
+## Base Account — Deep Guide for LLMs
+
+> Base Account is a passkey‑secured, ERC‑4337 smart wallet with universal sign‑in, USDC payments, sponsored gas, batch transactions, spend permissions, and sub‑accounts.
+
+### What you can do here
+- Add Base Account to web and mobile apps
+- Authenticate users with SIWE + ERC‑6492
+- Accept USDC with Base Pay and sponsor gas with a paymaster
+- Use batch transactions and ERC‑20 gas payments
+- Grant revocable spend permissions and create app‑scoped sub‑accounts
+
+## Minimal Critical Code (provider + pay)
+```ts
+import { createBaseAccountSDK, pay, getPaymentStatus } from '@base-org/account'
+
+const provider = createBaseAccountSDK().getProvider()
+const { id } = await pay({ amount: '5.00', to: '0xRecipient', testnet: true })
+const { status } = await getPaymentStatus({ id })
+```
+
+## Navigation (with brief descriptions)
+
+### Introduction
+- [What is Base Account?](./overview/what-is-base-account) — Overview
+
+### Quickstart
+- [Web (Next.js)](./quickstart/web) — Web integration
+- [Web (React)](./quickstart/web-react) — React example
+- [React Native Integration](./quickstart/mobile-integration) — Mobile
+
+### Guides
+- [Authenticate Users](./guides/authenticate-users) — SIWE + ERC‑6492
+- [Accept Payments](./guides/accept-payments) — Base Pay
+- [Batch Transactions](./improve-ux/batch-transactions) — Multi‑call
+- [Paymasters](./improve-ux/sponsor-gas/paymasters) — Sponsor gas
+- [ERC‑20 Paymasters](./improve-ux/sponsor-gas/erc20-paymasters) — ERC‑20 gas
+- [Spend Permissions](./improve-ux/spend-permissions) — Spending
+- [Sub‑Accounts](./improve-ux/sub-accounts) — App‑scoped accounts
+- [MagicSpend](./improve-ux/magic-spend) — Coinbase balance spend
+- [Sign & Verify Data](./guides/sign-and-verify-typed-data) — Signatures
+
+### Framework Integrations
+- [Wagmi Setup](./framework-integrations/wagmi/setup) — Wagmi
+- [Wagmi Sign in with Base](./framework-integrations/wagmi/sign-in-with-base) — Auth
+- [Wagmi Base Pay](./framework-integrations/wagmi/base-pay) — Payments
+- [Wagmi Other Use Cases](./framework-integrations/wagmi/other-use-cases) — Patterns
+- [Privy Setup](./framework-integrations/privy/setup) — Privy
+- [Privy Sub‑Accounts](./framework-integrations/privy/sub-accounts) — Privy sub‑accounts
+- [Dynamic](./framework-integrations/nextjs-with-dynamic) — Dynamic integration
+
+### Reference (selected)
+- [SDK: createBaseAccount](./reference/core/createBaseAccount)
+- [SDK: getProvider](./reference/core/getProvider)
+- [Provider RPC Methods](./reference/core/provider-rpc-methods/request-overview)
+- [Capabilities](./reference/core/capabilities/overview)
+- [UI Elements](./reference/ui-elements/brand-guidelines)
+- [Onchain Contracts](./reference/onchain-contracts/smart-wallet)
+
+### More
+- [Troubleshooting](./more/troubleshooting/usage-details/popups)
+- [Base Gasless Campaign](./more/base-gasless-campaign)
+- [Telemetry](./more/telemetry)
+- [Migration Guide](./guides/migration-guide)
+
+### Basenames
+- [FAQ](./basenames/basenames-faq)
+- [Transfer](./basenames/basename-transfer)
+- [OnchainKit Tutorial](./basenames/basenames-onchainkit-tutorial)
+- [Wagmi Tutorial](./basenames/basenames-wagmi-tutorial)
+
+### Contribute
+- [Contribute](./contribute/contribute-to-base-account-docs)
+- [Security + Bug Bounty](./contribute/security-and-bug-bounty)
+
+
+## Quickstart (excerpts)
+
+Source: `https://docs.base.org/base-account/quickstart/web`
+
+Base Account lets you add a passkey‑secured ERC‑4337 smart account to your app, with sponsored gas, batch transactions, spend permissions, and sub‑accounts.
+
+Install and initialize:
+
+```bash
+npm install @base-org/account
+```
+
+```ts
+import { createBaseAccount } from '@base-org/account'
+
+const account = await createBaseAccount({
+ owner: '0xYourEOA',
+ chain: 'base-sepolia'
+})
+```
+
+Send a payment with Base Pay (testnet):
+
+```ts
+import { pay, getPaymentStatus } from '@base-org/account'
+
+const { id } = await pay({ amount: '5.00', to: '0xRecipient', testnet: true })
+const { status } = await getPaymentStatus({ id })
+```
+
+Batch two calls in one user operation:
+
+```ts
+const result = await account.provider.request({
+ method: 'wallet_sendCalls',
+ params: [{
+ calls: [
+ { to: USDC, data: erc20.approve(SPENDER, AMOUNT) },
+ { to: MERCHANT, data: erc20.transfer(MERCHANT, AMOUNT) }
+ ]
+ }]
+})
+```
+
+Expected result: a single ERC‑4337 userOp executes both calls atomically. Combine with a paymaster to sponsor user gas.
+
+Additional sources:
+- `https://docs.base.org/base-account/quickstart/mobile-integration`
+- `https://docs.base.org/base-account/framework-integrations/wagmi/setup`
+
+
+## Key Concepts (excerpts)
+
+Source: `https://docs.base.org/base-account/overview/what-is-base-account`
+
+- Ownership model: A Base Account is owned by an EOA or another smart wallet. Ownership can be rotated via recovery flows, and sub‑accounts can be created for app‑scoped spend.
+- Recovery: Supports social/device delegates with threshold approval. Ownership only changes after quorum is met and verification passes.
+- Nonces and batching: User operations increment nonces. Use batching to execute multi‑step flows in one atomic op and improve UX.
+- Gas abstraction: Integrate a paymaster to sponsor gas, including ERC‑20‑denominated gas, for gasless user experiences.
+- Spend permissions: Grant revocable, scoped allowances for specific contracts, functions, or limits to reduce approve‑forever risk.
+- Sub‑accounts: Create scoped accounts for apps/contexts to contain risk and simplify accounting.
+- Security assumptions: Only configured owner(s) can authorize changes. Recovery requires meeting your policy’s threshold.
+
+Additional sources:
+- `https://docs.base.org/base-account/overview/architecture`
+- `https://docs.base.org/base-account/improve-ux/spend-permissions`
+- `https://docs.base.org/base-account/improve-ux/sponsor-gas/paymasters`
+- `https://docs.base.org/base-account/improve-ux/batch-transactions`
+
+
+## API Reference (selected, pruned)
+
+Provider RPC methods (subset)
+
+- `wallet_sendCalls(params)` — Execute one or more calls in a single ERC‑4337 user operation. Returns a user operation hash.
+ - Source: `https://docs.base.org/base-account/reference/core/provider-rpc-methods/wallet_sendCalls`
+- `wallet_getCapabilities()` — Return wallet features and limits supported by the provider.
+ - Source: `https://docs.base.org/base-account/reference/core/provider-rpc-methods/request-overview`
+
+SDK helpers (subset)
+
+- `createBaseAccount(options)` — Initialize SDK and get a configured provider and helpers
+ - Source: `https://docs.base.org/base-account/reference/core/createBaseAccount`
+- `getProvider()` — Access the EIP‑1193 provider wired to Base Account
+ - Source: `https://docs.base.org/base-account/reference/core/getProvider`
+- `createSubAccount({ label })` — Create an app‑scoped sub‑account
+ - Source: `https://docs.base.org/base-account/reference/core/capabilities/overview`
+- `pay({ amount, to, testnet })` and `getPaymentStatus({ id })` — Base Pay helpers for USDC flows
+ - Source: `https://docs.base.org/base-account/guides/accept-payments`
+
+Minimal request example for `wallet_sendCalls`:
+
+```json
+{
+ "method": "wallet_sendCalls",
+ "params": [
+ {
+ "calls": [
+ { "to": "0xA0b8...USDC", "data": "0x095ea7b3..." },
+ { "to": "0xMerchant", "data": "0xa9059cbb..." }
+ ],
+ "sponsor": { "type": "paymaster" }
+ }
+ ]
+}
+```
+
+
+## Examples (common flows)
+
+Example: Authenticate user with SIWE + ERC‑6492
+
+Source: `https://docs.base.org/base-account/guides/authenticate-users`
+
+```ts
+// Sign‑in with Ethereum using a 6492‑compatible signature
+const message = createSiweMessage({ domain, address, statement, uri, version: '1', chainId })
+const signature = await provider.request({ method: 'personal_sign', params: [message, address] })
+// Verify server‑side with 6492 envelope support
+```
+
+Example: USDC checkout with paymaster sponsorship
+
+Sources:
+- `https://docs.base.org/base-account/guides/accept-payments`
+- `https://docs.base.org/base-account/improve-ux/sponsor-gas/paymasters`
+
+```ts
+await provider.request({
+ method: 'wallet_sendCalls',
+ params: [{
+ calls: [
+ { to: USDC, data: erc20.approve(MERCHANT, AMOUNT) },
+ { to: MERCHANT, data: erc20.transfer(MERCHANT, AMOUNT) }
+ ],
+ sponsor: { type: 'paymaster', token: 'USDC' }
+ }]
+})
+```
+
+Example: Create and use a sub‑account for scoped spend
+
+Source: `https://docs.base.org/base-account/improve-ux/sub-accounts`
+
+```ts
+const sub = await account.createSubAccount({ label: 'checkout' })
+await sub.provider.request({ method: 'wallet_sendCalls', params: [{ calls: [{ to: MERCHANT, data: data }] }] })
+```
+
diff --git a/docs/base-account/llms.txt b/docs/base-account/llms.txt
index dbee4e0c2..14dce0449 100644
--- a/docs/base-account/llms.txt
+++ b/docs/base-account/llms.txt
@@ -2,393 +2,39 @@
## Base Account Documentation
-This file contains structured documentation for AI assistants about Base Account. Base Account is a multi-chain self-custodial cryptocurrency wallet that enables users to create an account in seconds with no app or extension required, thanks to its reliance on Passkeys.
+> Base Account is a passkey-secured, ERC-4337 smart wallet with one‑tap payments, spend permissions, and sub‑accounts—built for seamless multi‑chain apps.
-Please refer to https://docs.base.org/llms-full.txt for the complete Base documentation.
+## Introduction
+- [What is Base Account?](./overview/what-is-base-account) — Core concepts and benefits
-# Quick Start
-You can try Base Account today on [any supported chain](./overview/what-is-base-account#supported-networks)! Checkout our guides:
-- [Quick Demo using OnchainKit (5 mins)](./quickstart/quick-demo)
-- [Add to Existing Next.js Project (15 mins)](./quickstart/web)
-- [Add to Existing React Native Project](./quickstart/mobile-integration)
+## Quickstart
+- [Web (Next.js)](./quickstart/web) — Add Base Account to a web app
+- [React Native Integration](./quickstart/mobile-integration) — Mobile setup and flows
-Reach out to us in the `#base-account` channel on [Discord](https://discord.com/invite/buildonbase) if you have any questions.
+## Guides
+- [Authenticate Users](./guides/authenticate-users) — SIWE with ERC‑6492 signatures
+- [Accept Payments](./guides/accept-payments) — One‑tap USDC payments with Base Pay
-# What is Base Account?
+## Framework Integrations
+- [Wagmi: Setup](./framework-integrations/wagmi/setup) — Configure connectors and chains
+- [Privy: Setup](./framework-integrations/privy/setup) — Authentication + sub‑accounts
-Base Account is a Smart-Wallet-backed account that gives every user:
+## Reference
+- [Account SDK: createBaseAccount](./reference/core/createBaseAccount) — Initialize SDK and provider
+- [Provider RPC: wallet_sendCalls](./reference/core/provider-rpc-methods/wallet_sendCalls) — Batch and sponsored calls
-* **Universal sign-on** – one passkey works across every Base-enabled app.
-* **One-tap payments** – low-friction USDC payments built into the account layer.
-* **Private profile vault** – opt-in sharing of email, phone, shipping address, and more.
-* **Multi-chain support** – one address that works across nine EVM networks (and counting).
+## More
+- [Troubleshooting: Popups](./more/troubleshooting/usage-details/popups) — Browser settings and flows
+- [Troubleshooting: Gas Usage](./more/troubleshooting/usage-details/gas-usage) — Cost breakdown and tips
-Under the hood, each Base Account is an ERC-4337 Smart Wallet that can be deployed on any EVM-compatible chain; nine EVM mainnet chains are enabled out of the box, including Base Mainnet.
+## Basenames
+- [Basenames FAQ](./basenames/basenames-faq) — Naming, transfer, and primary name setup
-# Recommended Libraries
+## Contribute
+- [Contribute to Docs](./contribute/contribute-to-base-account-docs) — How to propose changes and fixes
-| Library | Description |
-|---------|-------------|
-| [OnchainKit](https://onchainkit.xyz/) | Ready made React components for wallet connection, transacting, and more. |
-| [Wagmi](https://wagmi.sh/) | React and Vite tools that provides hooks for wallet connection, contract interaction, and more.|
-| [Viem](https://viem.sh/) | TypeScript Interface for Ethereum that provides low-level primitives and wallet client abstractions. |
+## Optional
+- [Base Gasless Campaign](./more/base-gasless-campaign) — Incentives for sponsored gas integrations
+- [Telemetry](./more/telemetry) — Anonymous metrics and privacy details
-## Wallet Aggregators
-[Dynamic](https://docs.dynamic.xyz/wallets/advanced-wallets/coinbase-smart-wallet)
-[Privy](https://docs.privy.io/guide/react/recipes/misc/coinbase-smart-wallets)
-[ThirdWeb](http://portal.thirdweb.com/connect)
-[ConnectKit](https://docs.family.co/connectkit)
-[Web3Modal](https://docs.reown.com/web3modal/react/smart-accounts)
-[Web3-Onboard](https://www.blocknative.com/coinbase-wallet-integration)
-[RainbowKit](https://www.rainbowkit.com/)
-# Base Gasless Campaign
-
-Base is offering gas credits to help developers make the most of Base Account's [paymaster (sponsored transactions)](/base-account/improve-ux/sponsor-gas/paymasters) features.
-
-| Partner Tier | Base Gas Credit Incentive | Requirements | Actions |
-|--------------|---------------------------|--------------|----------|
-| 1 | $15k |
- Support Base Account
- Onboard to Coinbase Paymaster
- Preferred placement in your UI (ie "Sign in with Base" button)
|
1. Integrate Base Account SDK into your app, or bump to latest version of any [supporting wallet library](/base-account/framework-integrations/nextjs-with-wagmi).
2. Sign in / up for [Coinbase Developer Platform](https://www.coinbase.com/developer-platform) (takes less than 2 minutes). No KYC needed - just email and phone.
3. Check out the Paymaster product where the Base Mainnet Paymaster is enabled by default. Set and change your gas policy at any time.
1. Integrate Base Account SDK into your app, or bump to latest version of any [supporting wallet library](/base-account/framework-integrations/nextjs-with-wagmi).
2. Sign in / up for [Coinbase Developer Platform](https://www.coinbase.com/developer-platform) (takes less than 2 minutes). No KYC needed - just email and phone.
3. Check out the Paymaster product where the Base Mainnet Paymaster is enabled by default. Set and change your gas policy at any time.
| Create a demo of your Base Account integration, post on social (Farcaster and/or X) and tag Coinbase Wallet and/or Base|
-
-# Universal Sign-On
-
-Base Account is a universal sign-on for onchain apps. Users bring the same account, identity, and assets across apps.
-
-## How it works
-
-1. Base Account relies on [passkeys](https://passkeys.com), stored on users' devices, which can be used on our website (keys.coinbase.com) and [mobile app](https://www.coinbase.com/wallet).
-2. Our SDK, which developers integrate into their apps, uses keys.coinbase.com popups to allow users to see requests and sign with their passkey.
-3. The SDK and the popup use cross domain messaging to share information back to the app.
-
-# Networks
-
-The Base Account contracts can be [permissionlessly deployed](https://github.com/coinbase/smart-wallet/tree/main?tab=readme-ov-file#deployments) on any EVM-compatible network.
-Our SDK and client support the following networks.
-
-## Mainnet Networks
-- Base
-- Arbitrum
-- Optimism
-- Zora
-- Polygon
-- BNB
-- Avalanche
-- Lordchain
-- Ethereum mainnet (not recommended due to costs)
-
-## Testnet Networks
-- Sepolia
-- Base Sepolia
-
-# Passkeys
-
-Passkeys enable instant account creation and seamless authentication for Base Account users, dramatically simplifying onboarding. By leveraging FIDO2-compliant authentication, passkeys eliminate seed phrases while providing enterprise-grade security for both wallet access and onchain ownership.
-
-During account creation, a cryptographic key pair is generated and the private key is securely stored on the user's device, while the public key is registered onchain as an owner of the user's Base Account. The passkey serves two core functions:
-
-1. **Authentication**: Replaces passwords for wallet access.
-2. **Transaction Signing**: Functions as the signing key for onchain transactions, replacing private keys and seed phrases.
-
-## Cross-Device Support
-
-Passkeys leverage platform authenticator APIs for cross-device synchronization through:
-- iCloud Keychain (Apple devices)
-- Google Password Manager
-- 1Password
-- Any WebAuthn-compatible password manager
-
-This enables seamless multi-device support without exposing the underlying cryptographic material.
-
-**Account Recovery Considerations**: Without access to their passkey or a configured recovery key, users will permanently lose wallet access.
-
-# MagicSpend
-
-MagicSpend enables Base Account users to spend their Coinbase balances directly onchain. Users can connect their Coinbase account during the transaction flow, eliminating the need for manual onramps or transfers.
-
-## Benefits for Your App
-- Remove funding friction for new users
-- Tap into Coinbase's massive user base
-- Enable instant transactions without waiting for onramps
-
-## Supported Assets and Networks
-- Assets: ETH, USDC
-- Networks: Base
-
-## Best Practices
-Some apps check user's onchain balance and block certain interactions if the user has insufficient funds. This is a problem for MagicSpend users, who can access their Coinbase funds during the transaction flow.
-
-Apps can detect whether the connected address may have other funds accessible via `auxiliaryFunds` capability, which can be discovered via a [`wallet_getCapabilities` RPC call](https://eip5792.xyz/reference/getCapabilities).
-If a user has `auxiliaryFunds`, apps should not block interactions based on onchain balance.
-
-# Base Pay
-
-Base Pay lets you accept USDC payments with a single click—no cards, no FX fees, no chargebacks.
-
-* **Any user can pay** – works with every Base Account out of the box.
-* **USDC, not gas** – you charge in dollars; gas sponsorship is handled automatically.
-* **Fast** – most payments confirm in <2 seconds on Base.
-* **No extra fees** – you receive the full amount.
-
-## Usage
-
-```typescript
-import { createBaseAccountSDK, pay, getPaymentStatus } from '@base-org/account';
-
-// Initialize SDK
-const provider = createBaseAccountSDK().getProvider();
-
-// Trigger a payment
-const { id } = await pay({
- amount: '5.00', // USD amount (we quote USDC internally)
- to: '0xRecipient', // your address
- testnet: true // set false for Mainnet
-});
-
-// Poll until mined
-const { status } = await getPaymentStatus({ id });
-if (status === 'completed') console.log('🎉 payment settled');
-```
-
-# Sponsored Transactions (Paymasters)
-
-Base Account enables apps to pay for users' transaction gas fees, allowing free transactions or removing the need for users to hold native tokens.
-
-## Technical Details
-- Sponsored transactions are facilitated onchain via [ERC-4337](https://eips.ethereum.org/EIPS/eip-4337) compliant paymasters.
-- Apps must have a [ERC-7677](https://eips.ethereum.org/EIPS/eip-7677) compliant paymaster service.
-- Apps can share this paymaster to Base Account via the ERC-7677 `paymasterService` capability on a `wallet_sendCalls` request.
-
-# Spend Permissions
-
-Spend Permissions enable third-party signers to spend assets (native and ERC-20 tokens) from a user's Base Account. Once granted, Spend Permissions allow developers to move users' assets without any further signatures, unlocking use cases like subscriptions & trading bots.
-
-## Technical Details
-
-- Spend Permissions **supports all ERC-20 assets**
-- Spend Permissions **enable spending native tokens**
-- Spend Permissions offers granular **controls for recurrence**, e.g. "10 USDC per week"
-
-# Batch Transactions
-
-Base Account supports batch transactions, which allow developers to perform multiple operations in a single transaction. This is useful for reducing the number of transactions required to perform a complex operation, such as a swap or a multi-step transaction.
-
-## Technical Details
-
-- Batch transactions are facilitated via the [wallet_sendCalls](https://www.eip5792.xyz/reference/sendCalls) RPC.
-
-# ERC20 Paymasters
-
-Base Account enables users to pay for gas in ERC20 tokens! This enables users to use app-specific tokens as payment for gas.
-
-## Technical Details
-
-- We recommend the [Coinbase Developer Platform](https://www.coinbase.com/developer-platform) paymaster, as it is [fully set up to work with Base Account ERC-20 token gas payments](/base-account/improve-ux/sponsor-gas/erc20-paymasters).
-- Developers pay gas for their users in ETH and choose an exchange rate at which to receive an ERC-20 token as payment.
-
-# Sub Accounts
-
-Sub Accounts are hierarchical, app-specific accounts that extend from a user's primary Base Account. Each Sub Account operates as a distinct account that can:
-
-- Execute transactions and sign messages
-- Maintain separate asset management
-- Be fully controlled by both the universal Base Account and authorized application logic
-
-## Technical Details
-
-- Base Account Ownership: The user's Base Account acts as an owner of the Sub Account, allowing it to manage assets on the Sub Account and make calls from it.
-- App-Signer Agnostic: Sub Accounts are designed to be agnostic to whatever signing software an app wants to use: whether in-browser CryptoKey or server signers from teams like Privy or Turnkey.
-- When an app requests a Sub Account creation and the user approves, all future signing and transaction requests will use the Sub Account.
-
-## Sub Account scope
-
-Sub Accounts are currently scoped to an application's fully qualified domain name (FQDN). This means that a user has one sub account for your application, and it cannot be used on other applications on separate domains.
-
-# Signature Verification
-
-There are important details to verifying smart contract wallet signatures. The smart contract itself cannot produce a signature. Instead, the contract has a function:
-
-```solidity
-function isValidSignature(bytes32 _hash, bytes memory _signature) returns (bytes4 magicValue);
-```
-
-which can be called to determine if signature should be considered valid (defined in [EIP-1271](https://eips.ethereum.org/EIPS/eip-1271)).
-
-In the case of Base Account, a signature is considered valid if it was signed by a current signer (aka "owner") of the Base Account. For example, a user can sign a message with their passkey, and when `isValidSignature` is called on their Base Account, it would return that the signature is valid because their passkey is an owner.
-
-There is also an additional complexity: users receive their Base Account address immediately upon passkey registration, and are able to begin signing for their Base Account, but their Base Account is not deployed on a chain until the first transaction on that chain.
-
-[ERC-6492](https://eips.ethereum.org/EIPS/eip-6492) describes how to verify signatures in a way that works for both deployed and undeployed Base Accounts.
-
-## Offchain Verification
-For purely offchain signature verification––such as "Sign-In With Ethereum"––ensure you are using a ERC-6492-compliant signature verification library. We recommend Viem's [`verifyMessage`](https://viem.sh/docs/actions/public/verifyMessage#verifymessage) and [verifyTypedData](https://viem.sh/docs/actions/public/verifyTypedData).
-
-## Onchain Verification
-For signatures that will be used onchain, such as with [Permit2](https://github.com/Uniswap/permit2) or [Seaport](https://github.com/ProjectOpenSea/seaport) developers will need to inspect the signature offchain and remove unneeded ERC-6492 data, if it is present. We recommend using the [parseErc6492Signature](https://viem.sh/docs/utilities/parseErc6492Signature#parseerc6492signature) util from Viem.
-
-# Authentication Flow
-
-Base Account uses the "Sign in with Ethereum" (SIWE) standard for authentication:
-
-1. **No new passwords** – authentication happens with the key the user already controls.
-2. **Nothing to steal or reuse** – each login is a one-off, domain-bound signature that never leaves the user's device.
-3. **Wallet-agnostic** – works in any EIP-1193 wallet and follows the open [EIP-4361](https://eips.ethereum.org/EIPS/eip-4361) standard.
-
-## High-level flow
-
-1. User clicks "Sign in with Base"
-2. App requests a nonce from server
-3. SDK opens popup for user to approve connection with passkey
-4. User approves and receives address, message, and signature
-5. App verifies signature on server and creates session
-
-# SDK Usage
-
-## Installation
-
-```bash
-npm install @base-org/account
-```
-
-## Basic Setup
-
-```typescript
-import { createBaseAccountSDK, base } from '@base-org/account';
-
-const sdk = createBaseAccountSDK({
- appName: 'My App Name',
- appLogoUrl: 'https://example.com/logo.png',
- appChainIds: [base.constants.CHAIN_IDS.base],
-});
-
-const provider = sdk.getProvider();
-```
-
-## Request Handling
-
-Requests are handled in one of three ways:
-
-1. **Sent to the Wallet Service**: Wallet popup, extension, or mobile app
-2. **Handled locally by the SDK**: No external calls
-3. **Passed to default RPC provider**: For the given chain
-
-### Sent to Wallet Service
-
-- eth_ecRecover
-- personal_sign
-- personal_ecRecover
-- eth_signTransaction
-- eth_sendTransaction
-- eth_signTypedData_v1
-- eth_signTypedData_v3
-- eth_signTypedData_v4
-- eth_signTypedData
-- wallet_addEthereumChain
-- wallet_watchAsset
-- wallet_sendCalls
-- wallet_showCallsStatus
-- wallet_connect
-- wallet_addSubAccount
-
-### Handled Locally by SDK
-
-- eth_requestAccounts
-- eth_accounts
-- eth_coinbase
-- net_version
-- eth_chainId
-- wallet_getCapabilities
-- wallet_switchEthereumChain
-
-# Framework Integrations
-
-## Next.js with Wagmi
-
-```typescript
-import { http, createConfig } from 'wagmi'
-import { base, baseSepolia } from 'wagmi/chains'
-import { coinbaseWallet } from 'wagmi/connectors'
-
-const coinbaseConnector = coinbaseWallet({
- appName: 'Your App Name',
- preference: {
- options: 'smartWalletOnly' // Force Base Account usage
- }
-})
-
-export const config = createConfig({
- chains: [base, baseSepolia],
- connectors: [coinbaseConnector],
- transports: {
- [base.id]: http(),
- [baseSepolia.id]: http(),
- },
-})
-```
-
-## Next.js with Privy
-
-Base Account integrates with Privy for additional authentication and wallet management features, including automatic Sub Account creation and spend permissions.
-
-# Migration from Coinbase Wallet SDK
-
-The Base Account SDK is the successor to the Coinbase Wallet SDK. Developers should integrate the Base Account SDK such that users connect via a "Sign in with Base" button.
-
-We recommend adding the Base Account button as an additional option, next to existing "Coinbase Wallet" buttons, to allow a transition period for users to get familiar with the new Base brand.
-
-# Troubleshooting
-
-## Unsupported Calls
-
-Base Account follows the [ERC-4337](https://eips.ethereum.org/EIPS/eip-4337) standard for account abstraction. Some calls are not supported:
-
-- **Self Calls**: For security reasons, we do not allow 3rd party apps to make calls to a user's own Base Account address.
-- **CREATE**: Currently, we do not support the `CREATE` opcode for smart contracts deployment. Use factory contracts or `CREATE2` instead.
-
-## Popup Issues
-
-When Base Account is connected and receives a request, it opens keys.coinbase.com in a popup window:
-
-- **Default blocking behavior**: Most browsers block popups unless triggered by a click
-- **Cross-Origin-Opener-Policy**: Use `same-origin-allow-popups` or `unsafe-none`
-- **Popup linger behavior**: After responding to a request, popup lingers for 200ms to listen for followup requests
-
-# Gas Usage
-
-Base Accounts use more gas for transactions than traditional Ethereum accounts. On L2 networks, the cost difference is a matter of cents. The gas difference is due to:
-
-1. **ERC-4337 Bundling**
-2. **Smart Contract Operations** (including one-time deployment)
-3. **Signature Verification**
-
-## Gas Usage Breakdown
-
-| Operation Type | Traditional Account | Base Account |
-|---------------|------------|--------------|
-| Native Token Transfer | ~21,000 gas | ~100,000 gas |
-| ERC-20 Token Transfer | ~65,000 gas | ~150,000 gas |
-| First Deployment | N/A | ~300,000+ gas |
-
-# Telemetry
-
-Base Account includes an anonymous telemetry system to help improve the developer experience. Participation is optional and can be configured during SDK initialization.
-
-We collect:
-- **Request Success Metrics**: Success and failure rates of wallet requests
-- **Error Events**: Generic error events with operational context
-- **UI Component Usage**: Anonymous metrics on interface functionality
-
-**Privacy First**: No sensitive data—such as private keys, transaction contents, user addresses, or personal information—is ever collected.
-
-# Examples and Demos
-
-- [Coin Your Bangers](https://github.com/base/demos/tree/master/smart-wallet/coin-your-joke) - Sub Accounts demo
-- [Onchain Vibes Store](https://github.com/base/demos/tree/master/smart-wallet/onchain-vibes-store) - Profiles demo
-- [Sub Account MiniKit Template](https://github.com/base/demos/tree/master/smart-wallet/sub-accounts-minikit) - Integration with OnchainKit/MiniKit
-
-# Support
-
-Reach out to us in the `#base-account` channel on [Discord](https://discord.com/invite/buildonbase) if you have any questions.
-
-For more detailed information, visit the complete documentation at https://docs.base.org/base-account/
\ No newline at end of file
diff --git a/docs/base-account/more/base-gasless-campaign.mdx b/docs/base-account/more/base-gasless-campaign.mdx
index 240320827..7440b7a28 100644
--- a/docs/base-account/more/base-gasless-campaign.mdx
+++ b/docs/base-account/more/base-gasless-campaign.mdx
@@ -12,7 +12,3 @@ Base Account's [paymaster (sponsored transactions)](/smart-wallet/concepts/featu
| 2 | $10k |
- Support Coinbase Base Account
- Onboard to Coinbase Paymaster
|
1. Bump your Coinbase SDK to add Coinbase Base Account to your app, or bump to latest version of any [supporting wallet library](/smart-wallet/concepts/usage-details/wallet-library-support).
2. Sign in / up for [Coinbase Developer Platform](https://www.coinbase.com/developer-platform) (takes less than 2 minutes). No KYC needed - just email and phone.
3. Check out the Paymaster product where the Base Mainnet Paymaster is enabled by default. Set and change your gas policy at any time.
| Create a demo of your Coinbase Base Account integration, post on social (Farcaster and/or X) and tag Coinbase Wallet and/or Base |
-
-import PolicyBanner from "/snippets/PolicyBanner.mdx";
-
-
\ No newline at end of file
diff --git a/docs/base-account/more/telemetry.mdx b/docs/base-account/more/telemetry.mdx
index c07a72825..3cd542d1b 100644
--- a/docs/base-account/more/telemetry.mdx
+++ b/docs/base-account/more/telemetry.mdx
@@ -83,7 +83,3 @@ Happy building with Base Account!
— The Base Account team
-
-import PolicyBanner from "/snippets/PolicyBanner.mdx";
-
-
\ No newline at end of file
diff --git a/docs/base-account/more/troubleshooting/usage-details/gas-usage.mdx b/docs/base-account/more/troubleshooting/usage-details/gas-usage.mdx
index 68d211ee3..89bc88b2d 100644
--- a/docs/base-account/more/troubleshooting/usage-details/gas-usage.mdx
+++ b/docs/base-account/more/troubleshooting/usage-details/gas-usage.mdx
@@ -19,7 +19,3 @@ Here's a rough comparison of gas usage per account:
| Native Token Transfer | ~21,000 gas | ~100,000 gas |
| ERC-20 Token Transfer | ~65,000 gas | ~150,000 gas |
| First Deployment | N/A | ~300,000+ gas |
-
-import PolicyBanner from "/snippets/PolicyBanner.mdx";
-
-
\ No newline at end of file
diff --git a/docs/base-account/more/troubleshooting/usage-details/popups.mdx b/docs/base-account/more/troubleshooting/usage-details/popups.mdx
index 593838aeb..bccea3023 100644
--- a/docs/base-account/more/troubleshooting/usage-details/popups.mdx
+++ b/docs/base-account/more/troubleshooting/usage-details/popups.mdx
@@ -35,7 +35,3 @@ Normally, browsers block these programmatic requests to open popups.
- To address this, after the Base Account popup responds to a request, it will linger for 200ms to listen for another incoming request before closing.
- If a request is received *during* this 200ms window, it will be received and handled within the same popup window.
- If a request is received *after* the 200ms window and the popup has closed, opening the Base Account popup will be blocked by the browser.
-
-import PolicyBanner from "/snippets/PolicyBanner.mdx";
-
-
\ No newline at end of file
diff --git a/docs/base-account/more/troubleshooting/usage-details/simulations.mdx b/docs/base-account/more/troubleshooting/usage-details/simulations.mdx
index cb9894707..0c5b4829d 100644
--- a/docs/base-account/more/troubleshooting/usage-details/simulations.mdx
+++ b/docs/base-account/more/troubleshooting/usage-details/simulations.mdx
@@ -14,7 +14,3 @@ There is a hidden feature which enables you to easily copy transaction simulatio
-
-import PolicyBanner from "/snippets/PolicyBanner.mdx";
-
-
\ No newline at end of file
diff --git a/docs/base-account/more/troubleshooting/usage-details/unsupported-calls.mdx b/docs/base-account/more/troubleshooting/usage-details/unsupported-calls.mdx
index 62a9ae988..0a55f501f 100644
--- a/docs/base-account/more/troubleshooting/usage-details/unsupported-calls.mdx
+++ b/docs/base-account/more/troubleshooting/usage-details/unsupported-calls.mdx
@@ -27,6 +27,17 @@ Future versions of Base Account may support it.
You can use a factory contract or a transaction with the `CREATE2` opcode to deploy a smart contract.
-import PolicyBanner from "/snippets/PolicyBanner.mdx";
+## Solidity's Builtin `transfer` function
+
+The `transfer` function is a built-in member of the `address` type in Solidity that can be used to send ETH to an address. Base Account wallets cannot receive ETH using this function.
+This function has long been considered deprecated in favor of `call` by the Solidity community, but some older contracts still use it.
+
+The reason for this is that `transfer` only forwards 2300 gas to the `transfer` call, a protective mechanism that was designed to prevent reentrancy attacks by limiting the amount of
+gas available to a smart contract that might reenter the caller.
+In the modern world of smart contract wallets (including for Base Account), this is often not enough gas for the smart contract's `receive` or `fallback` functions to complete their work,
+causing the transaction to revert.
+
+### Known affected contracts
+
+- The [WETH9 contract](https://basescan.org/token/0x4200000000000000000000000000000000000006) uses `transfer` to send ETH to the user's wallet and therefore Base Accounts cannot directly unwrap ETH from it.
-
\ No newline at end of file
diff --git a/docs/base-account/more/troubleshooting/usage-details/wallet-library-support.mdx b/docs/base-account/more/troubleshooting/usage-details/wallet-library-support.mdx
index 7aa667caf..3db0359b4 100644
--- a/docs/base-account/more/troubleshooting/usage-details/wallet-library-support.mdx
+++ b/docs/base-account/more/troubleshooting/usage-details/wallet-library-support.mdx
@@ -14,7 +14,3 @@ Below are some popular wallet libraries and what we know of their plans for day
| [Web3Modal](https://docs.reown.com/web3modal/react/smart-accounts) | ✅ |
| [Web3-Onboard](https://www.blocknative.com/coinbase-wallet-integration) | ✅ |
| [RainbowKit](https://www.rainbowkit.com/) | ✅ |
-
-import PolicyBanner from "/snippets/PolicyBanner.mdx";
-
-
\ No newline at end of file
diff --git a/docs/base-account/overview/what-is-base-account.mdx b/docs/base-account/overview/what-is-base-account.mdx
index 2c8d7ad20..7ae0197ec 100644
--- a/docs/base-account/overview/what-is-base-account.mdx
+++ b/docs/base-account/overview/what-is-base-account.mdx
@@ -3,7 +3,7 @@ title: "Base Account Overview"
description: "What is a Base Account and how the Base Account SDK lets you add universal sign-in and one-tap USDC payments to any app."
---
-> **TL;DR** – Base Accounts are the onchain identity and account layer powering The Base App. With the **Base Account SDK** you can connect to over one hundred thousand users and unlock authentication and payments with just a few lines of code.
+> **TL;DR** – Base Accounts are the onchain identity and account layer powering the [Base App](https://base.app). With the **Base Account SDK** you can connect to over one hundred thousand users and unlock authentication and payments with just a few lines of code.
## What is a Base Account?
@@ -38,7 +38,3 @@ A Base Account is a Smart-Wallet–backed account that gives every user:
| Authentication flow | [Authenticate users](/base-account/guides/authenticate-users) |
| Accept USDC payments on Base | [Accept payments](/base-account/guides/accept-payments) |
| Deep-dive API shapes | [TypeScript API reference](/base-account/reference/core/getProvider) |
-
-import PolicyBanner from "/snippets/PolicyBanner.mdx";
-
-
\ No newline at end of file
diff --git a/docs/base-account/quickstart.mdx b/docs/base-account/quickstart.mdx
deleted file mode 100644
index 18be953b2..000000000
--- a/docs/base-account/quickstart.mdx
+++ /dev/null
@@ -1,54 +0,0 @@
----
-title: "Get Started with Base Account"
----
-
-Base Account is a multi-chain self-custodial cryptocurrency wallet. It enables users to create an account in seconds
-with no app or extension required, thanks to its reliance on [Passkeys](https://passkeys.com).
-
-This simple guide provides three options for getting started with Base Account:
-
-1. [Quick Demo using OnchainKit (5 mins)](/smart-wallet/quickstart/quick-demo) - The fastest way to see Base Account in action
-2. [Add to Existing Next.js Project (15 mins)](/smart-wallet/quickstart/nextjs-project) - Step-by-step integration with an existing Next.js application using Wagmi
-3. [Add to Existing React Native Project](/smart-wallet/quickstart/react-native-project) - A detailed guide for adding Base Account to an existing React Native project
-
-
-
-
-
-
-
-
-
-
- }
- />
-
-
-
-
-
-
-## Explore More Features
-
-After implementing either option, you can:
-
-- [Use MagicSpend](/smart-wallet/concepts/features/built-in/MagicSpend): Use Coinbase balances onchain;
-- [Get Free Sponsored Transactions](/smart-wallet/concepts/base-gasless-campaign): Use Paymaster to sponsor transactions and get qualified for up to $15k in gas credits;
-- [Support Sub Accounts](/smart-wallet/guides/sub-accounts): Improve user experience with embedded accounts controlled by their Base Account,
- eliminating the need for users to sign a transaction for every onchain action.
-- [Support Spend Permissions](/smart-wallet/guides/spend-permissions): Enable third-party signers to spend user assets
-
-import PolicyBanner from "/snippets/PolicyBanner.mdx";
-
-
\ No newline at end of file
diff --git a/docs/base-account/quickstart/ai-tools-available-for-devs.mdx b/docs/base-account/quickstart/ai-tools-available-for-devs.mdx
index d576587ab..2bf46fa69 100644
--- a/docs/base-account/quickstart/ai-tools-available-for-devs.mdx
+++ b/docs/base-account/quickstart/ai-tools-available-for-devs.mdx
@@ -46,7 +46,7 @@ We keep expanding the list of tools and features, so please check back soon for
\ No newline at end of file
diff --git a/docs/base-account/quickstart/frontend-integration.mdx b/docs/base-account/quickstart/frontend-integration.mdx
deleted file mode 100644
index 424c38b2c..000000000
--- a/docs/base-account/quickstart/frontend-integration.mdx
+++ /dev/null
@@ -1,198 +0,0 @@
----
-title: "Web"
----
-
-This option guides you through adding Base Account to an existing
-[Next.js](https://nextjs.org/) application using [Wagmi](https://wagmi.sh/).
-
-## Step 1: Install Dependencies
-
-Let's start by navigating to your project directory and installing the dependencies:
-
-
-```bash npm
-npm install @coinbase/wallet-sdk wagmi viem @tanstack/react-query
-```
-
-```bash pnpm
-pnpm add @coinbase/wallet-sdk wagmi viem @tanstack/react-query
-```
-
-```bash yarn
-yarn add @coinbase/wallet-sdk wagmi viem @tanstack/react-query
-```
-
-```bash bun
-bun add @coinbase/wallet-sdk wagmi viem @tanstack/react-query
-```
-
-
-## Step 2: Create Wagmi Config
-
-If your project does not have a Wagmi Config, create a file called `wagmi.ts` in the root directory of your project:
-
-```typescript
-import { http, createConfig } from "wagmi";
-import { baseSepolia } from "wagmi/chains";
-import { coinbaseWallet } from "wagmi/connectors";
-
-export const cbWalletConnector = coinbaseWallet({
- appName: "Wagmi Base Account",
- preference: "smartWalletOnly",
-});
-
-export const config = createConfig({
- chains: [baseSepolia],
- // turn off injected provider discovery
- multiInjectedProviderDiscovery: false,
- connectors: [cbWalletConnector],
- ssr: true,
- transports: {
- [baseSepolia.id]: http(),
- },
-});
-
-declare module "wagmi" {
- interface Register {
- config: typeof config;
- }
-}
-```
-
-
-The wagmi config is modified to include the Base Account connector `cbWalletConnector`.
-
-
-## Step 3: Create Providers Component
-
-Create a file called `providers.tsx` in your `app/` directory:
-
-```tsx
-"use client";
-
-import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
-import { useState, type ReactNode } from "react";
-import { WagmiProvider } from "wagmi";
-
-import { config } from "@/wagmi";
-
-export function Providers(props: { children: ReactNode }) {
- const [queryClient] = useState(() => new QueryClient());
-
- return (
-
-
- {props.children}
-
-
- );
-}
-```
-
-## Step 4: Add Providers to Root Layout
-
-Update your root layout file (`app/layout.tsx`):
-
-```tsx
-import "./globals.css";
-import type { Metadata } from "next";
-import type { ReactNode } from "react";
-
-import { Providers } from "./providers";
-
-export const metadata: Metadata = {
- title: "Base Account App",
- description: "Base Account Next.js integration",
-};
-
-export default function RootLayout(props: { children: ReactNode }) {
- return (
-
-
- {props.children}
-
-
- );
-}
-```
-
-## Step 5: Create `ConnectAndSIWE` Component
-
-Create a component for wallet connection and Sign-In With Ethereum (SIWE):
-
-```tsx
-import { useCallback, useEffect, useState } from "react";
-import type { Hex } from "viem";
-import { useAccount, useConnect, useSignMessage } from "wagmi";
-import { SiweMessage } from "siwe";
-import { cbWalletConnector } from "@/wagmi";
-
-export function ConnectAndSIWE() {
- const { connect } = useConnect({
- mutation: {
- onSuccess: (data) => {
- const address = data.accounts[0];
- const chainId = data.chainId;
- const m = new SiweMessage({
- domain: document.location.host,
- address,
- chainId,
- uri: document.location.origin,
- version: "1",
- statement: "Base Account SIWE Example",
- nonce: "12345678",
- });
- setMessage(m);
- signMessage({ message: m.prepareMessage() });
- },
- },
- });
- const account = useAccount();
- const [signature, setSignature] = useState(undefined);
- const { signMessage } = useSignMessage({
- mutation: { onSuccess: (sig) => setSignature(sig) },
- });
- const [message, setMessage] = useState(undefined);
-
- return (
-
-
-
Address: {account.address}
-
Signature: {signature}
-
Message: {message?.prepareMessage()}
-
- );
-}
-```
-
-## Step 6: Use the Component in a Page
-
-Add the component to `app/page.tsx`:
-
-```tsx
-import { ConnectAndSIWE } from '../components/ConnectAndSIWE'
-
-export default function Home() {
- return (
-
-
Base Account Integration
-
-
- )
-}
-```
-
-
-Congratulations! You just integrated Base Account in your app.
-You can now explore more features:
-
-- **[Backend Verification](/base-account/authenticate-users/signature-verification)**: Verify signatures on your backend
-- **[Accept Payments](/base-account/accept-payments/create-payment-request)**: Set up payment processing in your app
-- **[Request User Info](/base-account/accept-payments/requesting-user-info)**: Collect user profile data
-
-
-import PolicyBanner from "/snippets/PolicyBanner.mdx";
-
-
\ No newline at end of file
diff --git a/docs/base-account/quickstart/mobile-integration.mdx b/docs/base-account/quickstart/mobile-integration.mdx
index 1486b1841..8edc4143c 100644
--- a/docs/base-account/quickstart/mobile-integration.mdx
+++ b/docs/base-account/quickstart/mobile-integration.mdx
@@ -205,7 +205,3 @@ return (
Send us feedback on the [Base Discord](https://discord.com/invite/buildonbase/) or create a new issue on the [MobileWalletProtocol/react-native-client](https://github.com/MobileWalletProtocol/react-native-client/issues) repository.
-
-import PolicyBanner from "/snippets/PolicyBanner.mdx";
-
-
\ No newline at end of file
diff --git a/docs/base-account/quickstart/quick-demo.mdx b/docs/base-account/quickstart/quick-demo.mdx
deleted file mode 100644
index 6142b16fb..000000000
--- a/docs/base-account/quickstart/quick-demo.mdx
+++ /dev/null
@@ -1,125 +0,0 @@
----
-title: "Quick demo"
----
-
-This option uses the OnchainKit [Wallet component](/onchainkit/wallet/wallet). It is perfect for quickly experiencing Base Account functionality with minimal setup.
-
-
-**What is OnchainKit?**
-
-[OnchainKit](https://www.base.org/builders/onchainkit)
-is a suite of components allowing you to build
-your onchain app fast and quick. It is a
-very popular solution to build on Base. You can learn more about it [here](/onchainkit/getting-started).
-
-
-
-## Step 1: Clone the Template Repository
-
-Let's start by cloning the [repository](https://github.com/coinbase/onchain-app-template). Open a terminal and execute the following:
-
-```bash
-git clone https://github.com/coinbase/onchain-app-template
-cd onchain-app-template
-```
-
-## Step 2: Set Up Environment Variables
-
-Before running your project, you need to set up your environment variables. Follow these steps:
-
-### Get a ReOwn Project ID
-
-1. Sign up to [ReOwn Cloud](https://www.reown.com/cloud)
-2. Click on Create, and go through the steps (screenshot below):
- - Name your app (eg. OnchainKit-Template)
- - Select a product - Choose WalletKit
- - Select a platform - Choose JavaScript
-
-
-
-
-
- Screenshot of the steps to get your ReOwn Project ID
-
-
-### Get a Coinbase Developer Platform (CDP) Client API Key
-
-1. Sign up to [Coinbase Developer Platform (CDP)](https://portal.cdp.coinbase.com/)
-2. Create a new project and copy the [Client API Key](https://portal.cdp.coinbase.com/projects/api-keys/client-key) (screenshot below)
-
-
-
-### Create your .env file
-
-Now that you have obtained your ReOwn Project ID and CDP API Key, it is time to create the .env file:
-
-1. Copy `.env.local.default` from to the repository root and name it `.env`
-2. Leave `NEXT_PUBLIC_GOOGLE_ANALYTICS_ID` and `NEXT_PUBLIC_ENVIRONMENT` as they are
-3. Replace `NEXT_PUBLIC_CDP_API_KEY` and `NEXT_PUBLIC_WC_PROJECT_ID` with their respective values obtained from the previous steps
-
-Congratulations! We are ready to run the application.
-
-## Step 3: Install Dependencies and Run
-
-Open your terminal, make sure you are at the directory `/onchain-app-template` and execute the following:
-
-```bash
-# Install packages
-bun i
-
-# Run Next app
-bun run dev
-```
-
-
-**Make sure Bun is installed**
-
-This example uses [Bun](https://bun.sh/) as a preferred package manager. If you run into an error because you don't have Bun installed you can run the following command to install it:
-
-```bash
-curl -fsSL https://bun.sh/install | bash
-```
-
-
-
-Your app will be available at http://localhost:3000
-
-## Step 4: Explore Base Account Features
-
-The template includes:
-
-- Pre-integrated Wallet component
-- OnchainKit components for blockchain interactions
-- Basic authentication flow
-- Transaction handling (NFT Mint)
-
-This setup is ideal for:
-
-- Beginners exploring onchain apps and Base Account functionality
-- Developers evaluating Base Account capabilities
-- Quick prototyping
-
-If you are looking for a more in depth understanding of Base Account, explore these next steps:
-
-- **[Authenticate Users](/smart-wallet/guides/siwe)**: Implement secure user authentication
-- **[Accept Payments](/smart-wallet/guides/profiles)**: Set up payment processing in your app
-- **[Improve UX](/smart-wallet/guides/batch-transactions)**: Enhance user experience with advanced features
-
-import PolicyBanner from "/snippets/PolicyBanner.mdx";
-
-
\ No newline at end of file
diff --git a/docs/base-account/quickstart/web-react.mdx b/docs/base-account/quickstart/web-react.mdx
new file mode 100644
index 000000000..a7f9384ef
--- /dev/null
+++ b/docs/base-account/quickstart/web-react.mdx
@@ -0,0 +1,213 @@
+---
+title: "Web (React)"
+description: "This quick-start shows the minimum code required to add Sign in with Base and Base Pay to any React app using the Base Account SDK."
+---
+
+import { GithubRepoCard } from "/snippets/GithubRepoCard.mdx"
+
+This quick-start shows the **minimum** code required to add Sign in with Base and Base Pay to any React app using the Base Account SDK.
+
+## 1. Create a new React app
+
+If you're starting fresh, create a new React app:
+
+
+```bash npm
+npx create-react-app base-account-quickstart
+cd base-account-quickstart
+```
+
+```bash yarn
+yarn create react-app base-account-quickstart
+cd base-account-quickstart
+```
+
+
+## 2. Install the SDK
+
+
+```bash npm
+npm install @base-org/account @base-org/account-ui
+```
+
+```bash pnpm
+pnpm add @base-org/account @base-org/account-ui
+```
+
+```bash yarn
+yarn add @base-org/account @base-org/account-ui
+```
+
+```bash bun
+bun add @base-org/account @base-org/account-ui
+```
+
+
+
+**Got a peer dependency error?**
+
+Use `--legacy-peer-deps` flag if you get a peer dependency error.
+
+
+## 3. Replace src/App.js with this component
+
+```jsx title="src/App.js" lineNumbers
+import React, { useState } from 'react';
+import { createBaseAccountSDK, pay, getPaymentStatus } from '@base-org/account';
+import { SignInWithBaseButton, BasePayButton } from '@base-org/account-ui/react';
+
+function App() {
+ const [isSignedIn, setIsSignedIn] = useState(false);
+ const [paymentStatus, setPaymentStatus] = useState('');
+ const [paymentId, setPaymentId] = useState('');
+ const [theme, setTheme] = useState('light');
+
+ // Initialize SDK
+ const sdk = createBaseAccountSDK(
+ {
+ appName: 'Base Account Quick-start',
+ appLogo: 'https://base.org/logo.png',
+ }
+ );
+
+ // Optional sign-in step – not required for `pay()`, but useful to get the user address
+ const handleSignIn = async () => {
+ try {
+ await sdk.getProvider().request({ method: 'wallet_connect' });
+ setIsSignedIn(true);
+ } catch (error) {
+ console.error('Sign in failed:', error);
+ }
+ };
+
+ // One-tap USDC payment using the pay() function
+ const handlePayment = async () => {
+ try {
+ const { id } = await pay({
+ amount: '0.01', // USD – SDK quotes equivalent USDC
+ to: '0xRecipientAddress', // Replace with your recipient address
+ testnet: true // set to false or omit for Mainnet
+ });
+
+ setPaymentId(id);
+ setPaymentStatus('Payment initiated! Click "Check Status" to see the result.');
+ } catch (error) {
+ console.error('Payment failed:', error);
+ setPaymentStatus('Payment failed');
+ }
+ };
+
+ // Check payment status using stored payment ID
+ const handleCheckStatus = async () => {
+ if (!paymentId) {
+ setPaymentStatus('No payment ID found. Please make a payment first.');
+ return;
+ }
+
+ try {
+ const { status } = await getPaymentStatus({ id: paymentId });
+ setPaymentStatus(`Payment status: ${status}`);
+ } catch (error) {
+ console.error('Status check failed:', error);
+ setPaymentStatus('Status check failed');
+ }
+ };
+
+ const toggleTheme = () => {
+ setTheme(theme === 'light' ? 'dark' : 'light');
+ };
+
+ const dark = theme === 'dark';
+ const styles = {
+ container: { minHeight: '100vh', backgroundColor: dark ? '#111' : '#fff', color: dark ? '#fff' : '#000', display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', padding: '20px' },
+ card: { backgroundColor: dark ? '#222' : '#f9f9f9', borderRadius: '12px', padding: '30px', maxWidth: '400px', textAlign: 'center' },
+ title: { fontSize: '24px', fontWeight: 'bold', marginBottom: '10px', color: dark ? '#fff' : '#00f' },
+ subtitle: { fontSize: '16px', color: dark ? '#aaa' : '#666', marginBottom: '30px' },
+ themeToggle: { position: 'absolute', top: '20px', right: '20px', background: 'none', border: 'none', cursor: 'pointer', fontSize: '18px' },
+ buttonGroup: { display: 'flex', flexDirection: 'column', gap: '16px', alignItems: 'center' },
+ status: { marginTop: '20px', padding: '12px', backgroundColor: dark ? '#333' : '#f0f0f0', borderRadius: '8px', fontSize: '14px' },
+ signInStatus: { marginTop: '8px', fontSize: '14px', color: dark ? '#0f0' : '#060' }
+ };
+
+ return (
+
+
+
+
+
Base Account
+
Experience seamless crypto payments
+
+
+
+
+ {isSignedIn && (
+
+ ✅ Connected to Base Account
+
+ )}
+
+
+
+ {paymentId && (
+
+ )}
+
+
+ {paymentStatus && (
+
+ {paymentStatus}
+
+ )}
+
+
+ );
+}
+
+export default App;
+```
+
+
+**Note:**
+
+Make sure to replace `0xRecipientAddress` with your recipient address.
+
+
+## 4. Start your app
+
+```bash
+npm start
+```
+
+Open http://localhost:3000, click **Sign in with Base** (optional) and then **Pay**, approve the transaction, and you've sent 5 USDC on Base Sepolia—done! 🎉
+
+**Note:** If you have an existing React app, just install the SDK (`npm install @base-org/account`) and add the component above to your project.
+
+## Next steps
+
+* **[Request profile data](/base-account/guides/accept-payments#collect-user-information-optional)** – ask the user for email, shipping address, etc. during `pay()`
+* **[Sub Accounts guide](/base-account/improve-ux/sub-accounts)** – embed gas-less child wallets inside your app
+* **[Mobile quick-start](/base-account/quickstart/mobile-integration)** – the same flow for React Native
\ No newline at end of file
diff --git a/docs/base-account/quickstart/web.mdx b/docs/base-account/quickstart/web.mdx
index cdd672be6..445e4a911 100644
--- a/docs/base-account/quickstart/web.mdx
+++ b/docs/base-account/quickstart/web.mdx
@@ -1,10 +1,23 @@
---
title: "Web (HTML + JS)"
+description: "Integrate Sign in with Base and Base Pay using nothing but HTML and JavaScript."
---
This quick-start shows the **minimum** code required to add Sign in with Base and Base Pay to any web page using nothing but the Base Account SDK. No frameworks, no additional libraries.
-## 1. Install the SDK
+## 1. Install the SDK (Optional)
+
+
+**Interactive Playground:** Want to test the SDK functions before integrating? Try our [Base Pay SDK Playground](https://base.github.io/account-sdk/pay-playground) to experiment with `pay()` and `getPaymentStatus()` functions.
+
+
+You can use the Base Account SDK in two ways:
+
+### Option A: CDN (No installation required)
+Just include the script tag in your HTML - no build tools needed!
+
+### Option B: NPM Package
+If you prefer to install locally:
```bash npm
@@ -24,12 +37,16 @@ bun add @base-org/account
```
-If you prefer a CDN import, you can skip the install step and replace the `import` line below with:
-
+Then use ES modules:
```html
-
+
```
+This guide uses the CDN approach for simplicity.
+
## 2. Copy-paste this HTML file
```html title="index.html" lineNumbers
@@ -40,36 +57,96 @@ If you prefer a CDN import, you can skip the install step and replace the `impor
Base Account Quick-start
-
+