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 ( -
- Connected: {address} - -
- ) - } - - return ( -
- {connectors.map((connector) => ( - - ))} -
- ) -} -``` - -### Send one or multiple transactions - -Use Wagmi's contract hooks with Base Account for seamless smart contract interaction: - -```tsx -// components/ContractInteraction.tsx -import { useState } from 'react' -import { useReadContract, useWriteContract, useWaitForTransactionReceipt } from 'wagmi' -import { parseUnits } from 'viem' - -// ERC-20 Token ABI (simplified) -const tokenAbi = [ - { - name: 'balanceOf', - type: 'function', - stateMutability: 'view', - inputs: [{ name: 'account', type: 'address' }], - outputs: [{ name: '', type: 'uint256' }], - }, - { - name: 'transfer', - type: 'function', - stateMutability: 'nonpayable', - inputs: [ - { name: 'to', type: 'address' }, - { name: 'amount', type: 'uint256' }, - ], - outputs: [{ name: '', type: 'bool' }], - }, -] as const - -const USDC_ADDRESS = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913' // USDC on Base - -export function ContractInteraction() { - const [recipient, setRecipient] = useState('') - const [amount, setAmount] = useState('') - - // Read contract data - const { data: balance, refetch: refetchBalance } = useReadContract({ - address: USDC_ADDRESS, - abi: tokenAbi, - functionName: 'balanceOf', - args: ['0x...'], // User's address - }) - - // Write to contract - const { - data: hash, - writeContract, - isPending: isWriting - } = useWriteContract() - - const { - isLoading: isConfirming, - isSuccess: isConfirmed - } = useWaitForTransactionReceipt({ hash }) - - const handleTransfer = () => { - if (!recipient || !amount) return - - writeContract({ - address: USDC_ADDRESS, - abi: tokenAbi, - functionName: 'transfer', - args: [recipient as `0x${string}`, parseUnits(amount, 6)], // USDC has 6 decimals - }) - } - - return ( -
-
-

USDC Balance

-

{balance ? (Number(balance) / 1000000).toFixed(2) : '0'} USDC

- -
- -
- - setRecipient(e.target.value)} - className="w-full p-2 border rounded" - placeholder="0x..." - /> -
- -
- - setAmount(e.target.value)} - className="w-full p-2 border rounded" - placeholder="10.50" - step="0.01" - /> -
- - - - {hash && ( -
-
Transaction: {hash}
- {isConfirmed && ( -
Transfer completed!
- )} -
- )} -
- ) -} -``` -## Advanced Usage - -For additional features, you can always access the Base Account provider directly, like so: - -```tsx -import { useConnector } from 'wagmi' -import { parseEther, encodeFunctionData } from 'viem' - -export function BatchTransactions() { - const { address } = useAccount() - const connector = useConnector() - const [isLoading, setIsLoading] = useState(false) - - const sendBatchTransactions = async () => { - if (!connector || !address) return - - setIsLoading(true) - try { - // Create multiple calls - const calls = [ - { - to: '0x...' as `0x${string}`, - value: parseEther('0.001'), - data: '0x' as `0x${string}` - }, - { - to: '0x...' as `0x${string}`, - value: parseEther('0.002'), - data: '0x' as `0x${string}` - } - ] - - // Send batch transaction using connector - const result = await connector.provider?.request({ - method: 'wallet_sendCalls', - params: [{ - version: '1.0', - chainId: `0x${Number(8453).toString(16)}`, // Base mainnet - from: address, - calls: calls - }] - }) - - console.log('Batch transaction result:', result) - } catch (error) { - console.error('Batch transaction failed:', error) - } finally { - setIsLoading(false) - } - } -``` - -Once you have access to the Base Account provider, -you can use it to call specific RPC methods as shown in the [reference](/base-account/reference/core/getProvider). - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/base-account/framework-integrations/privy/setup.mdx b/docs/base-account/framework-integrations/privy/setup.mdx new file mode 100644 index 000000000..a1eb8c128 --- /dev/null +++ b/docs/base-account/framework-integrations/privy/setup.mdx @@ -0,0 +1,338 @@ +--- +title: "Setup" +description: "Configure Privy with Base Account for your React application" +--- + +Learn how to set up Privy with Base Account to enable seamless user authentication and wallet management. + +## Overview + +[Privy](https://www.privy.io/) provides user authentication and wallet management solutions for web3 applications. By integrating Privy with Base Account, you can offer users a smooth onboarding experience with embedded wallets and Base Account functionality. + +### What you'll achieve + +By the end of this guide, you will: + +- Set up Privy with Base Account support +- Configure embedded wallets for your users +- Enable Base Account as a wallet option + +## Installation + +After [creating a new Next.js project](https://nextjs.org/docs/app/getting-started/installation), install the required dependencies: + + +```bash npm +npm install @privy-io/react-auth @privy-io/wagmi-connector wagmi viem @base-org/account +``` + +```bash pnpm +pnpm add @privy-io/react-auth @privy-io/wagmi-connector wagmi viem @base-org/account +``` + +```bash yarn +yarn add @privy-io/react-auth @privy-io/wagmi-connector wagmi viem @base-org/account +``` + +```bash bun +bun add @privy-io/react-auth @privy-io/wagmi-connector wagmi viem @base-org/account +``` + + +## Configuration + +### 1. Set up Environment Variables + +Create a `.env.local` file in your project root: + +```bash +NEXT_PUBLIC_PRIVY_APP_ID=your_privy_app_id +``` + +Get your Privy App ID from the [Privy Dashboard](https://dashboard.privy.io/). + +### 2. Configure Privy Provider + +Create your Privy configuration with Base Account support: + +```tsx +// app/layout.tsx or pages/_app.tsx +'use client' + +import { PrivyProvider } from '@privy-io/react-auth' +import { WagmiProvider } from '@privy-io/wagmi-connector' +import { base } from 'viem/chains' +import { http } from 'viem' +import { baseAccount } from 'wagmi/connectors' + +const privyConfig = { + embeddedWallets: { + createOnLogin: 'users-without-wallets' as const, + requireUserPasswordOnCreate: false, + }, + loginMethods: ['email', 'wallet', 'google'], + appearance: { + theme: 'light' as const, + accentColor: '#0052FF', // Base blue + logo: 'https://your-logo-url.com/logo.png', + walletList: [ + 'base_account', + 'coinbase_wallet', // It is recommended to support coinbase_wallet for any users still using the legacy EOA + ], + }, + supportedChains: [base], +} + +const wagmiConfig = { + chains: [base], + transports: { + [base.id]: http(), + }, + connectors: [ + baseAccount({ + appName: 'Your App Name', + }) + ], +} + +export default function App({ children }: { children: React.ReactNode }) { + return ( + + + {children} + + + ) +} +``` + +To have Sign in with Base as a top level sign in option in the login modal, modify your `privyConfig` to include `base_account` in the `loginMethodsAndOrder` field. + +```tsx +const privyConfig = { + embeddedWallets: { + createOnLogin: 'users-without-wallets' as const, + requireUserPasswordOnCreate: false, + }, + loginMethodsAndOrder: { + primary: ['base_account','email', 'wallet'], + }, + appearance: { + theme: 'light' as const, + accentColor: '#0052FF' as const, // Base blue + walletList: ['base_account', 'coinbase_wallet'], + }, + supportedChains: [base], +}; +``` + +### 3. Create Authentication Hook + +Create a custom hook to manage authentication state: + +```tsx +// hooks/useAuth.ts +import { usePrivy } from '@privy-io/react-auth' +import { useWallets } from '@privy-io/react-auth' + +export function useAuth() { + const { + ready, + authenticated, + user, + login, + logout, + linkWallet, + unlinkWallet, + } = usePrivy() + + const { wallets } = useWallets() + + const embeddedWallet = wallets.find( + (wallet) => wallet.walletClientType === 'privy' + ) + + const baseAccountWallet = wallets.find( + (wallet) => wallet.walletClientType === 'base_account' + ) + + return { + ready, + authenticated, + user, + login, + logout, + linkWallet, + unlinkWallet, + embeddedWallet, + baseAccountWallet, + wallets, + } +} +``` + +### 4. Create Login Component + +Create a component to handle user authentication: + +```tsx +// components/AuthButton.tsx +import { useAuth } from '../hooks/useAuth' + +export function AuthButton() { + const { ready, authenticated, login, logout, user } = useAuth() + + if (!ready) { + return ( + + ) + } + + if (authenticated) { + return ( +
+ Welcome, {user?.email?.address || 'User'}! + +
+ ) + } + + return ( + + ) +} +``` + +### 5. Wallet Connection Component + +Create a component to manage wallet connections: + +```tsx +// components/WalletManager.tsx +import { useAuth } from '../hooks/useAuth' + +export function WalletManager() { + const { + authenticated, + embeddedWallet, + baseAccountWallet, + linkWallet, + wallets + } = useAuth() + + if (!authenticated) { + return

Please log in to manage wallets

+ } + + return ( +
+

Your Wallets

+ + {embeddedWallet && ( +
+

Embedded Wallet

+

+ Address: {embeddedWallet.address} +

+
+ )} + + {baseAccountWallet && ( +
+

Base Account

+

+ Address: {baseAccountWallet.address} +

+
+ )} + + {!baseAccountWallet && ( + + )} + +
+ Total wallets: {wallets.length} +
+
+ ) +} +``` + +## 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 ( +
+

Base Sub Account Demo

+ + {!subAccount ? ( +
+

Set up a Sub Account for gasless interactions

+ +
+ ) : ( +
+
+

Sub Account Ready

+

Address: {subAccount.address}

+
+ +
+ + setMessage(e.target.value)} + className="w-full p-2 border rounded" + placeholder="Enter message to sign" + /> + +
+ + {signature && ( +
+

Signature:

+

{signature}

+
+ )} +
+ )} +
+ ); +} +``` + +## 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 ( +
+

Sub Account Management

+ + {!subAccount ? ( + + ) : ( +
+

+ Sub Account created: {subAccount} +

+
+ )} +
+ ) +} +``` + +## Listing Sub Accounts + +Retrieve existing Sub Accounts for the connected wallet: + +```tsx +import { useAccount, useConnector } from 'wagmi' +import { useState, useEffect } from 'react' + +export function SubAccountsList() { + const { address, isConnected } = useAccount() + const connector = useConnector() + const [subAccounts, setSubAccounts] = useState([]) + const [isLoading, setIsLoading] = useState(false) + + const fetchSubAccounts = async () => { + if (!connector || !isConnected) return + + setIsLoading(true) + try { + const provider = connector.provider + + const result = await provider?.request({ + method: 'wallet_getSubAccounts', + params: [{ + version: '1.0', + chainId: `0x${Number(8453).toString(16)}`, + from: address, + }] + }) + + if (result?.subAccounts) { + setSubAccounts(result.subAccounts) + } + } catch (error) { + console.error('Failed to fetch Sub Accounts:', error) + } finally { + setIsLoading(false) + } + } + + useEffect(() => { + if (isConnected) { + fetchSubAccounts() + } + }, [isConnected, address]) + + return ( +
+
+

Your Sub Accounts

+ +
+ + {subAccounts.length === 0 ? ( +

No Sub Accounts found

+ ) : ( +
+ {subAccounts.map((subAccount, index) => ( +
+
+ {subAccount} + Sub Account #{index + 1} +
+
+ ))} +
+ )} +
+ ) +} +``` + +## Using Sub Accounts for Transactions + +Once you have Sub Accounts, you can use them to perform transactions: + +```tsx +import { useAccount, useConnector, useWriteContract } from 'wagmi' +import { useState } from 'react' + +export function SubAccountTransactions() { + const { address } = useAccount() + const connector = useConnector() + const [selectedSubAccount, setSelectedSubAccount] = useState('') + const [isLoading, setIsLoading] = useState(false) + + const sendTransactionFromSubAccount = async () => { + if (!connector || !selectedSubAccount) return + + setIsLoading(true) + try { + const provider = connector.provider + + // Send transaction using Sub Account + const result = await provider?.request({ + method: 'wallet_sendCalls', + params: [{ + version: '1.0', + chainId: `0x${Number(8453).toString(16)}`, + from: selectedSubAccount, // Use Sub Account as sender + calls: [{ + to: '0x...' as `0x${string}`, + value: '0x0', + data: '0x' as `0x${string}` + }] + }] + }) + + console.log('Transaction sent from Sub Account:', result) + } catch (error) { + console.error('Sub Account transaction failed:', error) + } finally { + setIsLoading(false) + } + } + + return ( +
+

Sub Account Transactions

+ +
+ + setSelectedSubAccount(e.target.value)} + className="w-full p-2 border rounded" + placeholder="0x... (Sub Account address)" + /> +
+ + +
+ ) +} +``` + +## Complete Example + +Here's a complete component that combines all Sub Account functionality: + +```tsx +import { useAccount, useConnector } from 'wagmi' +import { useState, useEffect } from 'react' + +export function SubAccountManager() { + const { address, isConnected } = useAccount() + const connector = useConnector() + const [subAccounts, setSubAccounts] = useState([]) + const [isCreating, setIsCreating] = useState(false) + const [isLoading, setIsLoading] = useState(false) + + const fetchSubAccounts = async () => { + if (!connector || !isConnected) return + + setIsLoading(true) + try { + const provider = connector.provider + const result = await provider?.request({ + method: 'wallet_getSubAccounts', + params: [{ + version: '1.0', + chainId: `0x${Number(8453).toString(16)}`, + from: address, + }] + }) + + if (result?.subAccounts) { + setSubAccounts(result.subAccounts) + } + } catch (error) { + console.error('Failed to fetch Sub Accounts:', error) + } finally { + setIsLoading(false) + } + } + + const createSubAccount = async () => { + if (!connector || !isConnected) return + + setIsCreating(true) + try { + const provider = connector.provider + const result = await provider?.request({ + method: 'wallet_addSubAccount', + params: [{ + version: '1.0', + chainId: `0x${Number(8453).toString(16)}`, + from: address, + }] + }) + + if (result?.subAccount) { + // Refresh the list + await fetchSubAccounts() + } + } catch (error) { + console.error('Failed to create Sub Account:', error) + } finally { + setIsCreating(false) + } + } + + useEffect(() => { + if (isConnected) { + fetchSubAccounts() + } + }, [isConnected]) + + if (!isConnected) { + return

Please connect your wallet first

+ } + + return ( +
+
+

Sub Account Manager

+ +
+ +
+
+

Your Sub Accounts ({subAccounts.length})

+ +
+ + {subAccounts.length === 0 ? ( +

No Sub Accounts found. Create one to get started!

+ ) : ( +
+ {subAccounts.map((subAccount, index) => ( +
+
+
+

{subAccount}

+

Sub Account #{index + 1}

+
+
+
+ ))} +
+ )} +
+
+ ) +} +``` + +## 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:
- Pay Popup + Pay Popup
### 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. + +
+ Pay with Coinbase on Base Account +
--- ## 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: Sub Account Creation Flow +### 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. 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. 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. 3. Check out the Paymaster product where the Base Mainnet Paymaster is enabled by default. Set and change your gas policy at any time.
  4. 4. Complete [this form](https://docs.google.com/forms/d/1yPnBFW0bVUNLUN_w3ctCqYM9sjdIQO3Typ53KXlsS5g/viewform?edit_requested=true)
  5. Credits will land within 1 week of completion
| -| 2 | $10k |
  • - Support Base Account
  • - Onboard to Coinbase Paymaster
|
  1. 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. 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. 3. Check out the Paymaster product where the Base Mainnet Paymaster is enabled by default. Set and change your gas policy at any time.
  4. 4. Complete [this form](https://docs.google.com/forms/d/1yPnBFW0bVUNLUN_w3ctCqYM9sjdIQO3Typ53KXlsS5g/viewform?edit_requested=true)
  5. Credits will land within 1 week of completion
| -| Bonus | $1k |
  • - Release demo
| 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. 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. 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. 3. Check out the Paymaster product where the Base Mainnet Paymaster is enabled by default. Set and change your gas policy at any time.
  4. 4. Complete [this form](https://docs.google.com/forms/d/1yPnBFW0bVUNLUN_w3ctCqYM9sjdIQO3Typ53KXlsS5g/viewform?edit_requested=true)
  5. Credits will land within 1 week of completion
| | Bonus | $1k |
  • - Release demo
| 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 - -
- - ReOwn Project ID - - 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) - -
- - Coinbase Developer Platform Client API Key - - Coinbase Developer Platform Client API Key -
- -### 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 - +

Base Account Demo

+ - - - - - + + +
+ + + + + + + ``` ## 3. Serve the file @@ -82,10 +159,17 @@ npx serve . python -m http.server ``` -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! 🎉 +Open http://localhost:3000, click **Sign in with Base** (optional) and then **Pay with Base**, approve the transaction, and you've sent 5 USDC on Base Sepolia—done! 🎉 + ## 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 +* **[Add Sign In With Base Button](/base-account/reference/ui-elements/sign-in-with-base-button)** – implement full SIWE authentication with backend verification +* **[Add Base Pay Button](/base-account/reference/ui-elements/base-pay-button)** – collect user information during payment flow + + +**Please Follow the Brand Guidelines** + +If you intend on using the `SignInWithBaseButton` or `BasePayButton`, 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/reference/base-pay/getPaymentStatus.mdx b/docs/base-account/reference/base-pay/getPaymentStatus.mdx index 286f40d91..8ada35024 100644 --- a/docs/base-account/reference/base-pay/getPaymentStatus.mdx +++ b/docs/base-account/reference/base-pay/getPaymentStatus.mdx @@ -1,140 +1,149 @@ --- title: "getPaymentStatus" +description: "Check the status of a payment transaction" --- -## Overview +Defined in the [Base Account SDK](https://github.com/base/account-sdk) + The `getPaymentStatus` function allows you to check the status of a payment transaction after it has been submitted. Use this to track whether a payment has been completed, is still pending, or has failed. -## Function Signature - -```typescript -/** - * Checks the status of a payment - * @throws {Error} If the transaction ID is invalid - */ -declare function getPaymentStatus(options: GetPaymentStatusOptions): Promise; -``` +**Try it out:** Test the `getPaymentStatus` function interactively in our [Base Pay SDK Playground](https://base.github.io/account-sdk/pay-playground). + ## Parameters -### GetPaymentStatusOptions + +Transaction hash from the pay result that you want to check the status of. -```typescript -interface GetPaymentStatusOptions { - /** Transaction hash from pay result */ - id: string; - /** Must match testnet setting used in pay */ - testnet?: boolean; -} -``` +**Pattern:** `^0x[0-9a-fA-F]{64}$` + + + +Must match the testnet setting used in the original pay call. Default: false + ## Returns -### PaymentStatus - -```typescript -interface PaymentStatus { - /** Current status of the payment */ - status: "completed" | "pending" | "failed" | "not_found"; - /** Original transaction hash */ - id: string; - /** Human-readable status message */ - message: string; - /** Sender address (present for pending, completed, and failed) */ - sender?: string; - /** Amount that was sent (present for completed transactions) */ - amount?: string; - /** Address that received the payment (present for completed transactions) */ - recipient?: string; - /** Error details (present for failed status) */ - error?: string; -} -``` + +Payment status information including current state and details. + + + +Current status of the payment. + +**Possible values:** +- `"completed"`: Payment successfully processed and confirmed +- `"pending"`: Payment still being processed by the network +- `"failed"`: Payment failed to process (funds not transferred) +- `"not_found"`: Transaction ID not found or invalid + + + +Original transaction hash that was queried. + + + +Human-readable status message explaining the current state. + + + +Sender address (present for pending, completed, and failed statuses). + + + +Amount that was sent (present for completed transactions). + + + +Address that received the payment (present for completed transactions). + -## Status Types + +Error details (present for failed status). + + + -- **`completed`**: Payment has been successfully processed and confirmed on the blockchain -- **`pending`**: Payment is still being processed by the network -- **`failed`**: Payment failed to process (funds are not transferred) -- **`not_found`**: Transaction ID not found or invalid -## Usage Example + +```typescript Basic Status Check +import { getPaymentStatus } from '@base-org/account'; -```typescript +const status = await getPaymentStatus({ + id: "0xabcd1234...", + testnet: false +}); + +console.log("Payment status:", status.status); +``` + +```typescript Complete Payment Flow import { pay, getPaymentStatus } from '@base-org/account'; -async function sendAndTrackPayment() { - try { - // First, send a payment - const payResult = await pay({ - amount: "10.50", - to: "0x1234567890123456789012345678901234567890", - testnet: false - }); - - if (payResult.success) { - console.log("Payment submitted:", payResult.id); - - // Check payment status - const status = await getPaymentStatus({ - id: payResult.id, - testnet: false - }); - - console.log("Payment status:", status.status); - console.log("Status message:", status.message); - - if (status.status === "completed") { - console.log("Payment completed successfully!"); - } else if (status.status === "pending") { - console.log("Payment is still processing..."); - } else if (status.status === "failed") { - console.log("Payment failed:", status.message); - } - } - } catch (error) { - console.error("Error:", error); - } +try { + // Send payment + const payment = await pay({ + amount: "10.50", + to: "0x1234567890123456789012345678901234567890" + }); +} catch (error) { + console.error(`Payment failed: ${error.message}`); +} + +try { + // Check status + const status = await getPaymentStatus({ + id: payment.id, + testnet: false + }); + + console.log("Status:", status.status); + catch (error) { + console.error(`Get status Failed: ${error.message}`); +} +``` + + + +```typescript Completed Payment +{ + status: "completed", + id: "0xabcd1234...", + message: "Payment completed successfully", + sender: "0x742d35Cc4Bf53E0e6C42E5d9F0A8D2F6D8A8B7C9", + amount: "10.50", + recipient: "0x1234567890123456789012345678901234567890" } ``` -## Polling for Status Updates +```typescript Pending Payment +{ + status: "pending", + id: "0xabcd1234...", + message: "Payment is being processed", + sender: "0x742d35Cc4Bf53E0e6C42E5d9F0A8D2F6D8A8B7C9" +} +``` -For pending payments, you may want to poll the status periodically: +```typescript Failed Payment +{ + status: "failed", + id: "0xabcd1234...", + message: "Payment failed due to insufficient balance", + sender: "0x742d35Cc4Bf53E0e6C42E5d9F0A8D2F6D8A8B7C9", + error: "Insufficient balance" +} +``` -```typescript -async function pollPaymentStatus(transactionId: string, testnet: boolean = false) { - const maxAttempts = 30; - const pollInterval = 2000; // 2 seconds - - for (let attempt = 0; attempt < maxAttempts; attempt++) { - try { - const status = await getPaymentStatus({ - id: transactionId, - testnet - }); - - if (status.status === "completed") { - console.log("Payment completed!"); - return status; - } else if (status.status === "failed") { - console.log("Payment failed:", status.message); - return status; - } - - // Still pending, wait before next check - await new Promise(resolve => setTimeout(resolve, pollInterval)); - } catch (error) { - console.error("Error checking payment status:", error); - break; - } - } - - console.log("Payment status check timed out"); - return null; +```typescript Transaction Not Found +{ + status: "not_found", + id: "0xabcd1234...", + message: "Transaction not found" } ``` + ## Error Handling @@ -148,4 +157,4 @@ Always wrap calls to `getPaymentStatus` in a try-catch block to handle these err import PolicyBanner from "/snippets/PolicyBanner.mdx"; - \ No newline at end of file + diff --git a/docs/base-account/reference/base-pay/pay.mdx b/docs/base-account/reference/base-pay/pay.mdx index b8217a783..ca49004a8 100644 --- a/docs/base-account/reference/base-pay/pay.mdx +++ b/docs/base-account/reference/base-pay/pay.mdx @@ -1,119 +1,205 @@ --- title: "pay" +description: "Send USDC payments on the Base network" --- -## Overview +Defined in the [Base Account SDK](https://github.com/base/account-sdk) + The `pay` function is the core method of Base Pay that lets your users send USDC (digital dollars) on the Base network. No crypto knowledge required - we handle all the complexity. **No fees for merchants or users.** -## Function Signature - -```typescript -/** - * Sends USDC to an Ethereum address - * @throws {Error} If the input parameters are invalid - */ -declare function pay(options: PayOptions): Promise; -``` +**Try it out:** Test the `pay` function interactively in our [Base Pay SDK Playground](https://base.github.io/account-sdk/pay-playground). + ## Parameters -### PayOptions + +Amount of USDC to send (e.g., "10.50" or "0.01"). + -```typescript -interface PayOptions { - /** Amount of USDC to send (e.g., "10.50" or "0.01") */ - amount: string; - /** Ethereum address to send USDC to (must start with 0x) */ - to: string; - /** Set to true to use Base Sepolia testnet instead of mainnet */ - testnet?: boolean; - /** Optional payer information configuration for data callbacks */ - payerInfo?: PayerInfo; -} + +Ethereum address to send USDC to (must start with 0x). -interface PayerInfo { - /** Information requests from the payer */ - requests: InfoRequest[]; - /** Optional callback URL for server-side validation */ - callbackURL?: string; -} +**Pattern:** `^0x[0-9a-fA-F]{40}$` + -interface InfoRequest { - /** The type of information being requested */ - type: 'email' | 'physicalAddress' | 'phoneNumber' | 'name' | 'onchainAddress'; - /** Whether this information is optional (default: false) */ - optional?: boolean; -} -``` + +Set to true to use Base Sepolia testnet instead of mainnet. Default: false + + + +Optional payer information configuration for data callbacks. + + + +Array of information requests from the payer. + + + +The type of information being requested. + +**Possible values:** `'email' | 'physicalAddress' | 'phoneNumber' | 'name' | 'onchainAddress'` + + + +Whether this information is optional. Default: false + + + + + +Optional callback URL for server-side validation. + + + ## Returns -### PayResult + +Payment result on success. The function throws an error on failure. -```typescript -interface PaymentSuccess { - success: true; - /** Transaction hash - use this to check payment status */ - id: string; - /** Amount that was sent */ - amount: string; - /** Address that received the payment */ - to: string; - /** Optional responses from information requests */ - payerInfoResponses?: PayerInfoResponses; -} + + +Transaction hash - use this to check payment status. + -interface PaymentError { - success: false; - /** Error message explaining what went wrong */ - error: string; - /** Amount that was attempted */ - amount: string; - /** Address that would have received the payment */ - to: string; -} + +Amount that was sent. + -type PayResult = PaymentSuccess | PaymentError; -``` + +Address that received the payment. + -## Usage Example + +Optional responses from information requests. + + + -```typescript +## Errors + +The `pay` function throws an error when the payment fails. The error object contains a message explaining what went wrong. + + + +```typescript Basic Payment import { pay } from '@base-org/account'; -async function sendPayment() { - try { - const result = await pay({ - amount: "10.50", - to: "0x1234567890123456789012345678901234567890", - testnet: false // Use mainnet - }); - - if (result.success) { - console.log("Payment successful!"); - console.log("Transaction ID:", result.id); - console.log("Amount sent:", result.amount); - console.log("Sent to:", result.to); - } else { - console.error("Payment failed:", result.error); +try { + const payment = await pay({ + amount: "10.50", + to: "0x1234567890123456789012345678901234567890", + testnet: false + }); + console.log(`Payment sent! Transaction ID: ${payment.id}`); +} catch (error) { + console.error(`Payment failed: ${error.message}`); +} +``` + +```typescript Payment with Data Collection +try { + const payment = await pay({ + amount: "25.00", + to: "0x1234567890123456789012345678901234567890", + payerInfo: { + requests: [ + { type: 'email', optional: false }, + { type: 'phoneNumber', optional: true }, + { type: 'physicalAddress', optional: true } + ], + callbackURL: "https://your-api.com/validate" + } + }); + + console.log(`Payment sent! Transaction ID: ${payment.id}`); + + // Access collected user information + if (payment.payerInfoResponses) { + 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('Address:', address.address1); + console.log('City:', address.city); + console.log('State:', address.state); + console.log('Postal Code:', address.postalCode); + console.log('Recipient Name:', `${address.name.firstName} ${address.name.familyName}`); } - } catch (error) { - console.error("Payment error:", error); } +} catch (error) { + console.error(`Payment failed: ${error.message}`); +} +``` + + + +```typescript Basic Success Response +{ + id: "0xabcd1234...", + amount: "10.50", + to: "0x1234567890123456789012345678901234567890" } ``` -## Error Handling +```typescript Success Response with Data Collection +{ + id: "0xabcd1234...", + amount: "25.00", + to: "0x1234567890123456789012345678901234567890", + payerInfoResponses: { + email: "user@example.com", + phoneNumber: { + number: "+1234567890", + country: "US" + }, + physicalAddress: { + address1: "123 Main St", + city: "San Francisco", + state: "CA", + postalCode: "94105", + country: "US", + name: { + firstName: "John", + familyName: "Doe" + } + } + } +} +``` + +```typescript Error (thrown) +{ + "code": 4001, + "message": "Request rejected", + "stack": "Error: Request rejected\n at getEthProviderError..." +} +``` + -The `pay` function can throw errors for invalid parameters: +## Error Handling -- Invalid amount format -- Invalid Ethereum address format -- Network connection issues -- User rejection of the payment +The `pay` function throws errors instead of returning a result. Always wrap calls to `pay` in a try-catch block to handle errors gracefully: -Always wrap calls to `pay` in a try-catch block to handle these errors gracefully. +```typescript +try { + const payment = await pay({ + amount: "10.00", + to: "0xRecipient" + }); + // Payment succeeded, use payment.id for tracking + console.log(`Payment sent! Transaction ID: ${payment.id}`); +} catch (error) { + // Payment failed + console.error(`Payment failed: ${error.message}`); +} +``` import PolicyBanner from "/snippets/PolicyBanner.mdx"; diff --git a/docs/base-account/reference/core/capabilities/atomic.mdx b/docs/base-account/reference/core/capabilities/atomic.mdx new file mode 100644 index 000000000..0aa5b0990 --- /dev/null +++ b/docs/base-account/reference/core/capabilities/atomic.mdx @@ -0,0 +1,254 @@ +--- +title: "atomic" +description: "Ensures batched transactions are executed atomically and contiguously" +--- + +Defined in [EIP-5792](https://eips.ethereum.org/EIPS/eip-5792) + + +The atomic capability specifies how wallets execute batches of transactions, providing guarantees for atomic transaction execution. When supported, all transactions in a batch must succeed together or fail together. + + +## Parameters + + +The atomic capability status for the current chain and account. + +**Possible values:** +- `"supported"`: Wallet will execute all calls atomically and contiguously +- `"ready"`: Wallet can upgrade to atomic execution pending user approval +- `"unsupported"`: No atomicity or contiguity guarantees + + +## Returns + + +The atomic capability configuration for the specified chain. + + + +Indicates the level of atomic execution support available. + + + + +## Example Usage + + +```typescript Check Capability Support +const capabilities = await provider.request({ + method: 'wallet_getCapabilities', + params: ['0xd46e8dd67c5d32be8058bb8eb970870f07244567'] +}); + +console.log(capabilities["0x2105"].atomic); +``` + +```typescript Send Atomic Transactions +const result = await provider.request({ + method: "wallet_sendCalls", + params: [{ + version: "1.0", + chainId: "0x2105", + from: "0xd46e8dd67c5d32be8058bb8eb970870f07244567", + atomicRequired: true, + calls: [ + { + to: "0x1234567890123456789012345678901234567890", + value: "0x0", + data: "0xa9059cbb000000000000000000000000..." + } + ] + }] +}); +``` + + + +```json Capability Response (Supported) +{ + "0x2105": { + "atomic": { + "supported": "supported" + } + } +} +``` + +```json Capability Response (Ready for Upgrade) +{ + "0x2105": { + "atomic": { + "supported": "ready" + } + } +} +``` + +```json Capability Response (Unsupported) +{ + "0x2105": { + "atomic": { + "supported": "unsupported" + } + } +} +``` + + +## Error Handling + +| Code | Message | Description | +| ---- | ------- | ----------- | +| 4100 | Atomic execution not supported | Wallet does not support atomic transaction execution | +| 5700 | Atomic capability required | Transaction requires atomic execution but wallet doesn't support it | +| 5750 | Atomic upgrade rejected | User rejected the upgrade to atomic execution capability | + +## Use Cases + +### DeFi Operations + +Atomic execution is crucial for DeFi operations where multiple steps must complete together: + +```typescript +// Swap tokens atomically +const swapCalls = await provider.request({ + method: "wallet_sendCalls", + params: [{ + version: "1.0", + chainId: "0x2105", + from: userAddress, + atomicRequired: true, + calls: [ + // 1. Approve token spend + { + to: tokenAddress, + value: "0x0", + data: approveCallData + }, + // 2. Execute swap + { + to: swapContractAddress, + value: "0x0", + data: swapCallData + }, + // 3. Claim rewards (if applicable) + { + to: rewardsContractAddress, + value: "0x0", + data: claimCallData + } + ] + }] +}); +``` + +### NFT Minting with Payment + +```typescript +// Mint NFT and pay fees atomically +const mintCalls = await provider.request({ + method: "wallet_sendCalls", + params: [{ + version: "1.0", + chainId: "0x2105", + from: userAddress, + atomicRequired: true, + calls: [ + // 1. Pay minting fee + { + to: paymentAddress, + value: "0x16345785d8a0000", // 0.1 ETH + data: "0x" + }, + // 2. Mint NFT + { + to: nftContractAddress, + value: "0x0", + data: mintCallData + } + ] + }] +}); +``` + +## Error Handling + +Handle atomic capability errors appropriately: + +```typescript +async function executeAtomicTransaction(calls) { + try { + // Check atomic capability first + const capabilities = await provider.request({ + method: 'wallet_getCapabilities', + params: [userAddress] + }); + + const atomicCapability = capabilities["0x2105"]?.atomic; + + if (!atomicCapability || atomicCapability === "unsupported") { + throw new Error("Atomic execution not supported"); + } + + // Execute atomic transaction + const result = await provider.request({ + method: "wallet_sendCalls", + params: [{ + version: "1.0", + chainId: "0x2105", + from: userAddress, + atomicRequired: true, + calls + }] + }); + + return result; + + } catch (error) { + if (error.code === 4100) { + console.error("Atomic execution not supported by wallet"); + // Fallback to sequential execution + return executeSequentialTransaction(calls); + } else { + console.error("Atomic transaction failed:", error); + throw error; + } + } +} +``` + +## Relationship with EIP-7702 + +The atomic capability works with EIP-7702 to enable EOA (Externally Owned Accounts) to upgrade to smart accounts that support atomic transaction execution: + +```typescript +// Check if wallet can upgrade to atomic execution +const capabilities = await provider.request({ + method: 'wallet_getCapabilities', + params: [eoaAddress] +}); + +if (capabilities["0x2105"].atomic === "ready") { + console.log("Wallet can upgrade to support atomic execution with user approval"); +} +``` + +## Best Practices + +1. **Check Capabilities First**: Always verify atomic support before requiring it +2. **Provide Fallbacks**: Implement sequential execution as a fallback when atomic isn't available +3. **Use for Related Operations**: Only require atomicity for operations that must succeed together +4. **Clear Error Messages**: Provide helpful error messages when atomic execution fails + + +The atomic capability is chain-specific. Always check support for the specific chain you're targeting. + + + +Apps should first check wallet capabilities using `wallet_getCapabilities` before sending requests requiring atomic execution. + + +import PolicyBanner from "/snippets/PolicyBanner.mdx"; + + \ No newline at end of file diff --git a/docs/base-account/reference/core/capabilities/auxiliaryFunds.mdx b/docs/base-account/reference/core/capabilities/auxiliaryFunds.mdx new file mode 100644 index 000000000..874997f01 --- /dev/null +++ b/docs/base-account/reference/core/capabilities/auxiliaryFunds.mdx @@ -0,0 +1,391 @@ +--- +title: "auxiliaryFunds" +description: "Indicates wallet access to funds beyond on-chain balance verification" +--- + +Defined in [EIP-5792](https://eips.ethereum.org/EIPS/eip-5792) + + +The auxiliaryFunds capability allows wallets to indicate they have access to funds beyond what can be directly verified on-chain by the wallet's address. This enables more flexible transaction execution and improved user experiences. + + + +This capability is not yet finalized and may change in future iterations. + + +## Parameters + +This capability has no configuration parameters. It is either supported or not supported by the wallet. + +## Returns + + +The auxiliary funds capability configuration for the specified chain. + + + +Indicates whether the wallet has access to auxiliary funding sources beyond on-chain balance. + + + + +## Example Usage + + +```typescript Check Auxiliary Funds Support +const capabilities = await provider.request({ + method: 'wallet_getCapabilities', + params: [userAddress] +}); + +const auxiliaryFunds = capabilities["0x2105"]?.auxiliaryFunds; +``` + +```typescript Balance Check with Auxiliary Funds +if (auxiliaryFunds?.supported) { + // Don't block transactions based on visible balance alone + console.log("Wallet has access to auxiliary funds"); +} else { + // Check on-chain balance before allowing transactions + const balance = await provider.request({ + method: 'eth_getBalance', + params: [userAddress, 'latest'] + }); +} +``` + + + +```json Capability Response (Supported) +{ + "0x2105": { + "auxiliaryFunds": { + "supported": true + } + } +} +``` + +```json Capability Response (Unsupported) +{ + "0x2105": { + "auxiliaryFunds": { + "supported": false + } + } +} +``` + + +## Error Handling + +| Code | Message | Description | +| ---- | ------- | ----------- | +| 4100 | Auxiliary funds not supported | Wallet does not support auxiliary funding sources | +| 4200 | Auxiliary funds unavailable | Auxiliary funding sources are temporarily unavailable | +| 4300 | Insufficient auxiliary funds | Auxiliary funds exist but are insufficient for the transaction | + +## Wallet Implementation + +Wallets supporting auxiliary funds must include the capability in their response: + +```typescript +// Wallet response to wallet_getCapabilities +{ + "0x2105": { // Base mainnet + "auxiliaryFunds": { + "supported": true + } + } +} +``` + +## App Behavior + +Apps should modify their balance checking logic when auxiliary funds are supported: + +### Without Auxiliary Funds + +```typescript +async function checkCanExecuteTransaction(amount: bigint) { + const balance = await provider.request({ + method: 'eth_getBalance', + params: [userAddress, 'latest'] + }); + + if (BigInt(balance) < amount) { + throw new Error("Insufficient balance"); + } + + return true; +} +``` + +### With Auxiliary Funds Support + +```typescript +async function checkCanExecuteTransaction(amount: bigint) { + const capabilities = await provider.request({ + method: 'wallet_getCapabilities', + params: [userAddress] + }); + + if (capabilities["0x2105"]?.auxiliaryFunds?.supported) { + // Wallet may have auxiliary funds, allow transaction + console.log("Auxiliary funds available, proceeding with transaction"); + return true; + } + + // Check on-chain balance as fallback + const balance = await provider.request({ + method: 'eth_getBalance', + params: [userAddress, 'latest'] + }); + + if (BigInt(balance) < amount) { + throw new Error("Insufficient balance"); + } + + return true; +} +``` + +## Use Cases + +### DeFi Applications + +Enable DeFi operations even when wallet balance appears insufficient: + +```typescript +class DeFiManager { + async executeSwap(fromToken: string, toToken: string, amount: string) { + const capabilities = await provider.request({ + method: 'wallet_getCapabilities', + params: [userAddress] + }); + + const hasAuxiliaryFunds = capabilities["0x2105"]?.auxiliaryFunds?.supported; + + if (!hasAuxiliaryFunds) { + // Check token balance for non-auxiliary wallets + const tokenBalance = await this.getTokenBalance(fromToken, userAddress); + if (BigInt(tokenBalance) < BigInt(amount)) { + throw new Error("Insufficient token balance"); + } + } + + // Proceed with swap + return provider.request({ + method: 'wallet_sendCalls', + params: [{ + version: "1.0", + chainId: "0x2105", + from: userAddress, + calls: [{ + to: swapContractAddress, + value: "0x0", + data: this.encodeSwap(fromToken, toToken, amount) + }] + }] + }); + } + + private async getTokenBalance(token: string, account: string): Promise { + // Implementation to check ERC-20 token balance + return "0"; + } + + private encodeSwap(from: string, to: string, amount: string): string { + // Implementation to encode swap call data + return "0x"; + } +} +``` + +### E-commerce Applications + +Allow purchases without blocking on visible balance: + +```typescript +class PaymentProcessor { + async processPurchase(amount: bigint, currency: string) { + const capabilities = await provider.request({ + method: 'wallet_getCapabilities', + params: [userAddress] + }); + + if (capabilities["0x2105"]?.auxiliaryFunds?.supported) { + // Wallet may access funds through auxiliary sources + console.log("Processing payment with auxiliary funds support"); + + return this.executePurchase(amount, currency); + } else { + // Check sufficient balance for regular wallets + const balance = await this.getCurrencyBalance(currency, userAddress); + + if (balance < amount) { + throw new Error(`Insufficient ${currency} balance`); + } + + return this.executePurchase(amount, currency); + } + } + + private async executePurchase(amount: bigint, currency: string) { + return provider.request({ + method: 'wallet_sendCalls', + params: [{ + version: "1.0", + chainId: "0x2105", + from: userAddress, + calls: [{ + to: paymentContractAddress, + value: currency === "ETH" ? `0x${amount.toString(16)}` : "0x0", + data: currency === "ETH" ? "0x" : this.encodeTokenTransfer(currency, amount) + }] + }] + }); + } + + private async getCurrencyBalance(currency: string, account: string): Promise { + if (currency === "ETH") { + const balance = await provider.request({ + method: 'eth_getBalance', + params: [account, 'latest'] + }); + return BigInt(balance); + } else { + // Get ERC-20 token balance + const balance = await this.getTokenBalance(currency, account); + return BigInt(balance); + } + } + + private encodeTokenTransfer(token: string, amount: bigint): string { + // Implementation to encode token transfer + return "0x"; + } + + private async getTokenBalance(token: string, account: string): Promise { + // Implementation to check token balance + return "0"; + } +} +``` + +### Gaming Applications + +Enable in-game purchases without balance restrictions: + +```typescript +class GamePurchaseManager { + async buyGameItem(itemId: string, price: bigint) { + const capabilities = await provider.request({ + method: 'wallet_getCapabilities', + params: [userAddress] + }); + + // Don't check balance if auxiliary funds are supported + if (!capabilities["0x2105"]?.auxiliaryFunds?.supported) { + await this.validateBalance(price); + } + + return provider.request({ + method: 'wallet_sendCalls', + params: [{ + version: "1.0", + chainId: "0x2105", + from: userAddress, + calls: [{ + to: gameContractAddress, + value: "0x0", + data: this.encodePurchaseItem(itemId, price) + }] + }] + }); + } + + private async validateBalance(requiredAmount: bigint) { + const balance = await provider.request({ + method: 'eth_getBalance', + params: [userAddress, 'latest'] + }); + + if (BigInt(balance) < requiredAmount) { + throw new Error("Insufficient balance for purchase"); + } + } + + private encodePurchaseItem(itemId: string, price: bigint): string { + // Implementation to encode game item purchase + return "0x"; + } +} +``` + +## Error Handling + +Handle auxiliary funds-related scenarios: + +```typescript +async function executeTransactionWithAuxiliarySupport(calls: any[]) { + try { + const result = await provider.request({ + method: 'wallet_sendCalls', + params: [{ + version: "1.0", + chainId: "0x2105", + from: userAddress, + calls + }] + }); + + return result; + + } catch (error) { + if (error.message.includes("insufficient funds")) { + const capabilities = await provider.request({ + method: 'wallet_getCapabilities', + params: [userAddress] + }); + + if (capabilities["0x2105"]?.auxiliaryFunds?.supported) { + console.log("Transaction failed despite auxiliary funds support"); + // May indicate auxiliary funds are temporarily unavailable + throw new Error("Payment method temporarily unavailable"); + } else { + throw new Error("Insufficient balance"); + } + } + + throw error; + } +} +``` + +## Best Practices + +1. **Graceful Degradation**: Always provide fallback balance checking for non-auxiliary wallets +2. **Clear Communication**: Inform users when auxiliary funding is being used +3. **Error Handling**: Handle cases where auxiliary funds may be temporarily unavailable +4. **Security**: Don't assume auxiliary funds are always available + + +The auxiliary funds capability improves user experience by enabling transactions that might otherwise be blocked by insufficient visible balance. + + + +Apps should still implement proper error handling as auxiliary funds may not always be available or sufficient. + + +## Related Capabilities + +Auxiliary funds works well with other capabilities: + +- **[Paymaster Service](/base-account/reference/core/capabilities/paymasterService)**: For sponsored transactions +- **[Atomic](/base-account/reference/core/capabilities/atomic)**: For ensuring transaction success with auxiliary funds +- **[Flow Control](/base-account/reference/core/capabilities/flowControl)**: For handling auxiliary fund failures + +import PolicyBanner from "/snippets/PolicyBanner.mdx"; + + \ No newline at end of file diff --git a/docs/base-account/reference/base-pay/DatacallbackURL.mdx b/docs/base-account/reference/core/capabilities/datacallback.mdx similarity index 79% rename from docs/base-account/reference/base-pay/DatacallbackURL.mdx rename to docs/base-account/reference/core/capabilities/datacallback.mdx index f64b65485..5ba9d573e 100644 --- a/docs/base-account/reference/base-pay/DatacallbackURL.mdx +++ b/docs/base-account/reference/core/capabilities/datacallback.mdx @@ -1,5 +1,6 @@ --- -title: "DatacallbackURL" +title: "dataCallback" +description: "Base Account allows you to collect personal information like email addresses, physical addresses, phone numbers, and names during transactions." --- ## Overview @@ -16,7 +17,6 @@ type DataCallbackType = | 'phoneNumber' // Phone number with country code | 'physicalAddress' // Physical address for shipping | 'name' // User's full name - | 'onchainAddress' // On-chain wallet address // Full type definitions for requests and capability @@ -50,6 +50,10 @@ type DataCallbackCapability = { state: string; postalCode: string; country: string; + name: { + firstName: string; + familyName: string; + }; } ``` @@ -106,7 +110,6 @@ Your callback API will receive a POST request with the following structure: data: string; }[]; chainId: string; - version: string; capabilities: { dataCallback: { requestedInfo: { @@ -123,7 +126,7 @@ Your callback API will receive a POST request with the following structure: state: string; postalCode: string; countryCode: string; - name?: { + name: { firstName: string; familyName: string; }; @@ -146,22 +149,23 @@ Your callback API must respond with one of two formats: ### 1. Success Response -Return the calls the user will end up submitting. They can be the same calls or new ones, but they must be present. You can change all capabilities (e.g. switching Paymaster if calls happen on a different chain) except the data callback capability, which must remain present. +Return the calls the user will end up submitting wrapped in a `request` object. They can be the same calls or new ones, but they must be present. You can change all capabilities (e.g. switching Paymaster if calls happen on a different chain) except the data callback capability, which must remain present. ```typescript { - calls: { - to: string; - data: string; - }[]; - chainId: string; - version: string; - capabilities: { - dataCallback: { - // Original or updated dataCallback capability + request: { + calls: { + to: string; + data: string; + }[]; + chainId: string; + capabilities: { + dataCallback: { + // Original or updated dataCallback capability + }; + // Other capabilities can be changed as needed }; - // Other capabilities can be changed as needed - }; + } } ``` @@ -184,6 +188,10 @@ Return validation errors to prompt the user to correct their information: state?: string; postalCode?: string; countryCode?: string; + name?: { + firstName?: string; + familyName?: string; + }; }; name?: { firstName?: string; @@ -225,13 +233,20 @@ export async function POST(request: Request) { return Response.json({ errors }); } - // Success - return original calls - return Response.json({ - calls: requestData.calls, - chainId: requestData.chainId, - version: requestData.version, - capabilities: requestData.capabilities + // Success - return the request data wrapped in a request object + // The wallet expects the response to contain the original or modified calls + return Response.json({ + request: requestData }); + + // Alternative: Explicitly enumerate fields if needed + // return Response.json({ + // request: { + // calls: requestData.calls, + // chainId: requestData.chainId, + // capabilities: requestData.capabilities + // } + // }); } catch (error) { return Response.json({ @@ -252,7 +267,3 @@ export async function POST(request: Request) { 4. **Privacy**: Users always have full control over their data. They can choose to share or withhold any information, and they're clearly shown what data you're requesting. 5. **Validation**: Base Account performs basic validation before sending data to your callback URL. This includes checking that emails are valid and addresses are properly formatted. - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/base-account/reference/core/capabilities/flowControl.mdx b/docs/base-account/reference/core/capabilities/flowControl.mdx new file mode 100644 index 000000000..a41e6d049 --- /dev/null +++ b/docs/base-account/reference/core/capabilities/flowControl.mdx @@ -0,0 +1,294 @@ +--- +title: "flowControl" +description: "Control transaction batch behavior after failed or reverted calls" +--- + +Defined in [ERC-7867](https://github.com/ethereum/ERCs/pull/7867) (Proposed) + + +The flowControl capability allows dapps to specify how transaction batches should behave when individual calls fail or revert. This provides fine-grained control over transaction execution flow and enables more sophisticated error handling strategies. + + + +This capability is currently proposed in ERC-7867 and is not yet finalized. Implementation details may change as the specification develops. + + +## Parameters + + +Specifies the behavior when a transaction call fails or reverts. + +**Possible values:** +- `"continue"`: Continue executing remaining calls +- `"stop"`: Stop execution on failure +- `"retry"`: Attempt to retry the failed call + + + +Optional fallback transaction to execute if the primary call fails. + + + +The recipient address for the fallback call. + + + +The value to send with the fallback call (in wei, hex format). + + + +The call data for the fallback call (hex format). + + + + +## Returns + + +The flow control capability configuration for the specified chain. + + + +Indicates whether the wallet supports flow control functionality. + + + + +## Example Usage + + +```typescript Check Flow Control Support +const capabilities = await provider.request({ + method: 'wallet_getCapabilities', + params: [userAddress] +}); + +const flowControlCapability = capabilities["0x2105"]?.flowControl; +``` + +```typescript Proposed Usage (Subject to Change) +const result = await provider.request({ + method: 'wallet_sendCalls', + params: [{ + version: "1.0", + chainId: "0x2105", + from: userAddress, + calls: [ + { + to: "0x1234567890123456789012345678901234567890", + value: "0x0", + data: "0xa9059cbb000000000000000000000000...", + flowControl: { + onFailure: "continue", + fallbackCall: { + to: "0x...", + data: "0x..." + } + } + } + ] + }] +}); +``` + + + +```json Capability Response (Supported) +{ + "0x2105": { + "flowControl": { + "supported": true + } + } +} +``` + +```json Capability Response (Unsupported) +{ + "0x2105": { + "flowControl": { + "supported": false + } + } +} +``` + + +## Error Handling + +| Code | Message | Description | +| ---- | ------- | ----------- | +| 4100 | Flow control not supported | Wallet does not support flow control functionality | +| 5700 | Flow control capability required | Transaction requires flow control but wallet doesn't support it | +| 5800 | Invalid flow control parameters | The provided flow control configuration is invalid | + +## Potential Use Cases + +### E-commerce Transactions + +Handle scenarios where some purchases succeed while others fail: + +```typescript +// Example: Multi-item purchase with flow control +const purchaseResult = await provider.request({ + method: 'wallet_sendCalls', + params: [{ + version: "1.0", + chainId: "0x2105", + from: userAddress, + calls: [ + // Primary item purchase + { + to: marketplaceContract, + value: "0x0", + data: purchaseItem1CallData, + flowControl: { onFailure: "continue" } + }, + // Secondary item purchase + { + to: marketplaceContract, + value: "0x0", + data: purchaseItem2CallData, + flowControl: { onFailure: "continue" } + }, + // Payment processing (critical) + { + to: paymentContract, + value: "0x16345785d8a0000", + data: "0x", + flowControl: { onFailure: "stop" } + } + ] + }] +}); +``` + +### DeFi Operations with Fallbacks + +Implement sophisticated DeFi strategies with backup options: + +```typescript +// Example: Swap with fallback routing +const swapWithFallback = await provider.request({ + method: 'wallet_sendCalls', + params: [{ + version: "1.0", + chainId: "0x2105", + from: userAddress, + calls: [ + // Primary DEX swap + { + to: primaryDexAddress, + value: "0x0", + data: primarySwapCallData, + flowControl: { + onFailure: "fallback", + fallbackCall: { + to: secondaryDexAddress, + data: secondarySwapCallData + } + } + } + ] + }] +}); +``` + +### Batch Operations with Error Recovery + +Execute batch operations that can gracefully handle individual failures: + +```typescript +// Example: Bulk token approvals with recovery +const bulkApprovals = await provider.request({ + method: 'wallet_sendCalls', + params: [{ + version: "1.0", + chainId: "0x2105", + from: userAddress, + calls: tokenAddresses.map((token, index) => ({ + to: token, + value: "0x0", + data: approveCallData, + flowControl: { + onFailure: "continue", // Don't stop batch if one approval fails + retryCount: 2 // Retry failed approvals + } + })) + }] +}); +``` + +## Checking Capability Support + +Once implemented, check for flow control support: + +```typescript +async function checkFlowControlSupport() { + try { + const capabilities = await provider.request({ + method: 'wallet_getCapabilities', + params: [userAddress] + }); + + const flowControlCapability = capabilities["0x2105"]?.flowControl; + + if (flowControlCapability?.supported) { + console.log("Flow control capability supported"); + return true; + } else { + console.log("Flow control capability not supported"); + return false; + } + } catch (error) { + console.error("Error checking flow control capability:", error); + return false; + } +} +``` + +## Expected Benefits + +When implemented, flow control will provide: + +1. **Better User Experience**: Partial success instead of complete failure +2. **Flexible Error Handling**: Apps can define custom failure responses +3. **Reduced Gas Waste**: Avoid re-executing successful operations +4. **Complex Workflows**: Enable sophisticated multi-step processes + +## Development Status + +This capability is actively being developed: + +- **ERC-7867**: Formal proposal for flow control capability +- **Community Input**: Ongoing discussions about implementation details +- **Wallet Integration**: Pending finalization of specification + +## Preparing for Flow Control + +While waiting for implementation, developers can: + +1. **Design Flexible Architecture**: Build apps that can adapt to different execution models +2. **Implement Fallback Logic**: Create manual fallback strategies for critical operations +3. **Monitor Specification**: Track ERC-7867 progress for implementation updates +4. **Test Sequential Execution**: Use current capabilities to simulate flow control behavior + + +Stay updated on ERC-7867 development to implement flow control as soon as it's available in production wallets. + + + +The examples above are conceptual and may not reflect the final implementation. Always refer to the latest ERC-7867 specification for accurate details. + + +## Related Capabilities + +Flow control works alongside other capabilities: + +- **[Atomic](/base-account/reference/core/capabilities/atomic)**: For strict all-or-nothing execution +- **[Paymaster Service](/base-account/reference/core/capabilities/paymasterService)**: For sponsored transaction flows +- **[Auxiliary Funds](/base-account/reference/core/capabilities/auxiliaryFunds)**: For flexible funding sources + +import PolicyBanner from "/snippets/PolicyBanner.mdx"; + + \ No newline at end of file diff --git a/docs/base-account/reference/core/capabilities/overview.mdx b/docs/base-account/reference/core/capabilities/overview.mdx new file mode 100644 index 000000000..8388c89de --- /dev/null +++ b/docs/base-account/reference/core/capabilities/overview.mdx @@ -0,0 +1,212 @@ +--- +title: "Capabilities Overview" +description: "Understand how to use Base Account capabilities with wallet_connect and wallet_sendCalls" +--- + +Base Account supports various capabilities that extend functionality beyond standard wallet operations. Capabilities are feature flags that indicate what additional functionality a wallet supports for specific chains and accounts. + +## Core Concepts + +Capabilities are discovered using `wallet_getCapabilities` and utilized through `wallet_connect` and `wallet_sendCalls` methods. Each capability is chain-specific and may have different availability depending on the account type. + +### Discovery Pattern + +```typescript +// Check what capabilities are available +const capabilities = await provider.request({ + method: 'wallet_getCapabilities', + params: [userAddress] +}); + +// Check specific capability for Base mainnet +const baseCapabilities = capabilities["0x2105"]; // Base mainnet chain ID +``` + +## Available Capabilities + +| Capability | Method | Description | +|-----------|---------|-------------| +| [signInWithEthereum](/base-account/reference/core/capabilities/signInWithEthereum) | `wallet_connect` | SIWE authentication | +| [auxiliaryFunds](/base-account/reference/core/capabilities/auxiliaryFunds) | `wallet_sendCalls` | MagicSpend - use Coinbase balances onchain | +| [atomic](/base-account/reference/core/capabilities/atomic) | `wallet_sendCalls` | Atomic batch transactions | +| [paymasterService](/base-account/reference/core/capabilities/paymasterService) | `wallet_sendCalls` | Gasless transactions | +| [flowControl](/base-account/reference/core/capabilities/flowControl) | `wallet_sendCalls` | Flow control | +| [datacallback](/base-account/reference/core/capabilities/datacallback) | `wallet_sendCalls` | Data callback | + +## Using with wallet_connect + +The `wallet_connect` method supports capabilities for connection and authentication: + +### Basic Connection + +```typescript +// Simple connection without capabilities +const result = await provider.request({ + method: 'wallet_connect', + params: [{ + version: '1' + }] +}); +``` + +### Authentication with signInWithEthereum + +```typescript +// Generate nonce for security +const nonce = window.crypto.randomUUID().replace(/-/g, ''); + +const { accounts } = await provider.request({ + method: 'wallet_connect', + params: [{ + version: '1', + capabilities: { + signInWithEthereum: { + nonce, + chainId: '0x2105' // Base Mainnet + } + } + }] +}); + +// Extract authentication data +const { address } = accounts[0]; +const { message, signature } = accounts[0].capabilities.signInWithEthereum; + +// Verify signature on your backend +await fetch('/auth/verify', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ address, message, signature }) +}); +``` + +## Using with wallet_sendCalls + +The `wallet_sendCalls` method supports transaction-related capabilities: + +### Basic Transaction + +```typescript +// Simple transaction without capabilities +const result = await provider.request({ + method: 'wallet_sendCalls', + params: [{ + version: '1.0', + chainId: '0x2105', + from: userAddress, + calls: [{ + to: '0x...', + value: '0x0', + data: '0x...' + }] + }] +}); +``` + +### Gasless Transactions with Paymaster + +```typescript +await provider.request({ + method: 'wallet_sendCalls', + params: [{ + version: '1.0', + chainId: '0x2105', + from: userAddress, + calls: [{ + to: contractAddress, + value: '0x0', + data: encodedFunctionCall + }], + capabilities: { + paymasterService: { + url: "https://paymaster.base.org/api/v1/sponsor" + } + } + }] +}); +``` + +### Atomic Batch Transactions + +```typescript +await provider.request({ + method: 'wallet_sendCalls', + params: [{ + version: '1.0', + chainId: '0x2105', + from: userAddress, + atomicRequired: true, // Require atomic execution + calls: [ + { + to: tokenAddress, + value: '0x0', + data: approveCallData + }, + { + to: dexAddress, + value: '0x0', + data: swapCallData + } + ] + }] +}); +``` + +## Capability Detection Patterns + +### Check Single Capability + +```typescript +async function checkAuxiliaryFunds(address: string): Promise { + try { + const capabilities = await provider.request({ + method: 'wallet_getCapabilities', + params: [address] + }); + + return capabilities["0x2105"]?.auxiliaryFunds?.supported || false; + } catch (error) { + console.error('Failed to check capabilities:', error); + return false; + } +} +``` + +### Check Multiple Capabilities + +```typescript +async function getWalletCapabilities(address: string) { + const capabilities = await provider.request({ + method: 'wallet_getCapabilities', + params: [address] + }); + + const baseCapabilities = capabilities["0x2105"] || {}; + + return { + hasAuxiliaryFunds: baseCapabilities.auxiliaryFunds?.supported || false, + hasAtomicBatch: baseCapabilities.atomic?.supported === "supported", + hasPaymaster: !!baseCapabilities.paymasterService?.supported, + canAuthenticate: true // signInWithEthereum is always available with wallet_connect + }; +} +``` + +## Capability-Specific Guides + +For detailed information on each capability: + +- [signInWithEthereum](/base-account/reference/core/capabilities/signInWithEthereum) - SIWE authentication +- [auxiliaryFunds](/base-account/reference/core/capabilities/auxiliaryFunds) - MagicSpend integration +- [atomic](/base-account/reference/core/capabilities/atomic) - Atomic batch transactions +- [paymasterService](/base-account/reference/core/capabilities/paymasterService) - Gasless transactions + +## Related Methods + +- [`wallet_getCapabilities`](/base-account/reference/core/provider-rpc-methods/wallet_getCapabilities) - Discover available capabilities +- [`wallet_connect`](/base-account/reference/core/provider-rpc-methods/wallet_connect) - Connect with capabilities +- [`wallet_sendCalls`](/base-account/reference/core/provider-rpc-methods/wallet_sendCalls) - Execute transactions with capabilities + +import PolicyBanner from "/snippets/PolicyBanner.mdx"; + + diff --git a/docs/base-account/reference/core/capabilities/paymasterService.mdx b/docs/base-account/reference/core/capabilities/paymasterService.mdx new file mode 100644 index 000000000..2debdcb63 --- /dev/null +++ b/docs/base-account/reference/core/capabilities/paymasterService.mdx @@ -0,0 +1,322 @@ +--- +title: "paymasterService" +description: "Enable sponsored transactions using ERC-4337 paymaster web services" +--- + +Defined in [ERC-7677](https://eips.ethereum.org/EIPS/eip-7677) + + +The paymasterService capability enables apps to sponsor user transactions using ERC-4337 paymaster web services. This allows users to execute transactions without paying gas fees directly. + + + +This capability is not yet finalized and may change in future iterations. + + +## Parameters + + +The URL of the ERC-7677-compliant paymaster service that will sponsor the transactions. + +**Format:** Must be a valid HTTPS URL pointing to a paymaster service endpoint. + + +## Returns + + +The paymaster service capability configuration for the specified chain. + + + +Indicates whether the wallet supports paymaster service integration. + + + + +## Example Usage + + +```typescript Check Paymaster Support +const capabilities = await provider.request({ + method: 'wallet_getCapabilities', + params: [userAddress] +}); + +const paymasterSupport = capabilities["0x2105"]?.paymasterService; +``` + +```typescript Send Sponsored Transaction +const result = await provider.request({ + method: 'wallet_sendCalls', + params: [{ + version: "1.0", + chainId: "0x2105", + from: userAddress, + calls: [{ + to: "0x1234567890123456789012345678901234567890", + value: "0x0", + data: "0xa9059cbb000000000000000000000000..." + }], + capabilities: { + paymasterService: { + url: "https://your-paymaster-service.xyz" + } + } + }] +}); +``` + + + +```json Capability Response (Supported) +{ + "0x2105": { + "paymasterService": { + "supported": true + } + } +} +``` + +```json Capability Response (Unsupported) +{ + "0x2105": { + "paymasterService": { + "supported": false + } + } +} +``` + + +## Error Handling + +| Code | Message | Description | +| ---- | ------- | ----------- | +| 4100 | Paymaster service not supported | Wallet does not support paymaster service integration | +| 4200 | Invalid paymaster URL | The provided paymaster service URL is invalid or unreachable | +| 4300 | Paymaster service error | The paymaster service returned an error or is unavailable | +| 5700 | Paymaster capability required | Transaction requires paymaster service but wallet doesn't support it | + +## Paymaster Service Implementation + +The paymaster service must implement ERC-7677 compliance with these endpoints: + +### 1. Gas Estimation Endpoint + +```typescript +// pm_getPaymasterStubData +POST /rpc +{ + "jsonrpc": "2.0", + "id": 1, + "method": "pm_getPaymasterStubData", + "params": [ + userOp, // User operation object + entryPoint, // Entry point address + chainId, // Chain ID + context // Additional context + ] +} +``` + +### 2. Paymaster Data Endpoint + +```typescript +// pm_getPaymasterData +POST /rpc +{ + "jsonrpc": "2.0", + "id": 1, + "method": "pm_getPaymasterData", + "params": [ + userOp, // User operation object + entryPoint, // Entry point address + chainId, // Chain ID + context // Additional context + ] +} +``` + +## Complete Example + +Here's a complete example of implementing sponsored transactions: + +```typescript +class SponsoredTransactionManager { + private paymasterUrl = "https://api.example.com/paymaster"; + + async executeSponsored(calls: any[]) { + try { + // 1. Check paymaster capability + const capabilities = await provider.request({ + method: 'wallet_getCapabilities', + params: [userAddress] + }); + + if (!capabilities["0x2105"]?.paymasterService?.supported) { + throw new Error("Paymaster services not supported"); + } + + // 2. Execute sponsored transaction + const result = await provider.request({ + method: 'wallet_sendCalls', + params: [{ + version: "1.0", + chainId: "0x2105", + from: userAddress, + calls, + capabilities: { + paymasterService: { + url: this.paymasterUrl + } + } + }] + }); + + console.log("Sponsored transaction submitted:", result); + return result; + + } catch (error) { + console.error("Sponsored transaction failed:", error); + throw error; + } + } + + // Example: Sponsored token transfer + async sponsoredTransfer(token: string, to: string, amount: string) { + const calls = [{ + to: token, + value: "0x0", + data: this.encodeTransfer(to, amount) + }]; + + return this.executeSponsored(calls); + } + + private encodeTransfer(to: string, amount: string): string { + // Encode ERC-20 transfer function call + // This is a simplified example + return `0xa9059cbb${to.slice(2).padStart(64, '0')}${BigInt(amount).toString(16).padStart(64, '0')}`; + } +} + +// Usage +const sponsoredTx = new SponsoredTransactionManager(); + +// Execute sponsored token transfer +await sponsoredTx.sponsoredTransfer( + "0xA0b86a33E6441b8a2f0d2d2a71Cba0F42c4B1D2E", // USDC token + "0x742d35Cc4Bf53E0e6C42E5d9F0A8D2F6D8A8B7C9", // recipient + "1000000" // 1 USDC (6 decimals) +); +``` + +## Error Handling + +Handle paymaster-related errors appropriately: + +```typescript +async function executeWithPaymaster(calls: any[]) { + try { + const result = await provider.request({ + method: 'wallet_sendCalls', + params: [{ + version: "1.0", + chainId: "0x2105", + from: userAddress, + calls, + capabilities: { + paymasterService: { + url: "https://paymaster.example.com" + } + } + }] + }); + + return result; + + } catch (error) { + if (error.code === 4100) { + console.error("Paymaster service not supported"); + // Fallback to regular transaction + return executeRegularTransaction(calls); + } else if (error.message.includes("paymaster")) { + console.error("Paymaster service error:", error); + // Handle paymaster-specific errors + throw new Error("Transaction sponsorship failed"); + } else { + console.error("Transaction failed:", error); + throw error; + } + } +} +``` + +## Use Cases + +### Gaming Applications + +```typescript +// Sponsor in-game item purchases +const gameItemPurchase = await provider.request({ + method: 'wallet_sendCalls', + params: [{ + version: "1.0", + chainId: "0x2105", + from: playerAddress, + calls: [{ + to: gameContractAddress, + value: "0x0", + data: purchaseItemCallData + }], + capabilities: { + paymasterService: { + url: "https://game-paymaster.example.com" + } + } + }] +}); +``` + +### DeFi Onboarding + +```typescript +// Sponsor first-time user transactions +const onboardingTx = await provider.request({ + method: 'wallet_sendCalls', + params: [{ + version: "1.0", + chainId: "0x2105", + from: newUserAddress, + calls: [ + // Stake tokens + { + to: stakingContract, + value: "0x0", + data: stakeCallData + } + ], + capabilities: { + paymasterService: { + url: "https://defi-onboarding-paymaster.example.com" + } + } + }] +}); +``` + +## Best Practices + +1. **Validate Paymaster URLs**: Ensure paymaster service URLs are trustworthy and ERC-7677 compliant +2. **Handle Failures Gracefully**: Implement fallbacks for when paymaster services are unavailable +3. **Monitor Costs**: Track paymaster usage to manage sponsorship costs +4. **User Communication**: Clearly communicate when transactions are sponsored + + +The paymaster service capability enables seamless user experiences by removing the need for users to hold native tokens for gas fees. + + +import PolicyBanner from "/snippets/PolicyBanner.mdx"; + + \ No newline at end of file diff --git a/docs/base-account/reference/core/capabilities/signInWithEthereum.mdx b/docs/base-account/reference/core/capabilities/signInWithEthereum.mdx new file mode 100644 index 000000000..ed5cf53fc --- /dev/null +++ b/docs/base-account/reference/core/capabilities/signInWithEthereum.mdx @@ -0,0 +1,336 @@ +--- +title: "signInWithEthereum" +description: "Enable secure authentication using the Sign-In With Ethereum (SIWE) standard" +--- + +Defined in [EIP-4361](https://eips.ethereum.org/EIPS/eip-4361) + + +The signInWithEthereum capability enables secure user authentication following the SIWE (Sign-In With Ethereum) standard. This capability is only available with the `wallet_connect` method and provides a standardized way to authenticate users with their Ethereum accounts. + + +## Parameters + + +A unique random string to prevent replay attacks. Should be generated fresh for each authentication attempt. + + + +The chain ID as a hexadecimal string (e.g., "0x2105" for Base Mainnet). + + +## Returns + + +Authentication result containing the signed message and signature. + + + +The SIWE-formatted message that was signed by the user. + + + +The cryptographic signature of the message, which can be verified on your backend. + + + + +## Usage with wallet_connect + +The `signInWithEthereum` capability must be used with the `wallet_connect` method: + + +```typescript Basic Authentication +import { createBaseAccountSDK } from '@base-org/account'; + +const provider = createBaseAccountSDK().getProvider(); + +// Generate a unique nonce +const nonce = window.crypto.randomUUID().replace(/-/g, ''); + +try { + // Connect with signInWithEthereum capability + const { accounts } = await provider.request({ + method: 'wallet_connect', + params: [{ + version: '1', + capabilities: { + signInWithEthereum: { + nonce, + chainId: '0x2105' // Base Mainnet + } + } + }] + }); + + // Extract authentication data + const { address } = accounts[0]; + const { message, signature } = accounts[0].capabilities.signInWithEthereum; + + console.log('User address:', address); + console.log('Signed message:', message); + console.log('Signature:', signature); +} catch (error) { + console.error('Authentication failed:', error); +} +``` + +```typescript Backend Verification +import { createPublicClient, http } from 'viem'; +import { base } from 'viem/chains'; + +const client = createPublicClient({ + chain: base, + transport: http() +}); + +export async function verifyAuthentication(req, res) { + const { address, message, signature } = req.body; + + try { + // Verify the signature + const isValid = await client.verifyMessage({ + address, + message, + signature + }); + + if (!isValid) { + return res.status(401).json({ + error: 'Invalid signature' + }); + } + + // Create session or JWT token + const token = generateAuthToken(address); + + res.json({ + success: true, + token + }); + } catch (error) { + console.error('Verification failed:', error); + res.status(500).json({ + error: 'Verification failed' + }); + } +} +``` + + + +```json Authentication Response +{ + "accounts": [{ + "address": "0x1234567890123456789012345678901234567890", + "capabilities": { + "signInWithEthereum": { + "message": "localhost:3000 wants you to sign in with your Ethereum account:\n0x1234567890123456789012345678901234567890\n\nSign in with Ethereum to the app.\n\nURI: http://localhost:3000\nVersion: 1\nChain ID: 8453\nNonce: abc123def456\nIssued At: 2024-01-15T10:30:00Z", + "signature": "0x1234567890abcdef..." + } + } + }], + "chainId": "0x2105", + "isConnected": true +} +``` + + +## Security Considerations + +### Nonce Management + +Always use fresh, unique nonces for each authentication attempt: + +```typescript +// Generate cryptographically secure nonce +const nonce = window.crypto.randomUUID().replace(/-/g, ''); + +// Or fetch from your backend +const nonce = await fetch('/auth/nonce').then(r => r.text()); +``` + +### Backend Verification + +Verify signatures on your backend to prevent tampering: + +```typescript +// Server-side nonce tracking +const usedNonces = new Set(); + +export async function verifyAuth(req, res) { + const { address, message, signature } = req.body; + + // Extract nonce from message + const nonce = extractNonceFromMessage(message); + + // Check if nonce has been used + if (usedNonces.has(nonce)) { + return res.status(400).json({ + error: 'Nonce already used' + }); + } + + // Verify signature + const isValid = await client.verifyMessage({ + address, + message, + signature + }); + + if (isValid) { + usedNonces.add(nonce); + // Create session... + } +} +``` + +## Integration Examples + +### Express.js Backend + +```typescript +import express from 'express'; +import crypto from 'crypto'; +import { createPublicClient, http } from 'viem'; +import { base } from 'viem/chains'; + +const app = express(); +app.use(express.json()); + +const client = createPublicClient({ chain: base, transport: http() }); +const nonces = new Set(); + +// Generate nonce endpoint +app.get('/auth/nonce', (_, res) => { + const nonce = crypto.randomBytes(16).toString('hex'); + nonces.add(nonce); + res.send(nonce); +}); + +// Verify authentication +app.post('/auth/verify', async (req, res) => { + const { address, message, signature } = req.body; + + // Extract and validate nonce + const nonce = message.match(/Nonce: (\w+)/)?.[1]; + if (!nonce || !nonces.delete(nonce)) { + return res.status(400).json({ + error: 'Invalid or reused nonce' + }); + } + + // Verify signature + const valid = await client.verifyMessage({ + address, + message, + signature + }); + + if (!valid) { + return res.status(401).json({ + error: 'Invalid signature' + }); + } + + // Success - create session + res.json({ success: true }); +}); +``` + +### React Integration + +```tsx +import { useState } from 'react'; +import { createBaseAccountSDK } from '@base-org/account'; +import { SignInWithBaseButton } from '@base-org/account-ui/react'; + +export function AuthComponent() { + const [user, setUser] = useState(null); + const [loading, setLoading] = useState(false); + + const handleSignIn = async () => { + setLoading(true); + + try { + const provider = createBaseAccountSDK().getProvider(); + + // Generate nonce + const nonce = window.crypto.randomUUID().replace(/-/g, ''); + + // Authenticate with Base Account + const { accounts } = await provider.request({ + method: 'wallet_connect', + params: [{ + version: '1', + capabilities: { + signInWithEthereum: { + nonce, + chainId: '0x2105' + } + } + }] + }); + + const { address } = accounts[0]; + const { message, signature } = accounts[0].capabilities.signInWithEthereum; + + // Verify on backend + const response = await fetch('/auth/verify', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ address, message, signature }) + }); + + if (response.ok) { + setUser({ address }); + } + } catch (error) { + console.error('Authentication failed:', error); + } finally { + setLoading(false); + } + }; + + return ( +
+ {user ? ( +
Welcome, {user.address}
+ ) : ( + + )} +
+ ); +} +``` + +## Error Handling + +| Code | Message | Description | +| ---- | ------- | ----------- | +| 4001 | User rejected the request | User denied the authentication request | +| 4100 | Method not supported | Wallet doesn't support signInWithEthereum capability | +| -32602 | Invalid params | Invalid nonce or chainId provided | + + +The `signInWithEthereum` capability only works with the `wallet_connect` method. Using it with other methods like `eth_requestAccounts` will not work. + + + +Base Account signatures include ERC-6492 wrapper for undeployed smart wallets, which viem's `verifyMessage` handles automatically. + + +## Best Practices + +1. **Fresh Nonces**: Always generate unique nonces for each authentication attempt +2. **Secure Generation**: Use cryptographically secure random number generation +3. **Nonce Tracking**: Track used nonces on your backend to prevent replay attacks +4. **Signature Verification**: Always verify signatures on your backend, never trust client-side verification +5. **Session Management**: Create secure sessions or JWT tokens after successful verification + +import PolicyBanner from "/snippets/PolicyBanner.mdx"; + + diff --git a/docs/base-account/reference/core/createBaseAccount.mdx b/docs/base-account/reference/core/createBaseAccount.mdx new file mode 100644 index 000000000..4aa2c86da --- /dev/null +++ b/docs/base-account/reference/core/createBaseAccount.mdx @@ -0,0 +1,332 @@ +--- +title: "createBaseAccountSDK" +description: "Create a Base Account SDK instance with EIP-1193 compliant provider" +--- + +Defined in the [Base Account SDK](https://github.com/base/account-sdk) + + +Creates a Base Account SDK instance that provides an EIP-1193 compliant Ethereum provider and additional account management functionality. This is the primary entry point for integrating Base Account into your application. + + +## Parameters + + +Configuration options for creating the SDK instance. + + + +The name of your application. Defaults to "App" if not provided. + + + +URL to your application's logo image. Used in wallet UI. Defaults to empty string if not provided. + + + +Array of chain IDs that your application supports. Defaults to empty array if not provided. + + + +Optional preferences for SDK behavior. + + + +Custom wallet URL override. Only use when overriding the default wallet URL with a custom environment. + + + +Attribution configuration for Smart Wallet transactions. + + + +When true, Smart Wallet will generate a 16 byte hex string from the app's origin. + + + +Custom 16 byte hex string appended to initCode and executeBatch calldata. Cannot be used with `auto: true`. + + + + + +Whether to enable functional telemetry. Defaults to `true`. + + + + + +Sub-account configuration options. + + + +Function that returns the owner account for signing sub-account transactions. + + +```typescript +type ToOwnerAccountFn = () => Promise<{ account: OwnerAccount | null; }> +``` + +Where `OwnerAccount` is a union type of: +- `LocalAccount` (from viem) - A local account with private key +- `WebAuthnAccount` (from viem) - A WebAuthn-based account for passkey authentication + + + +When true (default), enables Auto Spend Permissions for sub-accounts. This allows automatic transfers from the user's Base Account to the sub-account when funds are missing and attempts background transactions using existing spend permissions. Set to false to disable this behavior. Learn more in [Auto Spend Permissions](/base-account/improve-ux/sub-accounts#auto-spend-permissions). + + + + + +Mapping of chain IDs to paymaster URLs for gasless transactions. + + + + +## Returns + + +SDK instance with provider and sub-account management capabilities. + + + +Returns an EIP-1193 compliant Ethereum provider that can be used with web3 libraries like Viem, Wagmi, and Web3.js. + + + +Sub-account management methods. + + + +Creates a new sub-account. + + + +Retrieves the current sub-account information. + + + +Adds an owner to the sub-account. + + + +Sets the function for determining the owner account. The function should return a Promise resolving to an object with an `account` property that is either a `LocalAccount`, `WebAuthnAccount`, or `null`. + + + + + + + +```typescript Basic Setup +import { createBaseAccountSDK } from '@base-org/account'; +import { base } from 'viem/chains'; + +const sdk = createBaseAccountSDK({ + appName: 'My DApp', + appLogoUrl: 'https://mydapp.com/logo.png', + appChainIds: [base.id], +}); + +const provider = sdk.getProvider(); +``` + +```typescript Advanced Configuration +import { createBaseAccountSDK } from '@base-org/account'; +import { base, baseSepolia } from 'viem/chains'; + +const sdk = createBaseAccountSDK({ + appName: 'My Advanced DApp', + appLogoUrl: 'https://mydapp.com/logo.png', + appChainIds: [base.id, baseSepolia.id], + preference: { + attribution: { + auto: true + }, + telemetry: true + }, + subAccounts: { + toOwnerAccount: async () => ({ + account: cryptoAccount?.account || null + }) + }, + paymasterUrls: { + [base.id]: 'https://paymaster.base.org', + [baseSepolia.id]: 'https://paymaster.base-sepolia.org' + } +}); +``` + +```typescript With Sub-Accounts +import { createBaseAccountSDK } from '@base-org/account'; + +const sdk = createBaseAccountSDK({ + appName: 'Sub-Account App', + appChainIds: [8453], + subAccounts: { + toOwnerAccount: async () => { + // Return the owner account that will sign sub-account transactions + // mainAccount should be a LocalAccount or WebAuthnAccount from viem + return { account: mainAccount || null }; + } + } +}); + +// Create a sub-account +const subAccount = await sdk.subAccount.create({ + type: 'create', + keys: [{ + type: 'p256', + publicKey: '0x...' + }] +}); + +// Get existing sub-account +const existingSubAccount = await sdk.subAccount.get(); +``` + + +## Integration Examples + +### With Viem + +```typescript +import { createWalletClient, custom } from 'viem'; +import { base } from 'viem/chains'; +import { createBaseAccountSDK } from '@base-org/account'; + +const sdk = createBaseAccountSDK({ + appName: 'Viem Integration', + appChainIds: [base.id] +}); + +const provider = sdk.getProvider(); + +const client = createWalletClient({ + chain: base, + transport: custom(provider) +}); +``` + +### With Wagmi + +```typescript +import { createConfig, custom } from 'wagmi'; +import { base } from 'wagmi/chains'; +import { createBaseAccountSDK } from '@base-org/account'; + +const sdk = createBaseAccountSDK({ + appName: 'Wagmi Integration', + appChainIds: [base.id] +}); + +const provider = sdk.getProvider(); + +const config = createConfig({ + chains: [base], + transports: { + [base.id]: custom(provider), + }, +}); +``` + +## Configuration Options + +### Attribution + +Configure transaction attribution for analytics and tracking: + +```typescript +// Auto-generate attribution from app origin +const sdk = createBaseAccountSDK({ + appName: 'My App', + preference: { + attribution: { auto: true } + } +}); + +// Custom attribution data +const sdk = createBaseAccountSDK({ + appName: 'My App', + preference: { + attribution: { dataSuffix: '0x1234567890123456789012345678901234567890' } + } +}); +``` + +### Paymaster Integration + +Enable gasless transactions with paymaster URLs: + +```typescript +const sdk = createBaseAccountSDK({ + appName: 'Gasless App', + appChainIds: [8453, 84532], + paymasterUrls: { + 8453: 'https://paymaster.base.org/api/v1/sponsor', + 84532: 'https://paymaster.base-sepolia.org/api/v1/sponsor' + } +}); +``` + +## Error Handling + +The SDK initialization is synchronous and will validate preferences during creation: + +```typescript +try { + const sdk = createBaseAccountSDK({ + appName: 'My App', + appChainIds: [8453], + subAccounts: { + toOwnerAccount: invalidFunction // Will throw validation error + } + }); +} catch (error) { + console.error('SDK initialization failed:', error); +} +``` + +## TypeScript Support + +The SDK is fully typed for TypeScript development: + +```typescript +import type { + CreateProviderOptions, + BaseAccountSDK, + ProviderInterface, + ToOwnerAccountFn +} from '@base-org/account'; +import { LocalAccount } from 'viem'; + +const toOwnerAccount: ToOwnerAccountFn = async () => { + // Your logic to get the owner account + const ownerAccount: LocalAccount | null = getOwnerAccount(); + return { account: ownerAccount }; +}; + +const options: CreateProviderOptions = { + appName: 'Typed App', + appChainIds: [8453], + subAccounts: { + toOwnerAccount + } +}; + +const sdk: BaseAccountSDK = createBaseAccountSDK(options); +const provider: ProviderInterface = sdk.getProvider(); +``` + + +The SDK automatically manages Cross-Origin-Opener-Policy validation and telemetry initialization. Make sure your application's headers allow popup windows if using the default wallet interface. + + + +The `createBaseAccountSDK` function is the primary entry point for Base Account integration. It provides both a standard EIP-1193 provider and advanced features like sub-account management and gasless transactions. + + +import PolicyBanner from "/snippets/PolicyBanner.mdx"; + + diff --git a/docs/base-account/reference/core/generateKeyPair.mdx b/docs/base-account/reference/core/generateKeyPair.mdx new file mode 100644 index 000000000..942bcb070 --- /dev/null +++ b/docs/base-account/reference/core/generateKeyPair.mdx @@ -0,0 +1,140 @@ +--- +title: "generateKeyPair" +description: "Generate a new P256 key pair for use with Base Account" +--- + +Defined in the [Base Account SDK](https://github.com/base/account-sdk) + + +Generates a new P256 key pair for use with Base Account. This is essential for advanced integrations and Sub Account management. + + +## Parameters + +This function takes no parameters. + +## Returns + + +A P256 key pair object containing the public and private keys. + + + +The public key for the generated pair in hexadecimal format. + + + +The private key for the generated pair. Handle with extreme care. + + + + + +```typescript Basic Usage +import { generateKeyPair } from '@base-org/account'; + +const keyPair = await generateKeyPair(); +console.log('Public key:', keyPair.publicKey); +``` + +```typescript Error Handling +try { + const keyPair = await generateKeyPair(); + return keyPair; +} catch (error) { + console.error('Failed to generate key pair:', error); + throw error; +} +``` + + + +```typescript Success Response +{ + publicKey: "0x04a1b2c3d4e5f6...", + privateKey: "0x1a2b3c4d5e6f7a..." +} +``` + + +## Error Handling + +| Code | Message | Description | +| ---- | ------- | ----------- | +| 4100 | Key generation not supported | Browser does not support cryptographic key generation | +| 4200 | Insufficient entropy | System lacks sufficient randomness for secure key generation | +| 4300 | Cryptographic system failure | Hardware or software cryptographic failure | + + +**Private Key Security** + +Never expose private keys in client-side code or transmit them over insecure channels. Store them securely using appropriate key management systems. + + +## Integration with Sub Accounts + +```typescript +import { generateKeyPair, createBaseAccountSDK } from '@base-org/account'; + +async function createSubAccountWithNewKeys() { + const sdk = createBaseAccountSDK({ + appName: 'My App', + appLogoUrl: 'https://example.com/logo.png', + appChainIds: [8453], // Base mainnet + }); + + // Generate new key pair for sub account + const keyPair = await generateKeyPair(); + + // Create sub account with the generated keys + const subAccount = await sdk.subAccount.create({ + type: 'create', + keys: [{ + type: 'webauthn-p256', + publicKey: keyPair.publicKey, + }], + }); + + return { subAccount, keyPair }; +} +``` + +## Error Handling + +The `generateKeyPair` function can throw errors for: + +- Cryptographic system failures +- Insufficient entropy +- Browser compatibility issues + +Always wrap calls to `generateKeyPair` in a try-catch block: + +```typescript +try { + const keyPair = await generateKeyPair(); + // Handle successful generation +} catch (error) { + if (error.message.includes('not supported')) { + console.error('Browser does not support key generation'); + } else { + console.error('Key generation failed:', error); + } +} +``` + +## Security Considerations + + +**Private Key Security** + +Never expose private keys in client-side code or transmit them over insecure channels. Store them securely using appropriate key management systems. + + +- Store private keys using secure storage mechanisms +- Never log private keys to console in production +- Consider using hardware security modules for production applications +- Implement proper key rotation policies + +import PolicyBanner from "/snippets/PolicyBanner.mdx"; + + \ No newline at end of file diff --git a/docs/base-account/reference/core/getCryptoKeyAccount.mdx b/docs/base-account/reference/core/getCryptoKeyAccount.mdx new file mode 100644 index 000000000..cf82c3e12 --- /dev/null +++ b/docs/base-account/reference/core/getCryptoKeyAccount.mdx @@ -0,0 +1,226 @@ +--- +title: "getCryptoKeyAccount" +description: "Retrieve the current crypto key account associated with the user's session" +--- + +Defined in the [Base Account SDK](https://github.com/base/account-sdk) + + + +## Parameters + +This function takes no parameters. + +## Returns + + +An object containing the user's crypto key account information or null if none exists. + + + +The user's crypto key account object, or null if none is available. + + + + +Public key associated with the account. + + + +Account type identifier. Value: "webauthn" + + + + + +Ethereum address of the account (42-character hex string starting with 0x). + + + +Public key associated with the account. + + + +Account type identifier. Value: "local" + + + + + + + +```typescript Basic Usage +import { getCryptoKeyAccount } from '@base-org/account'; + +const cryptoAccount = await getCryptoKeyAccount(); +if (cryptoAccount?.account) { + console.log('Account address:', cryptoAccount.account.address); +} +``` + +```typescript Account Verification +const cryptoAccount = await getCryptoKeyAccount(); + +if (!cryptoAccount?.account) { + console.log('No account found - user needs to sign in'); + return null; +} + +const { account } = cryptoAccount; +console.log('Account type:', account.type); +``` + + + +```typescript Success Response (WebAuthn Account) +{ + account: { + address: "0xd46e8dd67c5d32be8058bb8eb970870f07244567", + publicKey: "0x04a1b2c3d4e5f6...", + type: "webauthn" + } +} +``` + +```typescript Success Response (Local Account) +{ + account: { + address: "0x742d35Cc4Bf53E0e6C42E5d9F0A8D2F6D8A8B7C9", + publicKey: "0x04b2c3d4e5f6a7...", + type: "local" + } +} +``` + +```typescript Success Response (No Account) +{ + account: null +} +``` + + +## Error Handling + +| Code | Message | Description | +| ---- | ------- | ----------- | +| 4001 | User denied account access | User rejected the account access request | +| 4100 | SDK not initialized | Base Account SDK not properly initialized | +| 4200 | Session expired | User session has expired, requires reconnection | +| 4300 | Account unavailable | Account temporarily unavailable | + + +Always check if the account exists before using it, as users may not be connected or may have disconnected. + + +## Account State Management + +```typescript +import { getCryptoKeyAccount } from '@base-org/account'; + +class AccountManager { + private currentAccount: WebAuthnAccount | LocalAccount | null = null; + + async initialize() { + const cryptoAccount = await getCryptoKeyAccount(); + this.currentAccount = cryptoAccount?.account || null; + + return this.isConnected(); + } + + isConnected(): boolean { + return !!this.currentAccount; + } + + getAddress(): string | null { + return this.currentAccount?.address || null; + } + + getAccountType(): 'webauthn' | 'local' | null { + return this.currentAccount?.type || null; + } + + async refresh() { + const cryptoAccount = await getCryptoKeyAccount(); + const newAccount = cryptoAccount?.account; + + // Check if account changed + if (newAccount?.address !== this.currentAccount?.address) { + console.log('Account changed:', newAccount?.address); + this.currentAccount = newAccount; + return true; + } + + return false; + } +} +``` + +## Integration with Provider + +```typescript +import { getCryptoKeyAccount, createBaseAccountSDK } from '@base-org/account'; + +async function initializeApp() { + const sdk = createBaseAccountSDK({ + appName: 'My App', + appLogoUrl: 'https://example.com/logo.png', + appChainIds: [8453], // Base mainnet + }); + + // Check current account status + const cryptoAccount = await getCryptoKeyAccount(); + + if (cryptoAccount?.account) { + console.log('User is already connected:', cryptoAccount.account.address); + + // Get provider for transactions + const provider = sdk.getProvider(); + + return { + sdk, + provider, + account: cryptoAccount.account, + isConnected: true + }; + } else { + console.log('User needs to connect'); + + return { + sdk, + provider: null, + account: null, + isConnected: false + }; + } +} +``` + +## Account Verification + +```typescript +async function verifyAccountAccess() { + const cryptoAccount = await getCryptoKeyAccount(); + + if (!cryptoAccount?.account) { + throw new Error('No account available'); + } + + const { account } = cryptoAccount; + + // Verify account has required properties + if (!account.address || !account.publicKey) { + throw new Error('Invalid account data'); + } + + // Verify address format + if (!/^0x[a-fA-F0-9]{40}$/.test(account.address)) { + throw new Error('Invalid address format'); + } + + return account; +} +``` + +import PolicyBanner from "/snippets/PolicyBanner.mdx"; + + \ No newline at end of file diff --git a/docs/base-account/reference/core/getKeypair.mdx b/docs/base-account/reference/core/getKeypair.mdx new file mode 100644 index 000000000..0416a0c4f --- /dev/null +++ b/docs/base-account/reference/core/getKeypair.mdx @@ -0,0 +1,190 @@ +--- +title: "getKeypair" +description: "Retrieve an existing P256 key pair from storage" +--- + +Defined in the [Base Account SDK](https://github.com/base/account-sdk) + + +Retrieves an existing P256 key pair if one has been previously generated and stored. This is useful for checking if keys already exist before generating new ones. + + +## Parameters + +This function takes no parameters. + +## Returns + + +The stored P256 key pair or `null` if no key pair exists. + + + +The public key for the stored pair in hexadecimal format. + + + +The private key for the stored pair. Handle with extreme care. + + + + + +```typescript Basic Usage +import { getKeypair } from '@base-org/account'; + +const existingKeyPair = await getKeypair(); +if (existingKeyPair) { + console.log('Found existing key pair'); +} else { + console.log('No existing key pair found'); +} +``` + +```typescript Get or Create Pattern +import { getKeypair, generateKeyPair } from '@base-org/account'; + +let keyPair = await getKeypair(); +if (!keyPair) { + keyPair = await generateKeyPair(); +} +``` + + + +```typescript Success Response (Key Pair Found) +{ + publicKey: "0x04a1b2c3d4e5f6...", + privateKey: "0x1a2b3c4d5e6f7a..." +} +``` + +```typescript Success Response (No Key Pair) +null +``` + + + +**Private Key Access** + +The retrieved private keys should be handled with the same security considerations as newly generated keys. + + +## Get or Create Pattern + +A common pattern is to check for existing keys before generating new ones: + +```typescript +import { getKeypair, generateKeyPair } from '@base-org/account'; + +async function getOrCreateKeyPair() { + // Try to get existing key pair first + let keyPair = await getKeypair(); + + if (!keyPair) { + // Generate new key pair if none exists + console.log('No existing key pair, generating new one...'); + keyPair = await generateKeyPair(); + } else { + console.log('Using existing key pair'); + } + + return keyPair; +} +``` + + + +## Storage Behavior + +The `getKeypair` function retrieves keys from: + +- Browser's secure storage (for web applications) +- Platform-specific secure storage (for native applications) +- Memory cache (for the current session) + + +Key pairs are stored securely and are only accessible within the same origin and application context. + + +## Error Handling + +The `getKeypair` function can throw errors for: + +- Storage access failures +- Data corruption issues +- Browser compatibility problems + +Always wrap calls to `getKeypair` in a try-catch block: + +```typescript +try { + const keyPair = await getKeypair(); + if (keyPair) { + // Use existing keys + } else { + // No keys found, may need to generate new ones + } +} catch (error) { + console.error('Error accessing key storage:', error); + // Handle storage access errors +} +``` + +## Key Lifecycle Management + +```typescript +class KeyManager { + private keyPair: P256KeyPair | null = null; + + async initialize() { + try { + // Load existing keys + this.keyPair = await getKeypair(); + + if (this.keyPair) { + console.log('Loaded existing key pair'); + } else { + console.log('No stored keys found'); + } + + return !!this.keyPair; + } catch (error) { + console.error('Failed to initialize key manager:', error); + return false; + } + } + + hasKeys(): boolean { + return !!this.keyPair; + } + + async ensureKeys(): Promise { + if (!this.keyPair) { + console.log('Generating new key pair...'); + this.keyPair = await generateKeyPair(); + } + return this.keyPair; + } + + getPublicKey(): string | null { + return this.keyPair?.publicKey || null; + } +} +``` + +## Security Considerations + + +**Private Key Access** + +The retrieved private keys should be handled with the same security considerations as newly generated keys. + + +- Always verify key integrity before use +- Implement proper access controls +- Consider re-generating keys periodically for enhanced security + +import PolicyBanner from "/snippets/PolicyBanner.mdx"; + + \ No newline at end of file diff --git a/docs/base-account/reference/core/getProvider.mdx b/docs/base-account/reference/core/getProvider.mdx index 72373fee2..4d32e5219 100644 --- a/docs/base-account/reference/core/getProvider.mdx +++ b/docs/base-account/reference/core/getProvider.mdx @@ -22,25 +22,11 @@ const provider = sdk.getProvider(); ## Returns An EIP-1193 compliant Ethereum provider that supports: -- Standard RPC methods (`eth_requestAccounts`, `eth_sendTransaction`, etc.) -- Custom wallet methods (`wallet_sendCalls`, `wallet_connect`, etc.) +- Standard RPC methods (`eth_requestAccounts`, `eth_sendTransaction`, `wallet_sendCalls` etc.) +- Custom Wallet methods (`coinbase_fetchPermissions`) - Event subscription (`accountsChanged`, `chainChanged`, etc.) -## Provider Methods - -The returned provider supports all standard Ethereum JSON-RPC methods as well as Base Account-specific methods: - -### Standard Methods -- `eth_requestAccounts` - Request account access -- `eth_sendTransaction` - Send a transaction -- `eth_accounts` - Get connected accounts -- `personal_sign` - Sign a message -- `eth_signTypedData_v4` - Sign typed data - -### Base Account Methods -- `wallet_sendCalls` - Send batch transactions -- `wallet_connect` - Connect to Base Account -- `wallet_addSubAccount` - Add a sub account +For a full list of supported methods, see the [Provider Section](/base-account/reference/core/provider-rpc-methods/request-overview) ## Integration Examples @@ -194,7 +180,3 @@ const provider: EIP1193Provider = sdk.getProvider(); ``` The `getProvider()` method is the primary way to interact with Base Account from your application, providing a standard interface that works seamlessly with the web3 ecosystem. - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/base-account/reference/core/provider-rpc-methods/coinbase_fetchPermissions.mdx b/docs/base-account/reference/core/provider-rpc-methods/coinbase_fetchPermissions.mdx index 3729e5294..43c9e7796 100644 --- a/docs/base-account/reference/core/provider-rpc-methods/coinbase_fetchPermissions.mdx +++ b/docs/base-account/reference/core/provider-rpc-methods/coinbase_fetchPermissions.mdx @@ -112,7 +112,3 @@ Pagination information for the response. Ensure the `chainId`, `account`, and `spender` parameters are correctly formatted and valid for the blockchain you are querying. - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/base-account/reference/core/provider-rpc-methods/eth_accounts.mdx b/docs/base-account/reference/core/provider-rpc-methods/eth_accounts.mdx index 94d61ec77..e9c2de8ba 100644 --- a/docs/base-account/reference/core/provider-rpc-methods/eth_accounts.mdx +++ b/docs/base-account/reference/core/provider-rpc-methods/eth_accounts.mdx @@ -59,7 +59,3 @@ An array of Ethereum addresses (hexadecimal strings), which the connected user c | 4100 | Requested method not supported | The provider does not support the `eth_accounts` method | | 4900 | Disconnected | The provider is disconnected from the wallet | - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/base-account/reference/core/provider-rpc-methods/eth_blockNumber.mdx b/docs/base-account/reference/core/provider-rpc-methods/eth_blockNumber.mdx index f31e6ddba..d06d4d1c2 100644 --- a/docs/base-account/reference/core/provider-rpc-methods/eth_blockNumber.mdx +++ b/docs/base-account/reference/core/provider-rpc-methods/eth_blockNumber.mdx @@ -49,7 +49,3 @@ A hexadecimal string representing the integer of the current block number the cl The result is the block number in hexadecimal format. Convert to decimal to get the actual block number. - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/base-account/reference/core/provider-rpc-methods/eth_chainId.mdx b/docs/base-account/reference/core/provider-rpc-methods/eth_chainId.mdx index 82b131083..9d0a2e52e 100644 --- a/docs/base-account/reference/core/provider-rpc-methods/eth_chainId.mdx +++ b/docs/base-account/reference/core/provider-rpc-methods/eth_chainId.mdx @@ -57,7 +57,3 @@ A hexadecimal string representing the chain ID. Common chain IDs: Ethereum Mainnet (0x1), Base Mainnet (0x2105), Base Sepolia (0x14a34). - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/base-account/reference/core/provider-rpc-methods/eth_coinbase.mdx b/docs/base-account/reference/core/provider-rpc-methods/eth_coinbase.mdx index e96cb085b..81f7bdfa8 100644 --- a/docs/base-account/reference/core/provider-rpc-methods/eth_coinbase.mdx +++ b/docs/base-account/reference/core/provider-rpc-methods/eth_coinbase.mdx @@ -45,7 +45,3 @@ The current coinbase address (20 bytes). | Code | Message | Description | | ---- | ------------------------------ | ----------- | | 4100 | Requested method not supported | The method is not supported by the wallet | - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/base-account/reference/core/provider-rpc-methods/eth_estimateGas.mdx b/docs/base-account/reference/core/provider-rpc-methods/eth_estimateGas.mdx index f25192b08..72a093964 100644 --- a/docs/base-account/reference/core/provider-rpc-methods/eth_estimateGas.mdx +++ b/docs/base-account/reference/core/provider-rpc-methods/eth_estimateGas.mdx @@ -86,7 +86,3 @@ The amount of gas used as a hexadecimal string. The estimate may not be accurate if the blockchain state changes between the estimation and actual execution. - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/base-account/reference/core/provider-rpc-methods/eth_feeHistory.mdx b/docs/base-account/reference/core/provider-rpc-methods/eth_feeHistory.mdx index 47e72e4cf..1941877e5 100644 --- a/docs/base-account/reference/core/provider-rpc-methods/eth_feeHistory.mdx +++ b/docs/base-account/reference/core/provider-rpc-methods/eth_feeHistory.mdx @@ -104,7 +104,3 @@ Array of effective priority fee per gas data points for each block (optional). This method is useful for implementing dynamic fee estimation algorithms and understanding network congestion patterns. - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/base-account/reference/core/provider-rpc-methods/eth_gasPrice.mdx b/docs/base-account/reference/core/provider-rpc-methods/eth_gasPrice.mdx index e8e8ac8f8..b77de269d 100644 --- a/docs/base-account/reference/core/provider-rpc-methods/eth_gasPrice.mdx +++ b/docs/base-account/reference/core/provider-rpc-methods/eth_gasPrice.mdx @@ -49,7 +49,3 @@ A hexadecimal string representing the current gas price in wei. The result is in wei. To convert to gwei, divide by 10^9. - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/base-account/reference/core/provider-rpc-methods/eth_getBalance.mdx b/docs/base-account/reference/core/provider-rpc-methods/eth_getBalance.mdx index d39c98f0a..7a47563af 100644 --- a/docs/base-account/reference/core/provider-rpc-methods/eth_getBalance.mdx +++ b/docs/base-account/reference/core/provider-rpc-methods/eth_getBalance.mdx @@ -57,7 +57,3 @@ A hexadecimal string representing the current balance in wei. The result represents the balance in wei. To convert to ETH, divide by 10^18. - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/base-account/reference/core/provider-rpc-methods/eth_getBlockByHash.mdx b/docs/base-account/reference/core/provider-rpc-methods/eth_getBlockByHash.mdx index 86432c06a..878bf51cc 100644 --- a/docs/base-account/reference/core/provider-rpc-methods/eth_getBlockByHash.mdx +++ b/docs/base-account/reference/core/provider-rpc-methods/eth_getBlockByHash.mdx @@ -177,7 +177,3 @@ Array of uncle hashes. Setting `fullTransactionObjects` to true returns complete transaction details, while false returns only transaction hashes for better performance. - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/base-account/reference/core/provider-rpc-methods/eth_getBlockByNumber.mdx b/docs/base-account/reference/core/provider-rpc-methods/eth_getBlockByNumber.mdx index a05dc1cc6..d680e6c1a 100644 --- a/docs/base-account/reference/core/provider-rpc-methods/eth_getBlockByNumber.mdx +++ b/docs/base-account/reference/core/provider-rpc-methods/eth_getBlockByNumber.mdx @@ -178,7 +178,3 @@ Array of uncle hashes. Block parameter tags: "latest" (most recent block), "earliest" (genesis block), "pending" (next block to be mined), "safe" and "finalized" (post-merge Ethereum blocks). - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/base-account/reference/core/provider-rpc-methods/eth_getBlockTransactionCountByHash.mdx b/docs/base-account/reference/core/provider-rpc-methods/eth_getBlockTransactionCountByHash.mdx index 415247473..9f4cbb47e 100644 --- a/docs/base-account/reference/core/provider-rpc-methods/eth_getBlockTransactionCountByHash.mdx +++ b/docs/base-account/reference/core/provider-rpc-methods/eth_getBlockTransactionCountByHash.mdx @@ -64,7 +64,3 @@ This method returns the count of transactions in a specific block identified by ``` - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/base-account/reference/core/provider-rpc-methods/eth_getBlockTransactionCountByNumber.mdx b/docs/base-account/reference/core/provider-rpc-methods/eth_getBlockTransactionCountByNumber.mdx index 3877acc67..0e6343aa0 100644 --- a/docs/base-account/reference/core/provider-rpc-methods/eth_getBlockTransactionCountByNumber.mdx +++ b/docs/base-account/reference/core/provider-rpc-methods/eth_getBlockTransactionCountByNumber.mdx @@ -73,7 +73,3 @@ The number of transactions in this block as a hexadecimal string. This method returns the count of transactions in a specific block identified by its number. - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/base-account/reference/core/provider-rpc-methods/eth_getCode.mdx b/docs/base-account/reference/core/provider-rpc-methods/eth_getCode.mdx index 21a509c83..ac8d015c2 100644 --- a/docs/base-account/reference/core/provider-rpc-methods/eth_getCode.mdx +++ b/docs/base-account/reference/core/provider-rpc-methods/eth_getCode.mdx @@ -79,7 +79,3 @@ The bytecode at the given address as a hexadecimal string. Returns "0x" for exte If the result is "0x", the address is an externally owned account (EOA). If it contains bytecode, it's a smart contract. - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/base-account/reference/core/provider-rpc-methods/eth_getLogs.mdx b/docs/base-account/reference/core/provider-rpc-methods/eth_getLogs.mdx index 8bb72ef99..14854dc05 100644 --- a/docs/base-account/reference/core/provider-rpc-methods/eth_getLogs.mdx +++ b/docs/base-account/reference/core/provider-rpc-methods/eth_getLogs.mdx @@ -150,7 +150,3 @@ Large filter ranges may exceed provider limits. Consider using smaller block ran Topics are order-dependent. Use null as a wildcard for any topic position. - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/base-account/reference/core/provider-rpc-methods/eth_getProof.mdx b/docs/base-account/reference/core/provider-rpc-methods/eth_getProof.mdx index d1a77fe58..6397f0d89 100644 --- a/docs/base-account/reference/core/provider-rpc-methods/eth_getProof.mdx +++ b/docs/base-account/reference/core/provider-rpc-methods/eth_getProof.mdx @@ -127,7 +127,3 @@ Array of rlp-serialized MerkleTree-Nodes. This method is useful for verifying account states and storage values using Merkle proofs without trusting the provider. - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/base-account/reference/core/provider-rpc-methods/eth_getStorageAt.mdx b/docs/base-account/reference/core/provider-rpc-methods/eth_getStorageAt.mdx index f965a0fdb..430946836 100644 --- a/docs/base-account/reference/core/provider-rpc-methods/eth_getStorageAt.mdx +++ b/docs/base-account/reference/core/provider-rpc-methods/eth_getStorageAt.mdx @@ -67,7 +67,3 @@ Storage positions start at 0x0. The result is a 32-byte hexadecimal value repres ``` - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/base-account/reference/core/provider-rpc-methods/eth_getTransactionByBlockHashAndIndex.mdx b/docs/base-account/reference/core/provider-rpc-methods/eth_getTransactionByBlockHashAndIndex.mdx index ee9170d07..682f8cfec 100644 --- a/docs/base-account/reference/core/provider-rpc-methods/eth_getTransactionByBlockHashAndIndex.mdx +++ b/docs/base-account/reference/core/provider-rpc-methods/eth_getTransactionByBlockHashAndIndex.mdx @@ -144,7 +144,3 @@ Transaction indices start at 0x0 for the first transaction in a block. If the in ``` ``` - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/base-account/reference/core/provider-rpc-methods/eth_getTransactionByBlockNumberAndIndex.mdx b/docs/base-account/reference/core/provider-rpc-methods/eth_getTransactionByBlockNumberAndIndex.mdx index 775bd682e..e871b7823 100644 --- a/docs/base-account/reference/core/provider-rpc-methods/eth_getTransactionByBlockNumberAndIndex.mdx +++ b/docs/base-account/reference/core/provider-rpc-methods/eth_getTransactionByBlockNumberAndIndex.mdx @@ -156,7 +156,3 @@ Transaction indices start at 0x0 for the first transaction in a block. If the in ``` ``` - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/base-account/reference/core/provider-rpc-methods/eth_getTransactionByHash.mdx b/docs/base-account/reference/core/provider-rpc-methods/eth_getTransactionByHash.mdx index ac50e4a1c..ba8fda1d2 100644 --- a/docs/base-account/reference/core/provider-rpc-methods/eth_getTransactionByHash.mdx +++ b/docs/base-account/reference/core/provider-rpc-methods/eth_getTransactionByHash.mdx @@ -135,7 +135,3 @@ ECDSA signature s. If the transaction is pending, `blockHash`, `blockNumber`, and `transactionIndex` will be null. - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/base-account/reference/core/provider-rpc-methods/eth_getTransactionCount.mdx b/docs/base-account/reference/core/provider-rpc-methods/eth_getTransactionCount.mdx index 7662da1d7..dac04cd0a 100644 --- a/docs/base-account/reference/core/provider-rpc-methods/eth_getTransactionCount.mdx +++ b/docs/base-account/reference/core/provider-rpc-methods/eth_getTransactionCount.mdx @@ -59,7 +59,3 @@ A hexadecimal string representing the integer of the number of transactions sent This value is used as the nonce for subsequent transactions from the address. - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/base-account/reference/core/provider-rpc-methods/eth_getTransactionReceipt.mdx b/docs/base-account/reference/core/provider-rpc-methods/eth_getTransactionReceipt.mdx index ce6651751..49f6bbb92 100644 --- a/docs/base-account/reference/core/provider-rpc-methods/eth_getTransactionReceipt.mdx +++ b/docs/base-account/reference/core/provider-rpc-methods/eth_getTransactionReceipt.mdx @@ -125,7 +125,3 @@ Either 1 (success) or 0 (failure). Transaction receipts are only available for mined transactions. Pending transactions will return null. - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/base-account/reference/core/provider-rpc-methods/eth_getUncleCountByBlockHash.mdx b/docs/base-account/reference/core/provider-rpc-methods/eth_getUncleCountByBlockHash.mdx index 0660968bd..4b29e3401 100644 --- a/docs/base-account/reference/core/provider-rpc-methods/eth_getUncleCountByBlockHash.mdx +++ b/docs/base-account/reference/core/provider-rpc-methods/eth_getUncleCountByBlockHash.mdx @@ -65,6 +65,3 @@ Uncle blocks are blocks that were mined but not included in the main blockchain. ``` -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - diff --git a/docs/base-account/reference/core/provider-rpc-methods/eth_getUncleCountByBlockNumber.mdx b/docs/base-account/reference/core/provider-rpc-methods/eth_getUncleCountByBlockNumber.mdx index b31dc3bf1..bd61b0bf9 100644 --- a/docs/base-account/reference/core/provider-rpc-methods/eth_getUncleCountByBlockNumber.mdx +++ b/docs/base-account/reference/core/provider-rpc-methods/eth_getUncleCountByBlockNumber.mdx @@ -75,7 +75,3 @@ Uncle blocks are blocks that were mined but not included in the main blockchain. ``` - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/base-account/reference/core/provider-rpc-methods/eth_requestAccounts.mdx b/docs/base-account/reference/core/provider-rpc-methods/eth_requestAccounts.mdx index c59bec5d1..d9c181a14 100644 --- a/docs/base-account/reference/core/provider-rpc-methods/eth_requestAccounts.mdx +++ b/docs/base-account/reference/core/provider-rpc-methods/eth_requestAccounts.mdx @@ -51,7 +51,3 @@ An array of Ethereum addresses (hexadecimal strings) that the user has authorize Always handle the case where the user rejects the connection request (error code 4001) gracefully in your application. - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/base-account/reference/core/provider-rpc-methods/eth_sendRawTransaction.mdx b/docs/base-account/reference/core/provider-rpc-methods/eth_sendRawTransaction.mdx index 7d1146e5c..4ecf6c341 100644 --- a/docs/base-account/reference/core/provider-rpc-methods/eth_sendRawTransaction.mdx +++ b/docs/base-account/reference/core/provider-rpc-methods/eth_sendRawTransaction.mdx @@ -54,7 +54,3 @@ The transaction hash (32 bytes) as a hexadecimal string, or the zero hash if the Ensure the transaction is properly signed before submitting. Invalid signatures will result in transaction failure. - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/base-account/reference/core/provider-rpc-methods/eth_sendTransaction.mdx b/docs/base-account/reference/core/provider-rpc-methods/eth_sendTransaction.mdx index 04a175e23..83cc3d02f 100644 --- a/docs/base-account/reference/core/provider-rpc-methods/eth_sendTransaction.mdx +++ b/docs/base-account/reference/core/provider-rpc-methods/eth_sendTransaction.mdx @@ -92,7 +92,3 @@ The transaction hash (32 bytes) as a hexadecimal string. Make sure to validate transaction parameters before sending, especially the `to` address and `value` to prevent loss of funds. - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/base-account/reference/core/provider-rpc-methods/eth_signTypedData_v4.mdx b/docs/base-account/reference/core/provider-rpc-methods/eth_signTypedData_v4.mdx index ec90e7b84..8afb55c59 100644 --- a/docs/base-account/reference/core/provider-rpc-methods/eth_signTypedData_v4.mdx +++ b/docs/base-account/reference/core/provider-rpc-methods/eth_signTypedData_v4.mdx @@ -128,7 +128,3 @@ Always validate the domain and verifying contract address to prevent signature r EIP-712 signatures provide better security than personal_sign by giving users context about what they're signing. - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/base-account/reference/core/provider-rpc-methods/personal_sign.mdx b/docs/base-account/reference/core/provider-rpc-methods/personal_sign.mdx index 1b176eab0..16a0e9ad0 100644 --- a/docs/base-account/reference/core/provider-rpc-methods/personal_sign.mdx +++ b/docs/base-account/reference/core/provider-rpc-methods/personal_sign.mdx @@ -71,7 +71,3 @@ Human-readable error message describing what went wrong. Always verify signatures on the server side before trusting them. Client-side signature verification should only be used for UX purposes. - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/base-account/reference/core/provider-rpc-methods/request-overview.mdx b/docs/base-account/reference/core/provider-rpc-methods/request-overview.mdx index e876656a3..2a515c8cb 100644 --- a/docs/base-account/reference/core/provider-rpc-methods/request-overview.mdx +++ b/docs/base-account/reference/core/provider-rpc-methods/request-overview.mdx @@ -2,7 +2,7 @@ title: "Overview" --- -The `request` method allows apps to make make Ethereum RPC requests to the wallet. +The `request` method allows apps to make Ethereum RPC requests to the wallet. ## Specification @@ -13,18 +13,37 @@ interface RequestArguments { } interface ProviderRpcError extends Error { + message: string; code: number; data?: unknown; } -interface CoinbaseWalletProvider { +interface ProviderInterface { /** * @param {RequestArguments} args request arguments. * @returns A promise that resolves with the result. - * @throws {ProviderRpcError} incase of error. - * @fires CoinbaseWalletProvider#connect When the provider successfully connects. + * @throws {ProviderRpcError} in case of error. */ - request: (args: RequestArguments) => Promise + request(args: RequestArguments): Promise; + disconnect(): Promise; + emit(event: K, ...args: [ProviderEventMap[K]]): boolean; + on(event: K, listener: (_: ProviderEventMap[K]) => void): this; +} + +type CreateProviderOptions = Partial & { + preference?: Preference; + subAccounts?: Omit; + paymasterUrls?: Record; +}; + +interface BaseAccountSDK { + getProvider(): ProviderInterface; + subAccount: { + create(account: AddSubAccountAccount): Promise; + get(): Promise; + addOwner(params: { address?: `0x${string}`; publicKey?: `0x${string}`; chainId: number }): Promise; + setToOwnerAccount(toSubAccountOwner: ToOwnerAccountFn): void; + }; } ``` @@ -43,16 +62,16 @@ const txHash = await provider.request({ ``` ```ts setup.ts filename="setup.ts" -import { CoinbaseWalletSDK } from '@coinbase/wallet-sdk' +import { createBaseAccountSDK } from '@base-org/account' const baseSepoliaChainId = 84532; -export const sdk = new CoinbaseWalletSDK({ +export const sdk = createBaseAccountSDK({ appName: 'My App Name', appChainIds: [baseSepoliaChainId] }); -const provider = sdk.makeWeb3Provider(); +const provider = sdk.getProvider(); ``` @@ -68,32 +87,57 @@ Requests are handled in one of three ways The following RPC requests are sent to the Wallet application: -- 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 +- [`personal_sign`](/base-account/reference/core/provider-rpc-methods/personal_sign) +- [`eth_sendTransaction`](/base-account/reference/core/provider-rpc-methods/eth_sendTransaction) +- [`eth_sendRawTransaction`](/base-account/reference/core/provider-rpc-methods/eth_sendRawTransaction) +- [`eth_signTypedData_v4`](/base-account/reference/core/provider-rpc-methods/eth_signTypedData_v4) +- [`wallet_addEthereumChain`](/base-account/reference/core/provider-rpc-methods/wallet_addEthereumChain) +- [`wallet_watchAsset`](/base-account/reference/core/provider-rpc-methods/wallet_watchAsset) +- [`wallet_sendCalls`](/base-account/reference/core/provider-rpc-methods/wallet_sendCalls) +- [`wallet_getCallsStatus`](/base-account/reference/core/provider-rpc-methods/wallet_getCallsStatus) +- [`wallet_connect`](/base-account/reference/core/provider-rpc-methods/wallet_connect) +- [`wallet_getCapabilities`](/base-account/reference/core/provider-rpc-methods/wallet_getCapabilities) +- [`wallet_switchEthereumChain`](/base-account/reference/core/provider-rpc-methods/wallet_switchEthereumChain) +- [`wallet_addSubAccount`](/base-account/reference/core/provider-rpc-methods/wallet_addSubAccount) +- [`wallet_getSubAccounts`](/base-account/reference/core/provider-rpc-methods/wallet_getSubAccounts) +- [`coinbase_fetchPermissions`](/base-account/reference/core/provider-rpc-methods/coinbase_fetchPermissions) ### 2. Handled Locally by the SDK -The following requests are handled locally by the SDK, with no external calls. - -- eth_requestAccounts -- eth_accounts -- eth_coinbase -- net_version -- eth_chainId -- wallet_getCapabilities -- wallet_switchEthereumChain +The following requests are handled locally by the SDK, with no external calls: + +- [`eth_requestAccounts`](/base-account/reference/core/provider-rpc-methods/eth_requestAccounts) +- [`eth_accounts`](/base-account/reference/core/provider-rpc-methods/eth_accounts) +- [`eth_coinbase`](/base-account/reference/core/provider-rpc-methods/eth_coinbase) +- [`eth_chainId`](/base-account/reference/core/provider-rpc-methods/eth_chainId) +- [`web3_clientVersion`](/base-account/reference/core/provider-rpc-methods/web3_clientVersion) + +### 3. Passed to RPC Provider + +Standard Ethereum RPC methods are passed to the configured RPC provider for the current chain, including: + +- [`eth_getBalance`](/base-account/reference/core/provider-rpc-methods/eth_getBalance) +- [`eth_blockNumber`](/base-account/reference/core/provider-rpc-methods/eth_blockNumber) +- [`eth_gasPrice`](/base-account/reference/core/provider-rpc-methods/eth_gasPrice) +- [`eth_estimateGas`](/base-account/reference/core/provider-rpc-methods/eth_estimateGas) +- [`eth_feeHistory`](/base-account/reference/core/provider-rpc-methods/eth_feeHistory) +- [`eth_getBlockByNumber`](/base-account/reference/core/provider-rpc-methods/eth_getBlockByNumber) +- [`eth_getBlockByHash`](/base-account/reference/core/provider-rpc-methods/eth_getBlockByHash) +- [`eth_getTransactionByHash`](/base-account/reference/core/provider-rpc-methods/eth_getTransactionByHash) +- [`eth_getTransactionReceipt`](/base-account/reference/core/provider-rpc-methods/eth_getTransactionReceipt) +- [`eth_getTransactionCount`](/base-account/reference/core/provider-rpc-methods/eth_getTransactionCount) +- [`eth_getTransactionByBlockHashAndIndex`](/base-account/reference/core/provider-rpc-methods/eth_getTransactionByBlockHashAndIndex) +- [`eth_getTransactionByBlockNumberAndIndex`](/base-account/reference/core/provider-rpc-methods/eth_getTransactionByBlockNumberAndIndex) +- [`eth_getBlockTransactionCountByHash`](/base-account/reference/core/provider-rpc-methods/eth_getBlockTransactionCountByHash) +- [`eth_getBlockTransactionCountByNumber`](/base-account/reference/core/provider-rpc-methods/eth_getBlockTransactionCountByNumber) +- [`eth_getCode`](/base-account/reference/core/provider-rpc-methods/eth_getCode) +- [`eth_getStorageAt`](/base-account/reference/core/provider-rpc-methods/eth_getStorageAt) +- [`eth_getLogs`](/base-account/reference/core/provider-rpc-methods/eth_getLogs) +- [`eth_getProof`](/base-account/reference/core/provider-rpc-methods/eth_getProof) +- [`eth_getUncleCountByBlockHash`](/base-account/reference/core/provider-rpc-methods/eth_getUncleCountByBlockHash) +- [`eth_getUncleCountByBlockNumber`](/base-account/reference/core/provider-rpc-methods/eth_getUncleCountByBlockNumber) +- [`eth_sendRawTransaction`](/base-account/reference/core/provider-rpc-methods/eth_sendRawTransaction) import PolicyBanner from "/snippets/PolicyBanner.mdx"; - \ No newline at end of file + diff --git a/docs/base-account/reference/core/provider-rpc-methods/sdk-overview.mdx b/docs/base-account/reference/core/provider-rpc-methods/sdk-overview.mdx index 1740302c8..6b36855b2 100644 --- a/docs/base-account/reference/core/provider-rpc-methods/sdk-overview.mdx +++ b/docs/base-account/reference/core/provider-rpc-methods/sdk-overview.mdx @@ -9,7 +9,3 @@ These requests will be handled in one of three ways 1. Sent to the Wallet (Wallet mobile app, extension, or popup window). 2. Handled locally 3. Passed onto default RPC provider for the given chain, if it exists. - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/base-account/reference/core/provider-rpc-methods/standard-rpc-methods.mdx b/docs/base-account/reference/core/provider-rpc-methods/standard-rpc-methods.mdx index 866000671..4c018fe58 100644 --- a/docs/base-account/reference/core/provider-rpc-methods/standard-rpc-methods.mdx +++ b/docs/base-account/reference/core/provider-rpc-methods/standard-rpc-methods.mdx @@ -1,427 +1,158 @@ --- -title: "Other RPC Methods" -description: "Other commonly used Ethereum RPC methods are supported by Base Account" +title: "Standard RPC Methods" +description: "Standard Ethereum RPC methods supported by Base Account" --- -Base Account supports all standard Ethereum RPC methods, ensuring compatibility with existing web3 applications and libraries. This page provides a comprehensive reference for commonly used methods. +Base Account supports all standard Ethereum RPC methods, ensuring compatibility with existing web3 applications and libraries. Each method below links to detailed documentation including parameters, returns, and error handling. ## Account Methods -### eth_accounts +### [eth_accounts](/base-account/reference/core/provider-rpc-methods/eth_accounts) Returns a list of addresses owned by the client. -```javascript -const accounts = await provider.request({ method: 'eth_accounts' }); -// Returns: ['0x1234...', '0x5678...'] -``` - -### eth_requestAccounts +### [eth_requestAccounts](/base-account/reference/core/provider-rpc-methods/eth_requestAccounts) Requests that the user provide an Ethereum address to be identified by. This method is used to initiate a connection between your application and the user's wallet. -```javascript -const accounts = await provider.request({ method: 'eth_requestAccounts' }); -// Returns: ['0x1234...'] -``` - ## Chain Information -### eth_chainId +### [eth_chainId](/base-account/reference/core/provider-rpc-methods/eth_chainId) Returns the chain ID of the current network. -```javascript -const chainId = await provider.request({ method: 'eth_chainId' }); -// Returns: '0x2105' (8453 for Base mainnet) -``` - -### eth_blockNumber +### [eth_blockNumber](/base-account/reference/core/provider-rpc-methods/eth_blockNumber) Returns the number of the most recent block. -```javascript -const blockNumber = await provider.request({ method: 'eth_blockNumber' }); -// Returns: '0x1b4' (hex encoded block number) -``` - -### eth_coinbase +### [eth_coinbase](/base-account/reference/core/provider-rpc-methods/eth_coinbase) Returns the client coinbase address. -```javascript -const coinbase = await provider.request({ method: 'eth_coinbase' }); -// Returns: '0x1234...' -``` - ## Balance and Transaction Data -### eth_getBalance +### [eth_getBalance](/base-account/reference/core/provider-rpc-methods/eth_getBalance) Returns the balance of the account of given address. -```javascript -const balance = await provider.request({ - method: 'eth_getBalance', - params: ['0x1234...', 'latest'] -}); -// Returns: '0x1b1ae4d6e2ef500000' (hex encoded balance in wei) -``` - -### eth_getTransactionCount +### [eth_getTransactionCount](/base-account/reference/core/provider-rpc-methods/eth_getTransactionCount) Returns the number of transactions sent from an address. -```javascript -const nonce = await provider.request({ - method: 'eth_getTransactionCount', - params: ['0x1234...', 'latest'] -}); -// Returns: '0x1' (hex encoded transaction count) -``` - -### eth_getTransactionByHash +### [eth_getTransactionByHash](/base-account/reference/core/provider-rpc-methods/eth_getTransactionByHash) Returns information about a transaction by transaction hash. -```javascript -const transaction = await provider.request({ - method: 'eth_getTransactionByHash', - params: ['0xabcd...'] -}); -// Returns transaction object -``` - -### eth_getTransactionReceipt +### [eth_getTransactionReceipt](/base-account/reference/core/provider-rpc-methods/eth_getTransactionReceipt) Returns the receipt of a transaction by transaction hash. -```javascript -const receipt = await provider.request({ - method: 'eth_getTransactionReceipt', - params: ['0xabcd...'] -}); -// Returns transaction receipt object -``` - ## Block Information -### eth_getBlockByNumber +### [eth_getBlockByNumber](/base-account/reference/core/provider-rpc-methods/eth_getBlockByNumber) Returns information about a block by block number. -```javascript -const block = await provider.request({ - method: 'eth_getBlockByNumber', - params: ['latest', false] -}); -// Returns block object -``` - -### eth_getBlockByHash +### [eth_getBlockByHash](/base-account/reference/core/provider-rpc-methods/eth_getBlockByHash) Returns information about a block by block hash. -```javascript -const block = await provider.request({ - method: 'eth_getBlockByHash', - params: ['0x1234...', false] -}); -// Returns block object -``` - -### eth_getBlockTransactionCountByNumber +### [eth_getBlockTransactionCountByNumber](/base-account/reference/core/provider-rpc-methods/eth_getBlockTransactionCountByNumber) Returns the number of transactions in a block by block number. -```javascript -const count = await provider.request({ - method: 'eth_getBlockTransactionCountByNumber', - params: ['latest'] -}); -// Returns: '0x10' (hex encoded transaction count) -``` - -### eth_getBlockTransactionCountByHash +### [eth_getBlockTransactionCountByHash](/base-account/reference/core/provider-rpc-methods/eth_getBlockTransactionCountByHash) Returns the number of transactions in a block by block hash. -```javascript -const count = await provider.request({ - method: 'eth_getBlockTransactionCountByHash', - params: ['0x1234...'] -}); -// Returns: '0x10' (hex encoded transaction count) -``` - ## Transaction Methods -### eth_sendTransaction +### [eth_sendTransaction](/base-account/reference/core/provider-rpc-methods/eth_sendTransaction) Creates new message call transaction or a contract creation for signed transactions. -```javascript -const txHash = await provider.request({ - method: 'eth_sendTransaction', - params: [{ - from: '0x1234...', - to: '0x5678...', - value: '0x1b1ae4d6e2ef500000', // 0.5 ETH in wei - gas: '0x5208', // 21000 gas - gasPrice: '0x9184e72a000' // 10 gwei - }] -}); -// Returns: '0xabcd...' (transaction hash) -``` - -### eth_sendRawTransaction +### [eth_sendRawTransaction](/base-account/reference/core/provider-rpc-methods/eth_sendRawTransaction) Creates new message call transaction or a contract creation for signed transactions. -```javascript -const txHash = await provider.request({ - method: 'eth_sendRawTransaction', - params: ['0xf86c...'] // signed transaction data -}); -// Returns: '0xabcd...' (transaction hash) -``` - ## Gas and Fee Methods -### eth_estimateGas +### [eth_estimateGas](/base-account/reference/core/provider-rpc-methods/eth_estimateGas) Generates and returns an estimate of how much gas is necessary to allow the transaction to complete. -```javascript -const gasEstimate = await provider.request({ - method: 'eth_estimateGas', - params: [{ - from: '0x1234...', - to: '0x5678...', - value: '0x1b1ae4d6e2ef500000' - }] -}); -// Returns: '0x5208' (hex encoded gas estimate) -``` - -### eth_gasPrice +### [eth_gasPrice](/base-account/reference/core/provider-rpc-methods/eth_gasPrice) Returns the current price per gas in wei. -```javascript -const gasPrice = await provider.request({ method: 'eth_gasPrice' }); -// Returns: '0x9184e72a000' (hex encoded gas price in wei) -``` - -### eth_feeHistory +### [eth_feeHistory](/base-account/reference/core/provider-rpc-methods/eth_feeHistory) Returns base fee per gas and transaction effective priority fee per gas history for the requested/supported block range. -```javascript -const feeHistory = await provider.request({ - method: 'eth_feeHistory', - params: [ - '0x4', // block count - 'latest', // newest block - [25, 75] // reward percentiles - ] -}); -// Returns fee history object -``` - ## Contract and Storage Methods -### eth_getCode +### [eth_getCode](/base-account/reference/core/provider-rpc-methods/eth_getCode) Returns code at a given address. -```javascript -const code = await provider.request({ - method: 'eth_getCode', - params: ['0x1234...', 'latest'] -}); -// Returns: '0x608060405234801561001057600080fd5b50...' (contract bytecode) -``` - -### eth_getStorageAt +### [eth_getStorageAt](/base-account/reference/core/provider-rpc-methods/eth_getStorageAt) Returns the value from a storage position at a given address. -```javascript -const storageValue = await provider.request({ - method: 'eth_getStorageAt', - params: ['0x1234...', '0x0', 'latest'] -}); -// Returns: '0x0000000000000000000000000000000000000000000000000000000000000001' -``` - -### eth_getLogs +### [eth_getLogs](/base-account/reference/core/provider-rpc-methods/eth_getLogs) Returns an array of all logs matching a given filter object. -```javascript -const logs = await provider.request({ - method: 'eth_getLogs', - params: [{ - fromBlock: '0x1', - toBlock: 'latest', - address: '0x1234...', - topics: ['0xabcd...'] - }] -}); -// Returns array of log objects -``` - -### eth_getProof +### [eth_getProof](/base-account/reference/core/provider-rpc-methods/eth_getProof) Returns the account and storage values of the specified account including the Merkle-proof. -```javascript -const proof = await provider.request({ - method: 'eth_getProof', - params: ['0x1234...', ['0x0'], 'latest'] -}); -// Returns proof object -``` - ## Signing Methods -### personal_sign +### [personal_sign](/base-account/reference/core/provider-rpc-methods/personal_sign) Signs a message with the private key of the given account. -```javascript -const signature = await provider.request({ - method: 'personal_sign', - params: ['0x48656c6c6f20576f726c64', '0x1234...'] // "Hello World" in hex -}); -// Returns: '0x1234...' (signature) -``` - -### eth_signTypedData_v4 +### [eth_signTypedData_v4](/base-account/reference/core/provider-rpc-methods/eth_signTypedData_v4) Signs typed data according to EIP-712. -```javascript -const signature = await provider.request({ - method: 'eth_signTypedData_v4', - params: ['0x1234...', typedData] -}); -// Returns: '0x1234...' (signature) -``` - ## Network Methods -### wallet_addEthereumChain +### [wallet_addEthereumChain](/base-account/reference/core/provider-rpc-methods/wallet_addEthereumChain) Adds an Ethereum chain to the wallet. -```javascript -await provider.request({ - method: 'wallet_addEthereumChain', - params: [{ - chainId: '0x2105', - chainName: 'Base', - nativeCurrency: { - name: 'Ether', - symbol: 'ETH', - decimals: 18 - }, - rpcUrls: ['https://mainnet.base.org'], - blockExplorerUrls: ['https://basescan.org'] - }] -}); -``` - -### wallet_switchEthereumChain +### [wallet_switchEthereumChain](/base-account/reference/core/provider-rpc-methods/wallet_switchEthereumChain) Switches the wallet to the specified Ethereum chain. -```javascript -await provider.request({ - method: 'wallet_switchEthereumChain', - params: [{ chainId: '0x2105' }] // Base mainnet -}); -``` - -### wallet_watchAsset +### [wallet_watchAsset](/base-account/reference/core/provider-rpc-methods/wallet_watchAsset) Requests that the user track the token in their wallet. -```javascript -await provider.request({ - method: 'wallet_watchAsset', - params: { - type: 'ERC20', - options: { - address: '0x1234...', - symbol: 'TOKEN', - decimals: 18, - image: 'https://example.com/token.png' - } - } -}); -``` - ## Advanced Methods -### eth_getTransactionByBlockHashAndIndex +### [eth_getTransactionByBlockHashAndIndex](/base-account/reference/core/provider-rpc-methods/eth_getTransactionByBlockHashAndIndex) Returns information about a transaction by block hash and transaction index position. -```javascript -const transaction = await provider.request({ - method: 'eth_getTransactionByBlockHashAndIndex', - params: ['0x1234...', '0x0'] -}); -// Returns transaction object -``` - -### eth_getTransactionByBlockNumberAndIndex +### [eth_getTransactionByBlockNumberAndIndex](/base-account/reference/core/provider-rpc-methods/eth_getTransactionByBlockNumberAndIndex) Returns information about a transaction by block number and transaction index position. -```javascript -const transaction = await provider.request({ - method: 'eth_getTransactionByBlockNumberAndIndex', - params: ['latest', '0x0'] -}); -// Returns transaction object -``` - -### eth_getUncleCountByBlockHash +### [eth_getUncleCountByBlockHash](/base-account/reference/core/provider-rpc-methods/eth_getUncleCountByBlockHash) Returns the number of uncles in a block by block hash. -```javascript -const uncleCount = await provider.request({ - method: 'eth_getUncleCountByBlockHash', - params: ['0x1234...'] -}); -// Returns: '0x0' (hex encoded uncle count) -``` - -### eth_getUncleCountByBlockNumber +### [eth_getUncleCountByBlockNumber](/base-account/reference/core/provider-rpc-methods/eth_getUncleCountByBlockNumber) Returns the number of uncles in a block by block number. -```javascript -const uncleCount = await provider.request({ - method: 'eth_getUncleCountByBlockNumber', - params: ['latest'] -}); -// Returns: '0x0' (hex encoded uncle count) -``` - -### web3_clientVersion +### [web3_clientVersion](/base-account/reference/core/provider-rpc-methods/web3_clientVersion) Returns the current client version. -```javascript -const version = await provider.request({ method: 'web3_clientVersion' }); -// Returns: 'base-account/1.0.0' -``` - ## Error Handling All RPC methods can throw errors. Common error codes include: diff --git a/docs/base-account/reference/core/provider-rpc-methods/wallet_addEthereumChain.mdx b/docs/base-account/reference/core/provider-rpc-methods/wallet_addEthereumChain.mdx index 1153e5d51..2273c25c7 100644 --- a/docs/base-account/reference/core/provider-rpc-methods/wallet_addEthereumChain.mdx +++ b/docs/base-account/reference/core/provider-rpc-methods/wallet_addEthereumChain.mdx @@ -103,7 +103,3 @@ Returns null if successful. Make sure to provide accurate RPC URLs and block explorer URLs. Incorrect URLs may cause connectivity issues. - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/base-account/reference/core/provider-rpc-methods/wallet_addSubAccount.mdx b/docs/base-account/reference/core/provider-rpc-methods/wallet_addSubAccount.mdx index 98f4b78ff..0d208e96f 100644 --- a/docs/base-account/reference/core/provider-rpc-methods/wallet_addSubAccount.mdx +++ b/docs/base-account/reference/core/provider-rpc-methods/wallet_addSubAccount.mdx @@ -32,6 +32,14 @@ Hex string of the public key. + + +The address of the deployed account to add as a sub account. (required for "deployed" type). + + + +The chain ID that the account is deployed on. (required for "deployed" type). + @@ -56,7 +64,7 @@ Factory deployment data (optional). -```json Request +```json Create Request { "id": 1, "jsonrpc": "2.0", @@ -72,6 +80,21 @@ Factory deployment data (optional). }] } ``` + +```json Deployed Request +{ + "id": 1, + "jsonrpc": "2.0", + "method": "wallet_addSubAccount", + "params": [{ + "account": { + "type": "deployed", + "address": "0x1234567890123456789012345678901234567890", + "chainId": 8453 + } + }] +} +``` @@ -98,7 +121,3 @@ Factory deployment data (optional). This is an experimental feature and the API may change in future versions. - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/base-account/reference/core/provider-rpc-methods/wallet_connect.mdx b/docs/base-account/reference/core/provider-rpc-methods/wallet_connect.mdx index 67df77058..cbdcd9b94 100644 --- a/docs/base-account/reference/core/provider-rpc-methods/wallet_connect.mdx +++ b/docs/base-account/reference/core/provider-rpc-methods/wallet_connect.mdx @@ -22,17 +22,63 @@ The wallet connect version to use. The JSON-RPC version (typically "2.0"). + + +Optional capabilities to request during connection, such as signInWithEthereum for authentication. + + + +Request SIWE (Sign-In With Ethereum) authentication during connection. + + + +A unique random string to prevent replay attacks. + + + +The chain ID as a hexadecimal string (e.g., "0x2105" for Base Mainnet). + + + + + ## Returns -Connection result object containing account information. +Connection result object containing account information and capabilities results. -Array of connected account addresses. +Array of connected account objects. + + + +The account address. + + + +Capabilities results if requested during connection. + + + +SIWE authentication result if requested. + + + +The SIWE-formatted message that was signed. + + + +The cryptographic signature of the message. + + + + + + @@ -66,16 +112,55 @@ Whether the wallet is connected. }] } ``` + +```json With signInWithEthereum Capability +{ + "id": 1, + "jsonrpc": "2.0", + "method": "wallet_connect", + "params": [{ + "version": "1", + "capabilities": { + "signInWithEthereum": { + "nonce": "abc123def456", + "chainId": "0x2105" + } + } + }] +} +``` -```json Success Response +```json Basic Connection Response +{ + "id": 1, + "jsonrpc": "2.0", + "result": { + "accounts": [{ + "address": "0x407d73d8a49eeb85d32cf465507dd71d507100c1" + }], + "chainId": "0x2105", + "isConnected": true + } +} +``` + +```json signInWithEthereum Response { "id": 1, "jsonrpc": "2.0", "result": { - "accounts": ["0x407d73d8a49eeb85d32cf465507dd71d507100c1"], - "chainId": "0x1", + "accounts": [{ + "address": "0x407d73d8a49eeb85d32cf465507dd71d507100c1", + "capabilities": { + "signInWithEthereum": { + "message": "localhost:3000 wants you to sign in with your Ethereum account:\n0x407d73d8a49eeb85d32cf465507dd71d507100c1\n\nSign in with Ethereum to the app.\n\nURI: http://localhost:3000\nVersion: 1\nChain ID: 8453\nNonce: abc123def456\nIssued At: 2024-01-15T10:30:00Z", + "signature": "0x1234567890abcdef..." + } + } + }], + "chainId": "0x2105", "isConnected": true } } @@ -89,6 +174,7 @@ Whether the wallet is connected. | 4001 | User rejected the request | User denied the connection request | | 4100 | Requested method not supported | The method is not supported by the wallet | | 4200 | Wallet not available | The wallet is not installed or available | +| -32602 | Invalid params | Invalid nonce or chainId in signInWithEthereum capability | This is a Coinbase Wallet-specific method and may not be available in other wallets. @@ -98,6 +184,14 @@ This is a Coinbase Wallet-specific method and may not be available in other wall After successful connection, the wallet will emit connection events and provide access to account information. + +When using the `signInWithEthereum` capability, always generate a fresh, unique nonce for each authentication attempt to prevent replay attacks. The signature can be verified on your backend using libraries like viem. + + +## Usage with Capabilities + +You can use the `wallet_connect` with the [`signInWithEthereum`](/base-account/reference/core/capabilities/signInWithEthereum.mdx) capability to authenticate the user. + import PolicyBanner from "/snippets/PolicyBanner.mdx"; - \ No newline at end of file + diff --git a/docs/base-account/reference/core/provider-rpc-methods/wallet_getCallsStatus.mdx b/docs/base-account/reference/core/provider-rpc-methods/wallet_getCallsStatus.mdx new file mode 100644 index 000000000..40576b362 --- /dev/null +++ b/docs/base-account/reference/core/provider-rpc-methods/wallet_getCallsStatus.mdx @@ -0,0 +1,257 @@ +--- +title: "wallet_getCallsStatus" +description: "Get the status of a call batch sent via wallet_sendCalls" +--- + +Defined in [EIP-5792](https://eips.ethereum.org/EIPS/eip-5792) + + +Returns the status of a call batch that was sent via `wallet_sendCalls`. This method allows applications to track the execution status and retrieve transaction receipts for batch operations. + + +## Parameters + + +The call bundle identifier returned by a previous `wallet_sendCalls` request. + + +## Returns + + +Status information for the call batch. + + + +The version of the API being used. Currently "1.0". + + + +The chain ID in hexadecimal format. + + + +The call bundle identifier. + + + +Status code indicating the current state of the batch: +- **1xx (Pending)**: 100 = Batch received but not completed onchain +- **2xx (Confirmed)**: 200 = Batch included onchain without reverts +- **4xx (Offchain failures)**: 400 = Batch failed and wallet will not retry +- **5xx (Chain failures)**: 500 = Batch reverted completely +- **6xx (Partial failures)**: 600 = Batch reverted partially + + + +Indicates whether the wallet executed calls atomically. If `true`, all calls were executed in a single transaction. If `false`, calls were executed in multiple transactions. + + + +Transaction receipts for the call batch. Structure depends on the `atomic` field: +- If `atomic` is `true`: Single receipt or array of receipts for the batch transaction +- If `atomic` is `false`: Array of receipts for all transactions containing batch calls + + + +The logs generated by the calls. For smart contract wallets, only includes logs relevant to the specific calls. + + + +Transaction status: `0x1` for success, `0x0` for failure. + + + +Hash of the block containing these calls. + + + +Block number containing these calls (hex format). + + + +The amount of gas used by these calls (hex format). + + + +Hash of the transaction containing these calls. + + + + + +Optional capability-specific metadata. + + + + +## Example Usage + + +```json Request +{ + "id": 1, + "jsonrpc": "2.0", + "method": "wallet_getCallsStatus", + "params": ["0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331"] +} +``` + +```typescript SDK Usage +import { createBaseAccountSDK } from '@base-org/account'; + +const provider = createBaseAccountSDK().getProvider(); + +// Get status of a batch sent via wallet_sendCalls +const callsId = "0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331"; + +const status = await provider.request({ + method: 'wallet_getCallsStatus', + params: [callsId] +}); + +console.log('Batch status:', status.status); +console.log('Atomic execution:', status.atomic); +console.log('Receipts:', status.receipts); +``` + + + +```json Successful Batch (Atomic) +{ + "id": 1, + "jsonrpc": "2.0", + "result": { + "version": "1.0", + "chainId": "0x2105", + "id": "0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331", + "status": 200, + "atomic": true, + "receipts": [ + { + "logs": [ + { + "address": "0xa922b54716264130634d6ff183747a8ead91a40b", + "topics": ["0x5a2a90727cc9d000dd060b1132a5c977c9702bb3a52afe360c9c22f0e9451a68"], + "data": "0xabcd" + } + ], + "status": "0x1", + "blockHash": "0xf19bbafd9fd0124ec110b848e8de4ab4f62bf60c189524e54213285e7f540d4a", + "blockNumber": "0xabcd", + "gasUsed": "0xdef", + "transactionHash": "0x9b7bb827c2e5e3c1a0a44dc53e573aa0b3af3bd1f9f5ed03071b100bb039eaff" + } + ] + } +} +``` + +```json Pending Batch +{ + "id": 1, + "jsonrpc": "2.0", + "result": { + "version": "1.0", + "chainId": "0x2105", + "id": "0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331", + "status": 100, + "atomic": true, + "receipts": [] + } +} +``` + +```json Failed Batch +{ + "id": 1, + "jsonrpc": "2.0", + "result": { + "version": "1.0", + "chainId": "0x2105", + "id": "0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331", + "status": 500, + "atomic": true, + "receipts": [ + { + "logs": [], + "status": "0x0", + "blockHash": "0xf19bbafd9fd0124ec110b848e8de4ab4f62bf60c189524e54213285e7f540d4a", + "blockNumber": "0xabcd", + "gasUsed": "0xabc", + "transactionHash": "0x9b7bb827c2e5e3c1a0a44dc53e573aa0b3af3bd1f9f5ed03071b100bb039eaff" + } + ] + } +} +``` + + +## Status Code Reference + +| Code | Category | Meaning | +|------|----------|---------| +| 100 | Pending | Batch received but not completed onchain | +| 200 | Success | Batch included onchain without reverts | +| 400 | Offchain Error | Batch failed, wallet will not retry | +| 500 | Chain Error | Batch reverted completely | +| 600 | Partial Error | Batch reverted partially, some changes may be onchain | + +## Error Handling + +| Code | Message | Description | +| ---- | ------- | ----------- | +| -32602 | Invalid params | Invalid call bundle identifier | +| 4100 | Method not supported | Wallet doesn't support wallet_getCallsStatus | +| 4200 | Calls not found | No batch found with the specified identifier | + +## Usage with wallet_sendCalls + +This method is designed to work with batches sent via `wallet_sendCalls`: + +```typescript +// Send a batch of calls +const callsId = await provider.request({ + method: 'wallet_sendCalls', + params: [{ + version: '1.0', + chainId: '0x2105', + from: userAddress, + calls: [ + { to: '0x...', value: '0x0', data: '0x...' }, + { to: '0x...', value: '0x0', data: '0x...' } + ] + }] +}); + +// Poll for status updates +const checkStatus = async () => { + const status = await provider.request({ + method: 'wallet_getCallsStatus', + params: [callsId] + }); + + if (status.status === 200) { + console.log('Batch completed successfully!'); + console.log('Transaction receipts:', status.receipts); + } else if (status.status === 100) { + console.log('Batch still pending...'); + setTimeout(checkStatus, 2000); // Check again in 2 seconds + } else { + console.error('Batch failed with status:', status.status); + } +}; + +checkStatus(); +``` + + +The receipts structure varies based on whether the batch was executed atomically. Always check the `atomic` field to properly interpret the receipts array. + + + +This method follows the EIP-5792 standard for wallet batch operations. Not all wallets may support this method - check wallet capabilities first. + + +import PolicyBanner from "/snippets/PolicyBanner.mdx"; + + diff --git a/docs/base-account/reference/core/provider-rpc-methods/wallet_getCapabilities.mdx b/docs/base-account/reference/core/provider-rpc-methods/wallet_getCapabilities.mdx new file mode 100644 index 000000000..5db8d3d62 --- /dev/null +++ b/docs/base-account/reference/core/provider-rpc-methods/wallet_getCapabilities.mdx @@ -0,0 +1,347 @@ +--- +title: "wallet_getCapabilities" +description: "Get the wallet's supported capabilities for the given account" +--- + +Defined in [EIP-5792](https://eips.ethereum.org/EIPS/eip-5792) + + +Returns the wallet's capabilities for a given account. Capabilities indicate what additional functionality the wallet supports beyond standard RPC methods, such as atomic batch transactions, gasless transactions, auxiliary funds, and authentication features. + + +## Parameters + + +The account address to check capabilities for. +Pattern: `^0x[0-9a-fA-F]{40}$` + + +## Returns + + +An object where each key is a chain ID (as a hexadecimal string) and each value is an object containing the capabilities supported on that chain. + + + +Capabilities object for a specific chain (e.g., "0x2105" for Base Mainnet). + + + +Indicates wallet access to funds beyond on-chain balance verification (MagicSpend). + + + +Whether auxiliary funds are available for this account on this chain. + + + + + +Indicates support for atomic batch transaction execution. + + + +Atomic execution support level: "supported", "ready", or "unsupported". + + + + + +Indicates support for gasless transactions via paymaster services. + + + +Whether paymaster services are supported for this account on this chain. + + + + + +Indicates support for flow control capabilities. + + + +Whether flow control is supported for this account on this chain. + + + + + +Indicates support for data callback capabilities. + + + +Whether data callbacks are supported for this account on this chain. + + + + + + + + +## Example Usage + + +```json Request +{ + "id": 1, + "jsonrpc": "2.0", + "method": "wallet_getCapabilities", + "params": ["0x407d73d8a49eeb85d32cf465507dd71d507100c1"] +} +``` + +```typescript SDK Usage +import { createBaseAccountSDK } from '@base-org/account'; + +const provider = createBaseAccountSDK().getProvider(); + +// Get capabilities for a user address +const capabilities = await provider.request({ + method: 'wallet_getCapabilities', + params: ['0x407d73d8a49eeb85d32cf465507dd71d507100c1'] +}); + +// Check specific capabilities +const baseCapabilities = capabilities["0x2105"]; // Base Mainnet +const hasAuxiliaryFunds = baseCapabilities?.auxiliaryFunds?.supported; +const supportsAtomic = baseCapabilities?.atomic?.supported === "supported"; +const hasPaymaster = baseCapabilities?.paymasterService?.supported; + +console.log('Capabilities:', { + hasAuxiliaryFunds, + supportsAtomic, + hasPaymaster +}); +``` + + + +```json Full Capabilities Response +{ + "id": 1, + "jsonrpc": "2.0", + "result": { + "0x2105": { + "auxiliaryFunds": { + "supported": true + }, + "atomic": { + "supported": "supported" + }, + "paymasterService": { + "supported": true + }, + "flowControl": { + "supported": false + }, + "datacallback": { + "supported": false + } + }, + "0x14A34": { + "auxiliaryFunds": { + "supported": false + }, + "atomic": { + "supported": "ready" + }, + "paymasterService": { + "supported": true + } + } + } +} +``` + +```json Limited Capabilities Response +{ + "id": 1, + "jsonrpc": "2.0", + "result": { + "0x2105": { + "auxiliaryFunds": { + "supported": false + }, + "atomic": { + "supported": "unsupported" + }, + "paymasterService": { + "supported": false + } + } + } +} +``` + + +## Capability Detection Patterns + +### Check Single Capability + +```typescript +async function checkAuxiliaryFunds(userAddress: string): Promise { + try { + const capabilities = await provider.request({ + method: 'wallet_getCapabilities', + params: [userAddress] + }); + + return capabilities["0x2105"]?.auxiliaryFunds?.supported || false; + } catch (error) { + console.error('Failed to check capabilities:', error); + return false; + } +} +``` + +### Check Multiple Capabilities + +```typescript +async function getWalletCapabilities(userAddress: string) { + const capabilities = await provider.request({ + method: 'wallet_getCapabilities', + params: [userAddress] + }); + + const baseCapabilities = capabilities["0x2105"] || {}; + + return { + hasAuxiliaryFunds: baseCapabilities.auxiliaryFunds?.supported || false, + hasAtomicBatch: baseCapabilities.atomic?.supported === "supported", + hasPaymaster: !!baseCapabilities.paymasterService?.supported, + hasFlowControl: !!baseCapabilities.flowControl?.supported, + hasDataCallback: !!baseCapabilities.datacallback?.supported + }; +} +``` + +### Conditional Transaction Building + +```typescript +async function buildTransaction(userAddress: string, calls: any[]) { + const capabilities = await provider.request({ + method: 'wallet_getCapabilities', + params: [userAddress] + }); + + const baseCapabilities = capabilities["0x2105"] || {}; + + const txParams: any = { + version: '1.0', + chainId: '0x2105', + from: userAddress, + calls + }; + + // Add gasless capability if supported + if (baseCapabilities.paymasterService?.supported) { + txParams.capabilities = { + paymasterService: { + url: "https://paymaster.base.org/api/v1/sponsor" + } + }; + } + + // Use atomic execution if supported and multiple calls + if (calls.length > 1 && baseCapabilities.atomic?.supported === "supported") { + txParams.atomicRequired = true; + } + + return txParams; +} +``` + +## Error Handling + +| Code | Message | Description | +| ---- | ------- | ----------- | +| -32602 | Invalid params | Invalid account address format | +| 4100 | Method not supported | Wallet doesn't support wallet_getCapabilities | +| 4200 | Wallet not available | Wallet is not installed or available | + + +Capabilities are chain-specific and account-specific. Always check capabilities for the specific chain and account combination you're targeting. + + + +Not all wallets support all capabilities. Use capability detection to provide progressive enhancement rather than blocking functionality when capabilities aren't available. + + +## Integration with Other Methods + +### With wallet_sendCalls + +Use capabilities to enhance transaction execution: + +```typescript +const capabilities = await provider.request({ + method: 'wallet_getCapabilities', + params: [userAddress] +}); + +// Use capabilities in wallet_sendCalls +const result = await provider.request({ + method: 'wallet_sendCalls', + params: [{ + version: '1.0', + chainId: '0x2105', + from: userAddress, + atomicRequired: capabilities["0x2105"]?.atomic?.supported === "supported", + calls: [{ + to: '0x...', + value: '0x0', + data: '0x...' + }], + capabilities: { + paymasterService: capabilities["0x2105"]?.paymasterService?.supported ? { + url: "https://paymaster.base.org/api/v1/sponsor" + } : undefined + } + }] +}); +``` + +### With wallet_connect + +Capabilities detection doesn't affect wallet_connect, but you can use the results to inform your authentication flow: + +```typescript +const capabilities = await provider.request({ + method: 'wallet_getCapabilities', + params: [userAddress] +}); + +// signInWithEthereum is always available with wallet_connect +const { accounts } = await provider.request({ + method: 'wallet_connect', + params: [{ + version: '1', + capabilities: { + signInWithEthereum: { + nonce: generateNonce(), + chainId: '0x2105' + } + } + }] +}); +``` + +## Best Practices + +1. **Always Check First**: Call `wallet_getCapabilities` before using advanced features +2. **Cache Results**: Capabilities typically don't change frequently, consider caching +3. **Graceful Fallbacks**: Implement fallback behavior when capabilities aren't supported +4. **Chain-Specific**: Check capabilities for each chain your app supports +5. **Progressive Enhancement**: Use capabilities to enhance UX, not gate basic functionality + +## Related Documentation + +- [Capabilities Overview](/base-account/reference/core/capabilities/overview) - Complete guide to using capabilities +- [wallet_sendCalls](/base-account/reference/core/provider-rpc-methods/wallet_sendCalls) - Execute transactions with capabilities +- [wallet_connect](/base-account/reference/core/provider-rpc-methods/wallet_connect) - Connect with authentication capabilities + +import PolicyBanner from "/snippets/PolicyBanner.mdx"; + + diff --git a/docs/base-account/reference/core/provider-rpc-methods/wallet_getSubAccounts.mdx b/docs/base-account/reference/core/provider-rpc-methods/wallet_getSubAccounts.mdx index 6371c2da5..43e17ffdf 100644 --- a/docs/base-account/reference/core/provider-rpc-methods/wallet_getSubAccounts.mdx +++ b/docs/base-account/reference/core/provider-rpc-methods/wallet_getSubAccounts.mdx @@ -92,7 +92,3 @@ Factory deployment data. This is an experimental feature and the API may change in future versions. - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/base-account/reference/core/provider-rpc-methods/wallet_sendCalls.mdx b/docs/base-account/reference/core/provider-rpc-methods/wallet_sendCalls.mdx index c4fb29270..c465f587c 100644 --- a/docs/base-account/reference/core/provider-rpc-methods/wallet_sendCalls.mdx +++ b/docs/base-account/reference/core/provider-rpc-methods/wallet_sendCalls.mdx @@ -131,7 +131,3 @@ Ensure that the `chainId` matches the currently selected network in the wallet t When `atomicRequired` is set to `false`, consider the implications of partial execution if some calls fail while others succeed. - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/base-account/reference/core/provider-rpc-methods/wallet_switchEthereumChain.mdx b/docs/base-account/reference/core/provider-rpc-methods/wallet_switchEthereumChain.mdx index 584214e5e..ad09d004f 100644 --- a/docs/base-account/reference/core/provider-rpc-methods/wallet_switchEthereumChain.mdx +++ b/docs/base-account/reference/core/provider-rpc-methods/wallet_switchEthereumChain.mdx @@ -70,7 +70,3 @@ If the chain is not already added to the wallet, use `wallet_addEthereumChain` f Switching chains will affect all subsequent blockchain operations in your application. - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/base-account/reference/core/provider-rpc-methods/wallet_watchAsset.mdx b/docs/base-account/reference/core/provider-rpc-methods/wallet_watchAsset.mdx index 57ce4122e..8e1359987 100644 --- a/docs/base-account/reference/core/provider-rpc-methods/wallet_watchAsset.mdx +++ b/docs/base-account/reference/core/provider-rpc-methods/wallet_watchAsset.mdx @@ -114,7 +114,3 @@ Always verify the token contract address and ensure it's a legitimate token to p The token will appear in the user's wallet token list if successfully added. - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/base-account/reference/core/provider-rpc-methods/web3_clientVersion.mdx b/docs/base-account/reference/core/provider-rpc-methods/web3_clientVersion.mdx index 989f4a070..ce5ccd02a 100644 --- a/docs/base-account/reference/core/provider-rpc-methods/web3_clientVersion.mdx +++ b/docs/base-account/reference/core/provider-rpc-methods/web3_clientVersion.mdx @@ -45,7 +45,3 @@ The current client version string. | Code | Message | Description | | ---- | ------------------------------ | ----------- | | 4100 | Requested method not supported | The method is not supported by the wallet | - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/base-account/reference/core/sdk-utilities.mdx b/docs/base-account/reference/core/sdk-utilities.mdx index 54285cb6c..a92a14659 100644 --- a/docs/base-account/reference/core/sdk-utilities.mdx +++ b/docs/base-account/reference/core/sdk-utilities.mdx @@ -5,7 +5,9 @@ description: "Core utility functions for key management and account access" The Base Account SDK provides several core utility functions for managing cryptographic keys and accessing account information. These functions are essential for advanced integrations and Sub Account management. -## generateKeyPair +## Core Functions + +### [generateKeyPair](/base-account/reference/core/generateKeyPair) Generates a new P256 key pair for use with Base Account. @@ -16,44 +18,7 @@ const keyPair = await generateKeyPair(); console.log('New key pair generated:', keyPair); ``` -### Returns - -```typescript -Promise -``` - -A P256 key pair object containing: -- `publicKey`: The public key for the generated pair -- `privateKey`: The private key (handle with care) - -### Usage Example - -```tsx -async function createNewKeyPair() { - try { - const keyPair = await generateKeyPair(); - - // Use the public key for account creation - console.log('Public key:', keyPair.publicKey); - - // Store private key securely (if needed) - // Note: Handle private keys with extreme care - - return keyPair; - } catch (error) { - console.error('Failed to generate key pair:', error); - throw error; - } -} -``` - - -**Private Key Security** - -Never expose private keys in client-side code or transmit them over insecure channels. Store them securely using appropriate key management systems. - - -## getKeypair +### [getKeypair](/base-account/reference/core/getKeypair) Retrieves an existing P256 key pair if one has been previously generated and stored. @@ -68,36 +33,8 @@ if (existingKeyPair) { } ``` -### Returns - -```typescript -Promise -``` - -Returns the stored P256 key pair or `null` if no key pair exists. - -### Usage Example - -```tsx -async function getOrCreateKeyPair() { - // Try to get existing key pair first - let keyPair = await getKeypair(); - - if (!keyPair) { - // Generate new key pair if none exists - console.log('No existing key pair, generating new one...'); - keyPair = await generateKeyPair(); - } else { - console.log('Using existing key pair'); - } - - return keyPair; -} -``` - -## getCryptoKeyAccount +### [getCryptoKeyAccount](/base-account/reference/core/getCryptoKeyAccount) -Retrieves the current crypto account associated with the user's session. This is the primary function for accessing account information. ```tsx import { getCryptoKeyAccount } from '@base-org/account'; @@ -109,70 +46,6 @@ if (cryptoAccount?.account) { } ``` -### Returns - -```typescript -Promise<{ - account: OneOf | null; -}> -``` - -Returns an object containing: -- `account`: The user's account (WebAuthn-based or local), or `null` if no account exists - -### Account Types - -The returned account can be one of two types: - -#### WebAuthnAccount -For accounts using WebAuthn (passkeys): -```typescript -interface WebAuthnAccount { - address: string; - publicKey: string; - type: 'webauthn'; - // Additional WebAuthn-specific properties -} -``` - -#### LocalAccount -For local development accounts: -```typescript -interface LocalAccount { - address: string; - publicKey: string; - type: 'local'; - // Additional local account properties -} -``` - -### Usage Example - -```tsx -async function getCurrentAccount() { - try { - const cryptoAccount = await getCryptoKeyAccount(); - - if (!cryptoAccount?.account) { - console.log('No account found - user needs to sign in'); - return null; - } - - const { account } = cryptoAccount; - - console.log('Account found:'); - console.log('- Address:', account.address); - console.log('- Type:', account.type); - console.log('- Public Key:', account.publicKey); - - return account; - } catch (error) { - console.error('Failed to get crypto account:', error); - throw error; - } -} -``` - ## Complete Integration Example Here's how these utilities work together in a typical application: @@ -311,7 +184,3 @@ async function safeGetAccount() { 5. **User experience**: Provide clear feedback when accounts aren't found or need to be created These utilities provide the foundation for advanced Base Account integrations, particularly when working with Sub Accounts and custom authentication flows. - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/base-account/reference/onchain-contracts/basenames.mdx b/docs/base-account/reference/onchain-contracts/basenames.mdx index 9bd44a152..af3bf29d8 100644 --- a/docs/base-account/reference/onchain-contracts/basenames.mdx +++ b/docs/base-account/reference/onchain-contracts/basenames.mdx @@ -8,7 +8,3 @@ You can find further documentation in the README of the repository below: import { GithubRepoCard } from "/snippets/GithubRepoCard.mdx" - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/base-account/reference/onchain-contracts/smart-wallet.mdx b/docs/base-account/reference/onchain-contracts/smart-wallet.mdx index ffac47f0e..13a51ef57 100644 --- a/docs/base-account/reference/onchain-contracts/smart-wallet.mdx +++ b/docs/base-account/reference/onchain-contracts/smart-wallet.mdx @@ -8,7 +8,3 @@ import { GithubRepoCard } from "/snippets/GithubRepoCard.mdx" You can find further documentation in the README of the repository below: - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/base-account/reference/onchain-contracts/spend-permissions.mdx b/docs/base-account/reference/onchain-contracts/spend-permissions.mdx index 9410e445a..8821deffd 100644 --- a/docs/base-account/reference/onchain-contracts/spend-permissions.mdx +++ b/docs/base-account/reference/onchain-contracts/spend-permissions.mdx @@ -154,7 +154,3 @@ spend permission has been approved or revoked. ```solidity function getCurrentPeriod(SpendPermission memory spendPermission) public view returns (PeriodSpend memory); ``` - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/base-account/reference/spend-permission-utilities/fetchPermissions.mdx b/docs/base-account/reference/spend-permission-utilities/fetchPermissions.mdx new file mode 100644 index 000000000..eaf2f12d2 --- /dev/null +++ b/docs/base-account/reference/spend-permission-utilities/fetchPermissions.mdx @@ -0,0 +1,93 @@ +--- +title: "fetchPermissions" +description: "Retrieve available Spend Permissions for an account, chain, and spender" +--- + +Defined in the [Base Account SDK](https://github.com/base/account-sdk) + + + Returns all permissions available to a given `spender` for a user's Base + Account on a specific chain. + + +## Parameters + + + User's Base Account address to query. + + + + Target chain ID. + + + + Spender address you intend to use for spending. + + + + EIP-1193 compliant Ethereum provider instance. Get this from `sdk.getProvider()`. + + +## Returns + + + Array of spend permissions matching the query. + + + +Deterministic EIP-712 hash of the permission. + + + + Signature for the EIP-712 payload. + + + + Target chain ID. + + + +Underlying permission fields. + + + + + + +Duration in seconds. +Unix timestamp (seconds). +Unix timestamp (seconds). + + + + + + + + +```typescript Fetch permissions +import { fetchPermissions } from "@base-org/account/spend-permission"; +import { createBaseAccountSDK } from "@base-org/account"; + +const sdk = createBaseAccountSDK({ + appName: 'My App', + appLogoUrl: 'https://example.com/logo.png', + appChainIds: [84532], +}); + +const permissions = await fetchPermissions({ + account: "0xUserBaseAccountAddress", + chainId: 84532, + spender: "0xAppSpenderAddress", + provider: sdk.getProvider(), +}); +``` + + +## Error Handling + +Always wrap the call in a try-catch block to handle these errors gracefully. + +import PolicyBanner from "/snippets/PolicyBanner.mdx"; + + diff --git a/docs/base-account/reference/spend-permission-utilities/getPermissionStatus.mdx b/docs/base-account/reference/spend-permission-utilities/getPermissionStatus.mdx new file mode 100644 index 000000000..ebb5d7f4b --- /dev/null +++ b/docs/base-account/reference/spend-permission-utilities/getPermissionStatus.mdx @@ -0,0 +1,86 @@ +--- +title: "getPermissionStatus" +description: "Compute remaining spend and active status for a Spend Permission" +--- + +Defined in the [Base Account SDK](https://github.com/base/account-sdk) + + + Validates a permission against current time and revocation state and returns + remaining spend and next period start. + + +## Parameters + + + Signed permission to evaluate. This should be a SpendPermission object returned from [`requestSpendPermission`](/base-account/reference/spend-permission-utilities/requestSpendPermission) or fetched via [`fetchPermissions`](/base-account/reference/spend-permission-utilities/fetchPermissions). + + + +Deterministic EIP-712 hash of the permission. + + + + Signature for the EIP-712 payload. + + + + Target chain ID. + + + +Underlying permission fields. + + + + + + +Duration in seconds. +Unix timestamp (seconds). +Unix timestamp (seconds). + + + + + + + +## Returns + + +Status details for the permission. + + +Remaining allowance in wei for the current period. +When the next allowance period begins. +True if approved and not revoked or expired. + + + + +```typescript Check permission status +import { getPermissionStatus } from "@base-org/account/spend-permission"; + +const { isActive, remainingSpend } = await getPermissionStatus(permission); +``` + + + +```typescript Example response +{ + remainingSpend: 1000000000000000000n, + nextPeriodStart: new Date("2024-01-31T00:00:00Z"), + isActive: true +} +``` + + + +## Error Handling + +Always wrap the call in a try-catch block to handle these errors gracefully. + +import PolicyBanner from "/snippets/PolicyBanner.mdx"; + + diff --git a/docs/base-account/reference/spend-permission-utilities/prepareRevokeCallData.mdx b/docs/base-account/reference/spend-permission-utilities/prepareRevokeCallData.mdx new file mode 100644 index 000000000..37e3950be --- /dev/null +++ b/docs/base-account/reference/spend-permission-utilities/prepareRevokeCallData.mdx @@ -0,0 +1,88 @@ +--- +title: "prepareRevokeCallData" +description: "Construct calldata so your app's spender can revoke a Spend Permission without user interaction" +--- + +Defined in the [Base Account SDK](https://github.com/base/account-sdk) + + + Builds a single revoke call that your app's spender account can submit to + revoke a permission silently. This requires your app to control a spender + account with rights to revoke. + + +## Parameters + + + The spend permission to revoke. This should be a SpendPermission object returned from [`requestSpendPermission`](/base-account/reference/spend-permission-utilities/requestSpendPermission) or fetched via [`fetchPermissions`](/base-account/reference/spend-permission-utilities/fetchPermissions). + + + +Deterministic EIP-712 hash of the permission. + + + + Signature for the EIP-712 payload. + + + + Target chain ID. + + + +Underlying permission fields. + + + + + + +Duration in seconds. +Unix timestamp (seconds). +Unix timestamp (seconds). + + + + + + + +## Returns + + +Call data for the revoke transaction. + + + + + + + + + +```typescript Silent revoke via spender account +import { prepareRevokeCallData } from "@base-org/account/spend-permission"; + +const revokeCall = await prepareRevokeCallData(permission); + +await provider.request({ + method: "wallet_sendCalls", + params: [ + { + version: "2.0", + atomicRequired: true, + from: spender, + calls: [revokeCall], + }, + ], +}); +``` + + +## Error Handling + +Always wrap the call in a try-catch block to handle these errors gracefully. + +import PolicyBanner from "/snippets/PolicyBanner.mdx"; + + diff --git a/docs/base-account/reference/spend-permission-utilities/prepareSpendCallData.mdx b/docs/base-account/reference/spend-permission-utilities/prepareSpendCallData.mdx new file mode 100644 index 000000000..0c0ea6046 --- /dev/null +++ b/docs/base-account/reference/spend-permission-utilities/prepareSpendCallData.mdx @@ -0,0 +1,108 @@ +--- +title: "prepareSpendCallData" +description: "Prepare calldata to approve (if needed) and spend using a Spend Permission" +--- + +Defined in the [Base Account SDK](https://github.com/base/account-sdk) + + + Returns one or two calls your app's spender can submit to execute a spend. If + the permission is not yet registered onchain, an `approveWithSignature` call + is prepended before the `spend` call. + + +## Parameters + + + Signed permission returned from [`requestSpendPermission`](/base-account/reference/spend-permission-utilities/requestSpendPermission) or fetched via [`fetchPermissions`](/base-account/reference/spend-permission-utilities/fetchPermissions). + + + +Deterministic EIP-712 hash of the permission. + + + + Signature for the EIP-712 payload. + + + + Target chain ID. + + + +Underlying permission fields. + + + + + + +Duration in seconds. +Unix timestamp (seconds). +Unix timestamp (seconds). + + + + + + + + + Amount to spend (in wei). Omit to spend the remaining allowance. + + +## Returns + + +Array of calls to submit in order. + + + + + + + + + +```typescript Prepare and submit spend calls +import { prepareSpendCallData } from "@base-org/account/spend-permission"; + +const spendCalls = await prepareSpendCallData({ + permission, + amount: 10_000n, // optional +}); + +// If supported, submit both in a batch via wallet_sendCalls +await provider.request({ + method: "wallet_sendCalls", + params: [ + { + version: "2.0", + atomicRequired: true, + from: spender, + calls: spendCalls, + }, + ], +}); + +// Or submit sequentially with eth_sendTransaction +for (const call of spendCalls) { + await provider.request({ + method: "eth_sendTransaction", + params: [{ ...call, from: spender }], + }); +} +``` + + +## Error Handling + +Always wrap the call in a try-catch block to handle these errors gracefully. + + +Use `getPermissionStatus` to check `isActive` and `remainingSpend` before preparing a spend. + + +import PolicyBanner from "/snippets/PolicyBanner.mdx"; + + diff --git a/docs/base-account/reference/spend-permission-utilities/requestRevoke.mdx b/docs/base-account/reference/spend-permission-utilities/requestRevoke.mdx new file mode 100644 index 000000000..28b56d5e8 --- /dev/null +++ b/docs/base-account/reference/spend-permission-utilities/requestRevoke.mdx @@ -0,0 +1,74 @@ +--- +title: "requestRevoke" +description: "Request the user's approval to revoke a Spend Permission" +--- + +Defined in the [Base Account SDK](https://github.com/base/account-sdk) + + + Prompts the user to approve revoking the specified spend permission. Requires + user interaction via their Base Account wallet UI. + + +## Parameters + + + The spend permission to revoke. This should be a SpendPermission object returned from [`requestSpendPermission`](/base-account/reference/spend-permission-utilities/requestSpendPermission) or fetched via [`fetchPermissions`](/base-account/reference/spend-permission-utilities/fetchPermissions). + + + +Deterministic EIP-712 hash of the permission. + + + + Signature for the EIP-712 payload. + + + + Target chain ID. + + + +Underlying permission fields. + + + + + + +Duration in seconds. +Unix timestamp (seconds). +Unix timestamp (seconds). + + + + + + + +## Returns + + + Transaction hash of the revoke operation. + + + +```typescript User-initiated revoke +import { requestRevoke } from "@base-org/account/spend-permission"; + +try { + const hash = await requestRevoke(permission); + console.log("Revoke succeeded", hash); +} catch (err) { + console.warn("Revoke was rejected or failed", err); +} +``` + + +## Error Handling + +Always wrap the call in a try-catch block to handle these errors gracefully. + +import PolicyBanner from "/snippets/PolicyBanner.mdx"; + + diff --git a/docs/base-account/reference/spend-permission-utilities/requestSpendPermission.mdx b/docs/base-account/reference/spend-permission-utilities/requestSpendPermission.mdx new file mode 100644 index 000000000..a75b30885 --- /dev/null +++ b/docs/base-account/reference/spend-permission-utilities/requestSpendPermission.mdx @@ -0,0 +1,128 @@ +--- +title: "requestSpendPermission" +description: "Create and sign an EIP-712 Spend Permission for a user's Base Account" +--- + +Defined in the [Base Account SDK](https://github.com/base/account-sdk) + + + Constructs an EIP-712 payload for a spend permission and prompts the user to + sign it. Returns a `SpendPermission` object containing the signature and + normalized permission data you can later use to spend or register onchain. + + +## Parameters + + + Smart account that this spend permission applies to. + + + + The spender that can move funds from `account` within the configured limits. + + + + Token address. Supports ERC-7528 native token address and ERC-20 contracts. + + + + Chain ID where this permission is valid. + + + + Maximum amount that can be spent during each period (in wei). + + + + Length of each allowance period in days. + + + + Start time when the spend permission becomes valid. Defaults to now. + + + + Time when the spend permission expires. Defaults to never. + + + + Arbitrary salt to differentiate otherwise identical permissions. Hex string. + Defaults to a random value. + + + + Arbitrary data to attach to the permission. Hex string. Defaults to `0x`. + + + + EIP-1193 compliant Ethereum provider instance. Get this from `sdk.getProvider()`. + + +## Returns + + +Signed spend permission payload. + + + +Deterministic EIP-712 hash of the permission. + + + + Signature for the EIP-712 payload. + + + + Target chain ID. + + + +Underlying permission fields. + + + + + + +Duration in seconds. +Unix timestamp (seconds). +Unix timestamp (seconds). + + + + + + + + +```typescript Create and sign a spend permission +import { requestSpendPermission } from "@base-org/account/spend-permission"; +import { createBaseAccountSDK } from "@base-org/account"; + +const sdk = createBaseAccountSDK({ + appName: 'My App', + appLogoUrl: 'https://example.com/logo.png', + appChainIds: [84532], +}); + +const permission = await requestSpendPermission({ + account: "0xUserBaseAccountAddress", + spender: "0xAppSpenderAddress", + token: "0xTokenContractAddress", + chainId: 84532, + allowance: 1_000_000n, + periodInDays: 30, + provider: sdk.getProvider(), +}); + +console.log("Spend Permission:", permission); +``` + + +## Error Handling + +Always wrap the call in a try-catch block to handle these errors gracefully. + +import PolicyBanner from "/snippets/PolicyBanner.mdx"; + + diff --git a/docs/base-account/reference/ui-elements/base-pay-button.mdx b/docs/base-account/reference/ui-elements/base-pay-button.mdx index dc5ec1b72..6bd5c4733 100644 --- a/docs/base-account/reference/ui-elements/base-pay-button.mdx +++ b/docs/base-account/reference/ui-elements/base-pay-button.mdx @@ -435,7 +435,3 @@ For testing your integration: 4. **Verify user info collection**: Test with different `payerInfo` configurations The BasePayButton provides a complete, production-ready payment solution that handles all the complexity of crypto payments while providing a familiar user experience. - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/base-account/reference/ui-elements/brand-guidelines.mdx b/docs/base-account/reference/ui-elements/brand-guidelines.mdx index 16d030db2..83966cb77 100644 --- a/docs/base-account/reference/ui-elements/brand-guidelines.mdx +++ b/docs/base-account/reference/ui-elements/brand-guidelines.mdx @@ -3,6 +3,10 @@ title: 'Brand Guidelines' description: 'Design and brand guidelines for Sign in With Base and Base Pay buttons' --- +import {BasePayButton} from "/snippets/BasePayButton.mdx" +import {SignInWithBaseButton} from "/snippets/SignInWithBaseButton.mdx" + + ## Sign in With Base & Base Pay Base account offers two buttons to use in your application: @@ -13,6 +17,15 @@ Base account offers two buttons to use in your application: Integrating "Sign in With Base" offers a convenient and trusted way for users to access your services. By leveraging their established Base account, users can avoid creating and remembering new credentials, leading to a smoother onboarding and login process. +
+ +
+
+
+ +
+ + ### Best Practices To provide the best possible user experience when integrating "Sign in With Base," consider the following guidelines: @@ -95,6 +108,62 @@ For detailed technical integration steps and API references, please refer to the Integrating "Base Pay" offers one-click checkout for users with a Base Account. Integrate it into your product for easy purchase power for online and offline goods. +
+ +
+
+
+ +
+ ### Design & Brand Guidelines The "Base Pay" button should be easily recognizable and consistent across all platforms. Adhering to these design guidelines ensures a familiar and trusted experience for users. @@ -138,6 +207,3 @@ Following are some DOs and DON'Ts for the Base branding: You can find the full set of Base Pay and Sign in with Base media assets in the [Base Brand Assets Figma File](https://www.figma.com/design/L8K5httaDFjiAqfWrChMFi/Sign-in-with-Base---Base-Pay%E2%80%94Media-Kit?node-id=0-1&p=f&t=5766QEX963be2lrj-0). -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - diff --git a/docs/base-account/reference/ui-elements/sign-in-with-base-button.mdx b/docs/base-account/reference/ui-elements/sign-in-with-base-button.mdx index 859b59d78..79d1f5a88 100644 --- a/docs/base-account/reference/ui-elements/sign-in-with-base-button.mdx +++ b/docs/base-account/reference/ui-elements/sign-in-with-base-button.mdx @@ -538,7 +538,3 @@ function AuthButton({ onAuthSuccess, onAuthError }: AuthButtonProps) { 6. **Testing**: Test with different wallet states (connected, disconnected, etc.) The SignInWithBaseButton provides a complete, production-ready authentication solution that handles all the complexity of wallet-based authentication while providing a familiar user experience. - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/base-app/agents/best-practices.mdx b/docs/base-app/agents/best-practices.mdx new file mode 100644 index 000000000..81034eb85 --- /dev/null +++ b/docs/base-app/agents/best-practices.mdx @@ -0,0 +1,19 @@ +--- +title: 'Chat Agent Best Practices' +description: 'Guidelines and recommendations for building effective chat agents' +sidebarTitle: 'Best Practices' +--- + +### Best practices + +1. Always validate content structures +2. Provide fallbacks for unsupported clients +3. Test across client versions +4. Limit message and metadata size +5. Follow [XIP-67](https://community.xmtp.org/t/xip-67-inline-actions/941) +6. Your agent should respond to both direct messages and group chat messages (if applicable). +7. For direct messages, it should automatically respond. +8. We recommend reacting to messages with an emoji (like 👀or ⌛) to indicate the message was received, while the response is processing. +9. For group chat messages, agents should only respond if they are mentioned with the "@" symbol and the agent's name. (example "@bankr"), OR if the user replies directly to a message from the agent (using the reply content type). + +For group chat messages, agents should only respond if they are mentioned with the "@" symbol and the agent's name. (example "@bankr"), OR if the user replies directly to a message from the agent (using the reply content type). diff --git a/docs/base-app/agents/building-quality-agents.mdx b/docs/base-app/agents/building-quality-agents.mdx new file mode 100644 index 000000000..17ea39c98 --- /dev/null +++ b/docs/base-app/agents/building-quality-agents.mdx @@ -0,0 +1,146 @@ +--- +title: 'Building Quality Agents for Base App' +description: Learn the best practices and guidelines for creating high-quality agents that get featured in Base App +sidebarTitle: 'Building Quality Agents' +--- + +As you start building, review these guidelines to understand what we look for when featuring agents in the Base app. We recommend trying out existing agents in the app first to get a feel for the quality bar, what works well, and areas for improvement. + +## Build a high quality foundation + +Your agent should provide a seamless, professional experience that users will want to engage with repeatedly. Here are the core requirements: + +### Response Behavior + +**Multi-Channel Support** +- Respond to both DMs and group chats appropriately +- Maintain consistent functionality across different conversation types + +**Immediate Feedback** +- React to messages with a simple reaction (👀, 👍, ⌛, etc.) to show acknowledgment +- This gives users confidence their message was received while processing + +**Fast Response Times** +- Provide responses quickly (< 5 seconds) +- Users expect near-instant communication in messaging apps + +### Group Chat Etiquette + +In group chats, agents should only respond when: + +1. **Mentioned directly** with "@" + agent name (e.g., @bankr) +2. **Replied to directly** when a user replies to the agent's message using the reply content type + +This prevents spam and ensures agents participate naturally in group conversations. + +### Communication Style + +**Sound Human** +- Use conversational, fun, and clear language +- Keep responses polished but not robotic +- Match the energy and tone of the conversation + +**Privacy Conscious** +- Only ask for personal information when absolutely necessary +- Always explain why the information is needed +- Respect user privacy and data minimization principles + +## Craft compelling onboarding + +Your agent's first impression is critical. The onboarding message should immediately communicate value and give users a clear path forward. + +### Great Onboarding Message Structure + +1. **Introduce the agent** - Quick, friendly greeting with the agent's name +2. **Explain capabilities** - Clear, specific examples of what it can do +3. **Provide next steps** - Give users an obvious action to take + +### Example: High-Quality Onboarding + +``` +hey, i'm bankr. i can help you trade, transfer, and manage your crypto. here's the rundown: + +• trade anything: buy, sell, swap tokens on base, polygon, and mainnet. try "buy 0.1 eth of degen." +• send it: transfer crypto to anyone on x, farcaster, or a wallet address. +• get alpha: token recs, market data, charts. +• automate: set up recurring buys/sells. e.g. "buy $10 of $bnkr every week." + +what do you want to do first? +``` + +**Why this works:** +- Friendly, conversational tone +- Specific feature examples with concrete commands +- Clear value propositions +- Ends with a direct call-to-action + +### Example: Poor Onboarding + +``` +Gm! What can I help you with? +``` + +**Why this fails:** +- Generic greeting with no context +- No explanation of capabilities +- Puts burden on user to figure out what to do +- No clear value proposition + +## Showcase unique value + +### Solve Real Problems + +Your agent should: +- **Address a unique pain point** or bring a delightful twist to an existing space +- **Help users accomplish tasks** more easily than existing solutions +- **Provide clear benefits** that users can understand immediately + +### Enable User Success + +Focus on helping users: +- **Earn** - Generate income, rewards, or value +- **Connect** - Build relationships or communities +- **Have fun** - Provide entertainment or engaging experiences +- **Complete tasks** - Streamline workflows or processes + +### Design for Engagement + +**Build Natural Growth Loops** +- Include features that encourage sharing and re-engagement +- Make it beneficial for users to invite others +- Create ongoing value that brings users back + +**Plan the User Journey** +1. **Define the ideal user experience first** +2. **Craft agent messages around that journey** +3. **Guide users through progressive value discovery** + +### Continuous Engagement Strategy + +As users complete actions with your agent: + +- **Show clear next steps** - Always give users something else to try +- **Highlight ongoing value** - Explain how continued use benefits them +- **Create habit loops** - Design features that encourage regular interaction +- **Prevent one-and-done usage** - Build features that require return visits + +### Examples of Engagement Features + +- **Progressive features** - Unlock new capabilities as users engage more +- **Personalization** - Learn user preferences and customize experiences +- **Social elements** - Enable sharing achievements or inviting friends +- **Recurring value** - Automated tasks, alerts, or regular check-ins +- **Gamification** - Points, levels, or achievement systems + +## Next steps + +Once you've built your agent following these guidelines: + +1. **Test thoroughly** - Try your agent in different scenarios and conversation types +2. **Get feedback** - Have others test and provide honest feedback +3. **Iterate quickly** - Make improvements based on real user interactions +4. **Submit for review** - Follow the submission guidelines when ready + +Remember: agents often go through multiple rounds of feedback before being featured. Focus on creating genuine value and a polished user experience that people will love using repeatedly. + +For technical implementation details, see our [Chat Agents guide](/base-app/agents/chat-agents). diff --git a/docs/wallet-app/guides/chat-agents.mdx b/docs/base-app/agents/chat-agents.mdx similarity index 68% rename from docs/wallet-app/guides/chat-agents.mdx rename to docs/base-app/agents/chat-agents.mdx index 63352ee68..36f185def 100644 --- a/docs/wallet-app/guides/chat-agents.mdx +++ b/docs/base-app/agents/chat-agents.mdx @@ -1,17 +1,18 @@ --- -title: "Chat Agents in Coinbase Wallet" -sidebarTitle: "Chat Agents" +title: 'Chat Agents in Base App' +description: Learn how to build chat agents for Base App, using XMTP +sidebarTitle: 'Chat Agents' --- -This guide will cover how you can get started building messaging agents for Coinbase Wallet, using XMTP, a decentralized messaging protocol. Discover a fast, easy way to build and get distribution in Coinbase Wallet. +This guide will cover how you can get started building messaging agents for Base App, using XMTP, a decentralized messaging protocol. Discover a fast, easy way to build and get distribution in Base App. - Why agents? - Getting started with XMTP -- Getting featured in Coinbase Wallet +- Getting featured in Base App ## Why agents? -Messaging is the largest use-case in the world, but it’s more than just conversations—it’s a secure, programmable channel for financial and social innovation. When combined with the onchain capabilities of Coinbase Wallet, builders have a new surface area to build 10X better messaging experiences not currently possible on legacy platforms like WhatsApp or Messenger. +Messaging is the largest use-case in the world, but it’s more than just conversations—it’s a secure, programmable channel for financial and social innovation. When combined with the onchain capabilities of Base App, builders have a new surface area to build 10X better messaging experiences not currently possible on legacy platforms like WhatsApp or Messenger. Real Examples: @@ -21,7 +22,7 @@ Real Examples: • Travel Planning Agent: "Book flight LAX to NYC under $300" and get instant booking with crypto payments, all in your group chat -• Coinbase Wallet & XMTP are combining AI, crypto, and mini apps with secure messaging – to unlock use-cases never before possible. Secure group chats & DMs are the new surface area for developers. +• Base App & XMTP are combining AI, crypto, and mini apps with secure messaging – to unlock use-cases never before possible. Secure group chats & DMs are the new surface area for developers. ## Getting started @@ -130,7 +131,6 @@ You can also explore example implementations in the `/examples` folder, includin - [xmtp-transactions](https://github.com/ephemeraHQ/xmtp-agent-examples/tree/main/examples/xmtp-transactions): Use XMTP content types to send transactions - [xmtp-smart-wallet](https://github.com/ephemeraHQ/xmtp-agent-examples/tree/main/examples/xmtp-smart-wallet): Agent that uses a smart wallet to send messages - **STEP 5: TEST YOUR AGENT** **Development Testing** @@ -142,32 +142,32 @@ npm run dev ``` 2. Test on [xmtp.chat:](https://xmtp.chat/conversations) -• Go to https://xmtp.chat -• Connect your personal wallet -• Switch to Dev environment in settings -• Start a new conversation with your agent's public address (from .env) -• Send a test message and verify the agent responds + • Go to https://xmtp.chat + • Connect your personal wallet + • Switch to Dev environment in settings + • Start a new conversation with your agent's public address (from .env) + • Send a test message and verify the agent responds **Production Testing** 1. Update environment: ```javascript -XMTP_ENV=production +XMTP_ENV = production; ``` -2. Test on Coinbase Wallet: -• Open Coinbase Wallet mobile app -• Go to messaging -• Start conversation with your agent's address -• Verify functionality +2. Test on Base App: + • Open Base App mobile app + • Go to messaging + • Start conversation with your agent's address + • Verify functionality **STEP 6: GET A BASENAME FOR YOUR AGENT** Give your agent a human-readable name: -**1. Import agent wallet to Coinbase Wallet extension:** -• Install Coinbase Wallet browser extension +**1. Import agent wallet to Base App extension:** +• Install Base App browser extension • Import using your agent's private key **2. Purchase a basename:** @@ -200,13 +200,14 @@ Heroku, Vercel, or any Node.js hosting platform: **STEP 8: MONITOR AND MAINTAIN** -**Best Practices** -1. Logging: Add comprehensive logging to track agent activity -2. Error Handling: Implement try-catch blocks for network issues -3. Rate Limiting: Respect XMTP rate limits in your agent logic +**Best Practices** + +1. Logging: Add comprehensive logging to track agent activity +2. Error Handling: Implement try-catch blocks for network issues +3. Rate Limiting: Respect XMTP rate limits in your agent logic 4. Security: Never expose private keys; use environment variables -**Monitoring** +**Monitoring** Add to your agent for basic monitoring: @@ -225,11 +226,16 @@ console.log(` • Installations: ${inboxState.installations.length} • InstallationId: ${installationId} • Network: ${process.env.XMTP_ENV}`); + +* console.log(\`Agent started. Address: ${xmtp.address}\`) +* console.log(\`Environment: ${process.env.XMTP\_ENV}\`) +* console.log(\`Listening for messages...\`) + ``` ## Content types -Coinbase Wallet supports various XMTP content types for rich messaging capabilities. This document outlines all supported content types, including custom types for Quick Actions and Intent. +Base App supports various XMTP content types for rich messaging capabilities. This document outlines all supported content types, including custom types for Quick Actions and Intent. XMTP uses content types to define the structure and meaning of message types. @@ -238,32 +244,38 @@ We'll outline the standard and custom XMTP content types you can utilize in your ### XMTP Content Types **Text Messages** + - Content Type: `xmtp.org/text:1.0` - Purpose: Basic text messaging - Usage: Default for plain text **Attachments** + - Content Type: `xmtp.org/attachment:1.0` - Purpose: File attachments (inline) - Usage: Send files directly in messages **Remote Static Attachments** + - Content Type: `xmtp.org/remoteStaticAttachment:1.0` - Purpose: Remote file attachments via URLs - Usage: Reduce message size **Reactions** + - Content Type: `xmtp.org/reaction:1.0` - Purpose: Emoji reactions - Usage: React to messages with emojis - Note: Does not trigger read receipts **Replies** + - Content Type: `xmtp.org/reply:1.0` - Purpose: Threaded conversations - Usage: Reply to specific messages **Group Management** + - Content Types: - `xmtp.org/group_membership_change:1.0` - `xmtp.org/group_updated:1.0` @@ -271,27 +283,31 @@ We'll outline the standard and custom XMTP content types you can utilize in your - Usage: Automatic system messages **Read Receipts** + - Content Type: `xmtp.org/readReceipt:1.0` - Purpose: Read confirmations - Usage: Sent automatically **Transactions (Wallet Send Calls)** + - Content Type: `xmtp.org/walletSendCalls:1.0` - Purpose: Request wallet actions from users **Transaction Receipts** + - Content Type: `xmtp.org/transactionReference:1.0` - Purpose: Share blockchain transaction info -### Coinbase Wallet Content Types +### Base App Content Types -There are content types developed by the Coinbase Wallet team for agents in Coinbase Wallet. Other XMTP clients may not support these content types. +There are content types developed by the Base App team for agents in Base App. Other XMTP clients may not support these content types. **Quick Actions (coinbase.com/actions:1.0)** Purpose: Present interactive buttons in a message Structure: + ```typescript type ActionsContent = { id: string; @@ -310,11 +326,13 @@ type Action = { ``` Validation Rules: + - `id`, `description` are required - `actions` must be 1–10 items with unique IDs - Style must be one of: `primary`, `secondary`, `danger` Fallback: + ``` [Description] @@ -325,24 +343,35 @@ Reply with the number to select ``` Example: Payment Options + ```json { - id: 'payment_alice_123', - description: 'Choose amount to send to Alice', - actions: [ - { id: 'send_10', label: 'Send $10', style: 'primary' }, - { id: 'send_20', label: 'Send $20', style: 'primary' }, - { id: 'custom_amount', label: 'Custom Amount', style: 'secondary' }, + "id": "payment_alice_123", + "description": "Choose amount to send to Alice", + "actions": [ + { "id": "send_10", "label": "Send $10", "style": "primary" }, + { "id": "send_20", "label": "Send $20", "style": "primary" }, + { "id": "custom_amount", "label": "Custom Amount", "style": "secondary" } ], - expiresAt: '2024-12-31T23:59:59Z' + "expiresAt": "2024-12-31T23:59:59Z" } ``` + +Manifest Embed Example + + + **Intent (coinbase.com/intent:1.0)** Purpose: User expresses choice from Quick Actions Structure: + ```typescript type IntentContent = { id: string; @@ -352,16 +381,19 @@ type IntentContent = { ``` Validation Rules: + - `id`, `actionId` required - Must match corresponding Actions - `metadata` is optional, `<10KB` Fallback: + ``` User selected action: [actionId] ``` Example: Intent Message + ```json { id: 'payment_alice_123', @@ -373,28 +405,63 @@ Example: Intent Message } ``` -### Best practices + + Analytics dashboard with charts + + +## Custom Transaction Trays + +If you would like to display agent information such as favicon and branded title, utilize the metadata property of wallet send calls data. + +#### **Example: Display agent information** -- Always validate content structures -- Provide fallbacks for unsupported clients -- Test across client versions -- Limit message and metadata size -- Follow XIP-67 +```ts +// example of wallet send calls data shape{ + version: "1.0", + from: request.from as `0x${string}`, + chainId: this.networkConfig.chainId, + calls: [ + { + ... + metadata: { + description: "Transfer token", + ...other fields, + hostname: "tba.chat", + faviconUrl: "https://tba.chat/favicon", + title: "Your favorite Agent" + } + } + ] +} +``` -Your agent should respond to both direct messages and group chat messages (if applicable). + + Transaction tray + -For direct messages, it should automatically respond. +You can send “GM” to **tbachat.base.eth** to get more details about message content types we support and get the firsthand experience on by interacting with our agent -We recommend reacting to messages with an emoji (like 👀 or ⌛) to indicate the message was received, while the response is processing. +Our agent repo is found [here](https://github.com/siwan-cb/tba-chat-example-bot) + +### Best practices + +1. Always validate content structures +2. Provide fallbacks for unsupported clients +3. Test across client versions +4. Limit message and metadata size +5. Follow [XIP-67](https://community.xmtp.org/t/xip-67-inline-actions/941) +6. Your agent should respond to both direct messages and group chat messages (if applicable). +7. For direct messages, it should automatically respond. +8. We recommend reacting to messages with an emoji (like 👀or ⌛) to indicate the message was received, while the response is processing. +9. For group chat messages, agents should only respond if they are mentioned with the “@” symbol and the agent’s name. (example “@bankr”), OR if the user replies directly to a message from the agent (using the reply content type). For group chat messages, agents should only respond if they are mentioned with the "@" symbol and the agent's name. (example "@bankr"), OR if the user replies directly to a message from the agent (using the reply content type). ## Getting featured -Fill out the form [here](https://app.deform.cc/form/52b71db4-bfa2-4ef5-a954-76c66250bdd2/?page_number=0) to submit your agent for review. If approved, your agent will be featured in Coinbase Wallet. You will hear back from us within 5 business days. +Fill out the form [here](https://app.deform.cc/form/52b71db4-bfa2-4ef5-a954-76c66250bdd2/?page_number=0) to submit your agent for review. Note, agents often go through multiple rounds of feedback before they are ready to be featured. Once approved, your agent will be featured in Base App. You will hear back from us within 5 business days. Need help or have feature requests? Visit [https://community.xmtp.org/](https://community.xmtp.org/) - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/base-app/agents/content-types.mdx b/docs/base-app/agents/content-types.mdx new file mode 100644 index 000000000..d56dd49ab --- /dev/null +++ b/docs/base-app/agents/content-types.mdx @@ -0,0 +1,183 @@ +--- +title: 'Content Types for Chat Agents' +description: 'Learn about supported XMTP content types and how to use them in your chat agent' +sidebarTitle: 'Content Types' +--- + +## Content types + +Base App supports various XMTP content types for rich messaging capabilities. This document outlines all supported content types, including custom types for Quick Actions and Intent. + +XMTP uses content types to define the structure and meaning of message types. + +We'll outline the standard and custom XMTP content types you can utilize in your agent. + +### XMTP Content Types + +**Text Messages** + +- Content Type: `xmtp.org/text:1.0` +- Purpose: Basic text messaging +- Usage: Default for plain text + +**Attachments** + +- Content Type: `xmtp.org/attachment:1.0` +- Purpose: File attachments (inline) +- Usage: Send files directly in messages + +**Remote Static Attachments** + +- Content Type: `xmtp.org/remoteStaticAttachment:1.0` +- Purpose: Remote file attachments via URLs +- Usage: Reduce message size + +**Reactions** + +- Content Type: `xmtp.org/reaction:1.0` +- Purpose: Emoji reactions +- Usage: React to messages with emojis +- Note: Does not trigger read receipts + +**Replies** + +- Content Type: `xmtp.org/reply:1.0` +- Purpose: Threaded conversations +- Usage: Reply to specific messages + +**Group Management** + +- Content Types: + - `xmtp.org/group_membership_change:1.0` + - `xmtp.org/group_updated:1.0` +- Purpose: Membership & group metadata updates +- Usage: Automatic system messages + +**Read Receipts** + +- Content Type: `xmtp.org/readReceipt:1.0` +- Purpose: Read confirmations +- Usage: Sent automatically + +**Transactions (Wallet Send Calls)** + +- Content Type: `xmtp.org/walletSendCalls:1.0` +- Purpose: Request wallet actions from users + +**Transaction Receipts** + +- Content Type: `xmtp.org/transactionReference:1.0` +- Purpose: Share blockchain transaction info + +### Base App Content Types + +There are content types developed by the Base App team for agents in Base App. Other XMTP clients may not support these content types. + +**Quick Actions (coinbase.com/actions:1.0)** + +Purpose: Present interactive buttons in a message + +Structure: + +```typescript +type ActionsContent = { + id: string; + description: string; + actions: Action[]; + expiresAt?: string; +}; + +type Action = { + id: string; + label: string; + imageUrl?: string; + style?: 'primary' | 'secondary' | 'danger'; + expiresAt?: string; +}; +``` + +Validation Rules: + +- `id`, `description` are required +- `actions` must be 1–10 items with unique IDs +- Style must be one of: `primary`, `secondary`, `danger` + +Fallback: + +``` +[Description] + +[1] [Action Label 1] +[2] [Action Label 2] +... +Reply with the number to select +``` + +Example: Payment Options + +```json +{ + "id": "payment_alice_123", + "description": "Choose amount to send to Alice", + "actions": [ + { "id": "send_10", "label": "Send $10", "style": "primary" }, + { "id": "send_20", "label": "Send $20", "style": "primary" }, + { "id": "custom_amount", "label": "Custom Amount", "style": "secondary" } + ], + "expiresAt": "2024-12-31T23:59:59Z" +} +``` + + + Manifest Embed Example + + +**Intent (coinbase.com/intent:1.0)** + +Purpose: User expresses choice from Quick Actions + +Structure: + +```typescript +type IntentContent = { + id: string; + actionId: string; + metadata?: Record; +}; +``` + +Validation Rules: + +- `id`, `actionId` required +- Must match corresponding Actions +- `metadata` is optional, `<10KB` + +Fallback: + +``` +User selected action: [actionId] +``` + +Example: Intent Message + +```json +{ + id: 'payment_alice_123', + actionId: 'send_10', + metadata: { + timestamp: Date.now(), + actionLabel: 'Send $10' + } +} +``` + + + Analytics dashboard with charts + diff --git a/docs/base-app/agents/deeplinks.mdx b/docs/base-app/agents/deeplinks.mdx new file mode 100644 index 000000000..d0f0d502e --- /dev/null +++ b/docs/base-app/agents/deeplinks.mdx @@ -0,0 +1,436 @@ +--- +title: 'Deeplinks for Agent Interactions' +description: 'Learn how to use deeplinks to create seamless agent experiences and direct user interactions within Base App.' +sidebarTitle: 'Deeplinks' +--- + +> **What you'll learn** +> By the end of this guide, you'll be able to: +> - Implement deeplinks for direct messaging with agents +> - Create seamless user flows between group and private conversations +> - Follow best practices for deeplink implementation +> - Handle fallbacks and cross-client compatibility + +## Overview + +Deeplinks enable seamless navigation within Base App, allowing agents to create more engaging and intuitive user experiences. The most common use case is directing users to start a private conversation with an agent from a group chat context. + +## Direct Message Deeplinks + +### Use Case + +Your miniapp has an agent and you want to encourage people to chat with the agent directly. Or, your agent exists in a group chat context and wants users to interact with it privately. You could add a button like "Chat with me" and use this deeplink. + +### Syntax + +``` +cbwallet://messaging/address +``` + +### Parameters + +- **address** — The 0x address of the user you want to chat with (in hex format, e.g., `0xabc...1234`) + +### Implementation Examples + +**Basic Button Implementation:** + +```typescript + +``` + +**React Component with Error Handling:** + +```typescript +import { useState } from 'react'; + +interface DeeplinkButtonProps { + agentAddress: string; + label?: string; + className?: string; +} + +export function DeeplinkButton({ + agentAddress, + label = "Direct Message", + className = "btn-primary" +}: DeeplinkButtonProps) { + const [isLoading, setIsLoading] = useState(false); + + const handleDeeplink = async () => { + setIsLoading(true); + try { + const deeplink = `cbwallet://messaging/${agentAddress}`; + + // Check if running in supported environment + if (typeof window !== 'undefined') { + window.location.href = deeplink; + } else { + // Fallback for server-side rendering + console.warn('Deeplink not supported in this environment'); + } + } catch (error) { + console.error('Failed to open deeplink:', error); + } finally { + setIsLoading(false); + } + }; + + return ( + + ); +} +``` + +**Agent Message with Deeplink:** + +```typescript +import { Client } from "@xmtp/node-sdk"; + +async function sendDeeplinkMessage(conversation: any, agentAddress: string) { + const deeplink = `cbwallet://messaging/${agentAddress}`; + + await conversation.send( + `💬 Want to chat privately? Tap here to start a direct conversation:\n\n${deeplink}` + ); +} + +// Usage in agent logic +if (message.content.includes("/private") || message.content.includes("/dm")) { + await sendDeeplinkMessage(conversation, process.env.AGENT_ADDRESS); +} +``` + +## Advanced Implementation Patterns + +### Context-Aware Deeplinks + +Create deeplinks that carry context about the conversation or user intent: + +```typescript +interface DeeplinkContext { + source: 'group' | 'miniapp' | 'direct'; + action?: string; + gameId?: string; +} + +function createContextualDeeplink(agentAddress: string, context: DeeplinkContext): string { + const baseUrl = `cbwallet://messaging/${agentAddress}`; + + // While the deeplink format doesn't support query params, + // you can encode context in the initial message the agent sends + return baseUrl; +} + +// Agent can detect context and respond accordingly +async function handlePrivateMessage(message: any, conversation: any) { + const senderAddress = message.senderAddress; + + // Check if this is a new conversation from a deeplink + const messageHistory = await conversation.getMessages(); + + if (messageHistory.length <= 1) { + // This is likely from a deeplink - provide context-specific onboarding + await conversation.send( + "👋 Welcome to our private chat! I can help you with:\n\n" + + "• Personal game stats and progress\n" + + "• Custom strategy recommendations\n" + + "• Private trading insights\n\n" + + "What would you like to explore?" + ); + } +} +``` + +### Multi-Agent Coordination + +When working with multiple agents, create clear deeplinks for each: + +```typescript +const agentDeeplinks = { + trading: "cbwallet://messaging/0x1234...trading", + gaming: "cbwallet://messaging/0x5678...gaming", + social: "cbwallet://messaging/0x9abc...social" +}; + +function createAgentMenu() { + return ` +🤖 **Connect with our specialized agents:** + +🏦 [Trading Bot](${agentDeeplinks.trading}) - Portfolio management & market insights +🎮 [Game Master](${agentDeeplinks.gaming}) - Competitions & leaderboards +👥 [Social Hub](${agentDeeplinks.social}) - Community events & networking + +Each agent specializes in their domain for the best experience! + `; +} +``` + +## Best Practices + +### Validation and Error Handling + +Always validate content structures and provide robust error handling: + +```typescript +function validateAgentAddress(address: string): boolean { + // Check if it's a valid Ethereum address + const ethAddressRegex = /^0x[a-fA-F0-9]{40}$/; + return ethAddressRegex.test(address); +} + +function createSafeDeeplink(address: string): string | null { + if (!validateAgentAddress(address)) { + console.error('Invalid agent address:', address); + return null; + } + + return `cbwallet://messaging/${address}`; +} + +// Usage with validation +const deeplink = createSafeDeeplink(agentAddress); +if (deeplink) { + window.location.href = deeplink; +} else { + // Show error message or fallback + alert('Unable to open chat. Please try again.'); +} +``` + +### Fallbacks for Unsupported Clients + +Provide fallbacks for clients that don't support deeplinks: + +```typescript +function openAgentChat(agentAddress: string) { + const deeplink = `cbwallet://messaging/${agentAddress}`; + + // Try deeplink first + try { + window.location.href = deeplink; + + // Set a timeout to check if deeplink worked + setTimeout(() => { + // If user is still on the page, deeplink likely failed + if (document.hasFocus()) { + showFallbackOptions(agentAddress); + } + }, 2000); + + } catch (error) { + showFallbackOptions(agentAddress); + } +} + +function showFallbackOptions(agentAddress: string) { + // Show alternative options + const fallbackMessage = ` + 🔗 **Can't open direct chat?** + + Alternative ways to connect: + • Copy agent address: ${agentAddress} + • Search for the agent in your messaging app + • Join our community chat for support + `; + + // Display in modal or notification + showNotification(fallbackMessage); +} +``` + +### Testing Across Client Versions + +Test your deeplinks across different client versions and environments: + +```typescript +interface TestEnvironment { + userAgent: string; + isBaseApp: boolean; + supportsDeeplinks: boolean; +} + +function detectEnvironment(): TestEnvironment { + const userAgent = navigator.userAgent; + + return { + userAgent, + isBaseApp: userAgent.includes('BaseApp') || userAgent.includes('cbwallet'), + supportsDeeplinks: 'protocol' in window.location || userAgent.includes('Mobile') + }; +} + +function adaptDeeplinkForEnvironment(agentAddress: string): string { + const env = detectEnvironment(); + + if (env.isBaseApp) { + return `cbwallet://messaging/${agentAddress}`; + } else if (env.supportsDeeplinks) { + // Generic fallback + return `https://base.org/chat/${agentAddress}`; + } else { + // Web fallback + return `https://base.org/agents/${agentAddress}`; + } +} +``` + +## Message and Metadata Limits + +### Size Limitations + +Keep your deeplink messages concise and within platform limits: + +```typescript +const MAX_MESSAGE_LENGTH = 1000; // Adjust based on platform limits + +function createDeeplinkMessage(agentAddress: string, description: string): string { + const deeplink = `cbwallet://messaging/${agentAddress}`; + const message = `${description}\n\n${deeplink}`; + + if (message.length > MAX_MESSAGE_LENGTH) { + // Truncate description to fit + const availableLength = MAX_MESSAGE_LENGTH - deeplink.length - 4; // 4 for "\n\n" + const truncatedDescription = description.slice(0, availableLength - 3) + '...'; + return `${truncatedDescription}\n\n${deeplink}`; + } + + return message; +} +``` + +### Metadata Best Practices + +When sending deeplinks, include appropriate metadata: + +```typescript +async function sendDeeplinkWithMetadata(conversation: any, agentAddress: string) { + const message = createDeeplinkMessage( + agentAddress, + "💬 Continue our conversation privately for personalized assistance" + ); + + // Include metadata if supported by the client + const metadata = { + type: 'deeplink', + target: 'private_chat', + agent: agentAddress, + timestamp: Date.now() + }; + + await conversation.send(message, metadata); +} +``` + +## XIP-67 Compliance + +Ensure your deeplink implementation follows [XIP-67](https://github.com/xmtp/XIPs/blob/main/XIPs/xip-67.md) standards: + +### Protocol Requirements + +- Use proper URL scheme formatting +- Validate address formats before creating deeplinks +- Handle protocol registration appropriately +- Provide clear error messages for unsupported operations + +### Security Considerations + +```typescript +function secureDeeplinkHandler(url: string): boolean { + // Validate the deeplink format + const deeplinkRegex = /^cbwallet:\/\/messaging\/0x[a-fA-F0-9]{40}$/; + + if (!deeplinkRegex.test(url)) { + console.warn('Invalid deeplink format:', url); + return false; + } + + // Additional security checks + const address = url.split('/').pop(); + if (!validateAgentAddress(address!)) { + console.warn('Invalid agent address in deeplink:', address); + return false; + } + + return true; +} +``` + +## Implementation Checklist + + + +- [ ] Validate agent addresses before creating deeplinks +- [ ] Plan fallback experiences for unsupported clients +- [ ] Design error handling for failed deeplink navigation +- [ ] Test message length limits with your content + + + +- [ ] Implement proper URL validation +- [ ] Add loading states for better UX +- [ ] Create reusable components for consistency +- [ ] Include appropriate metadata where supported + + + +- [ ] Test across different client versions +- [ ] Verify fallback behavior works correctly +- [ ] Confirm message limits are respected +- [ ] Validate XIP-67 compliance + + + +- [ ] Monitor deeplink success rates +- [ ] Track user engagement through deeplinks +- [ ] Gather feedback on user experience +- [ ] Iterate based on usage patterns + + + +## Common Patterns + +### Agent-to-User Private Invitation + +```typescript +// In group chat, agent invites user to private conversation +if (message.content.includes("@agentname help")) { + const userAddress = message.senderAddress; + await conversation.send( + `👋 Hi there! For personalized assistance, let's chat privately:\n\n` + + `cbwallet://messaging/${process.env.AGENT_ADDRESS}\n\n` + + `I can provide detailed guidance tailored to your needs!` + ); +} +``` + +### Mini App Integration + +```typescript +// From Mini App, create deeplink to agent +function createAgentSupportLink(agentAddress: string, gameId: string) { + const deeplink = `cbwallet://messaging/${agentAddress}`; + + return ( +
+

Need help with this game?

+ + 💬 Chat with Game Master + +
+ ); +} +``` + +By following these patterns and best practices, you'll create seamless deeplink experiences that enhance user engagement and provide clear paths for continued interaction with your agents. diff --git a/docs/base-app/agents/getting-featured.mdx b/docs/base-app/agents/getting-featured.mdx new file mode 100644 index 000000000..32f708754 --- /dev/null +++ b/docs/base-app/agents/getting-featured.mdx @@ -0,0 +1,11 @@ +--- +title: 'Getting Your Agent Featured' +description: 'Learn how to submit your chat agent for review and get featured in Base App' +sidebarTitle: 'Getting Featured' +--- + +## Getting featured + +Fill out the form [here](https://app.deform.cc/form/52b71db4-bfa2-4ef5-a954-76c66250bdd2/?page_number=0) to submit your agent for review. Note, agents often go through multiple rounds of feedback before they are ready to be featured. Once approved, your agent will be featured in Base App. You will hear back from us within 5 business days. + +Need help or have feature requests? Visit [https://community.xmtp.org/](https://community.xmtp.org/) diff --git a/docs/base-app/agents/getting-started.mdx b/docs/base-app/agents/getting-started.mdx new file mode 100644 index 000000000..50acfdfcd --- /dev/null +++ b/docs/base-app/agents/getting-started.mdx @@ -0,0 +1,227 @@ +--- +title: 'Getting Started with Chat Agents' +description: 'Step-by-step guide to creating, testing, and deploying your first XMTP messaging agent' +sidebarTitle: 'Getting Started' +--- + +This guide will walk you through creating, testing, and deploying your first XMTP messaging agent. By the end, you'll have a fully functional agent that can send and receive messages on the XMTP messaging network. + +## Prerequisites + +Before you begin, make sure you have: + +- Node.js (v20 or higher) +- Git +- A code editor +- Basic knowledge of JavaScript/TypeScript + +## Helpful Resources + +- [Getting Started with XMTP (Video)](https://www.youtube.com/watch?v=djRLnWUvwIA) +- [Building Agents on XMTP](https://github.com/ephemeraHQ/xmtp-agent-examples) +- [Cursor Rules](https://github.com/ephemeraHQ/xmtp-agent-examples/blob/main/.cursor/rules/xmtp.md) +- [XMTP Documentation](https://docs.xmtp.org/) +- [Faucets](https://portal.cdp.coinbase.com/products/faucet) + +## Development Guide + + + + Clone the XMTP Agent Examples Repository: + + ```bash + git clone https://github.com/ephemeraHQ/xmtp-agent-examples.git + cd xmtp-agent-examples + ``` + + + + + Install all required packages: + + ```bash + npm install + ``` + + + + + Run the key generation script to create your agent's wallet: + + ```bash + npm run gen:keys + ``` + + This creates a .env file with: + + ```bash + WALLET_KEY=0x... # Your agent's private key + ENCRYPTION_KEY=... # Encryption key for local database + XMTP_ENV=dev # Environment (local, dev, production) + ``` + + + + + Create a basic agent that responds to messages: + + ```javascript + // import the xmtp sdk + import { Client, type XmtpEnv, type Signer } from "@xmtp/node-sdk"; + + // encryption key, must be consistent across runs + const encryptionKey: Uint8Array = ...; + const signer: Signer = ...; + const env: XmtpEnv = "dev"; + + // create the client + const client = await Client.create(signer, {encryptionKey, env }); + // sync the client to get the latest messages + await client.conversations.sync(); + + // listen to all messages + const stream = await client.conversations.streamAllMessages(); + for await (const message of stream) { + // ignore messages from the agent + if (message?.senderInboxId === client.inboxId ) continue; + // get the conversation by id + const conversation = await client.conversations.getConversationById(message.conversationId); + // send a message from the agent + await conversation.send("gm"); + } + ``` + + Then run your agent: + + ```bash + npm run dev + ``` + + ### Getting the address of a user + + Each user has a unique inboxId for retrieving their associated addresses (identifiers). One inboxId can have multiple identifiers like passkeys or EVM wallet addresses. + + ```tsx + const inboxState = await client.preferences.inboxStateFromInboxIds([ + message.senderInboxId, + ]); + const addressFromInboxId = inboxState[0].identifiers[0].identifier; + ``` + + ### Example Implementations + + Explore these examples in the `/examples` folder: + + - [xmtp-gm](https://github.com/ephemeraHQ/xmtp-agent-examples/tree/main/examples/xmtp-gm): A simple agent that replies to all text messages with "gm" + - [xmtp-gpt](https://github.com/ephemeraHQ/xmtp-agent-examples/tree/main/examples/xmtp-gpt): An example using GPT API's to answer messages + - [xmtp-nft-gated-group](https://github.com/ephemeraHQ/xmtp-agent-examples/tree/main/examples/xmtp-nft-gated-group): Add members to a group based on an NFT + - [xmtp-coinbase-agentkit](https://github.com/ephemeraHQ/xmtp-agent-examples/tree/main/examples/xmtp-coinbase-agentkit): Agent that uses a CDP for gassless USDC on base + - [xmtp-transactions](https://github.com/ephemeraHQ/xmtp-agent-examples/tree/main/examples/xmtp-transactions): Use XMTP content types to send transactions + - [xmtp-smart-wallet](https://github.com/ephemeraHQ/xmtp-agent-examples/tree/main/examples/xmtp-smart-wallet): Agent that uses a smart wallet to send messages + + + + + ### Development Testing + + 1. Start your agent: + ```javascript + npm run dev + ``` + + 2. Test on [xmtp.chat](https://xmtp.chat/conversations): + - Go to https://xmtp.chat + - Connect your personal wallet + - Switch to Dev environment in settings + - Start a new conversation with your agent's public address (from .env) + - Send a test message and verify the agent responds + + ### Production Testing + + 1. Update environment: + ```javascript + XMTP_ENV = production; + ``` + + 2. Test on Base App: + - Open Base App mobile app + - Go to messaging + - Start conversation with your agent's address + - Verify functionality + + + + + Give your agent a human-readable name: + + 1. **Import agent wallet to Base App extension:** + - Install Base App browser extension + - Import using your agent's private key + + 2. **Purchase a basename:** + - Visit https://base.org/names + - Connect your agent's wallet + - Search and purchase your desired basename (e.g., myagent.base.eth) + - Set as primary name + + 3. **Verify setup:** + - Your agent can now be reached via the basename instead of the long address + - Users can message myagent.base.eth instead of 0x123... + + + + + ### Option 1: Railway (Recommended) + + - Visit https://railway.app + - Connect your GitHub repository + - Add environment variables in Railway dashboard: + - XMTP_ENV=production + - WALLET_KEY=your_agent_private_key + - ENCRYPTION_KEY=your_agent_encryption_key + - Deploy and monitor logs + + ### Option 2: Other Platforms + + Heroku, Vercel, or any Node.js hosting platform: + - Ensure your package.json has the correct start script + - Set environment variables in your hosting platform + - Deploy your repository + + + + + ### Best Practices + + 1. Logging: Add comprehensive logging to track agent activity + 2. Error Handling: Implement try-catch blocks for network issues + 3. Rate Limiting: Respect XMTP rate limits in your agent logic + 4. Security: Never expose private keys; use environment variables + + ### Monitoring + + Add to your agent for basic monitoring: + + ```javascript + const inboxState = await client.preferences.inboxState(); + const address = inboxState.identifiers[0].identifier; + const inboxId = client.inboxId; + const installationId = client.installationId; + const conversations = await client.conversations.list(); + + console.log(` + ✓ XMTP Client: + • InboxId: ${inboxId} + • Address: ${address} + • Conversations: ${conversations.length} + • Installations: ${inboxState.installations.length} + • InstallationId: ${installationId} + • Network: ${process.env.XMTP_ENV}`); + + console.log(`Agent started. Address: ${xmtp.address}`) + console.log(`Environment: ${process.env.XMTP_ENV}`) + console.log(`Listening for messages...`) + ``` + + + diff --git a/docs/base-app/agents/mini-apps-and-agents.mdx b/docs/base-app/agents/mini-apps-and-agents.mdx new file mode 100644 index 000000000..da57c21a7 --- /dev/null +++ b/docs/base-app/agents/mini-apps-and-agents.mdx @@ -0,0 +1,348 @@ +--- +title: 'Mini Apps & Agents' +description: 'Learn how to integrate Mini Apps with chat agents for seamless in-conversation experiences that drive engagement and virality.' +sidebarTitle: 'Mini Apps & Agents' +--- + +> **What you'll learn** +> By the end of this guide, you'll be able to: +> - Integrate Mini Apps with chat agents for seamless in-conversation experiences +> - Share Mini Apps directly through agent messages with rich previews +> - Create engaging group experiences using agents and Mini Apps together +> - Implement user mentions and social features in agent-driven Mini App experiences +> - Build viral, conversational Mini App distribution through chat + +## Why Mini Apps & Agents Together? + +Mini Apps can live inside conversations, letting friends play, trade, plan, and coordinate together without ever leaving the chat. + +This means that you can have virality spread through natural conversation ("try this right here") along with sharing in larger settings. When combined with intelligent agents, you create powerful experiences that spread through natural conversation. + +This integration unlocks unique distribution and engagement patterns: + +**Natural virality:** "Try this right here" moments spread organically through conversation +**Contextual engagement:** Agents can introduce Mini Apps at the perfect moment +**Group coordination:** Agents orchestrate multiplayer experiences and keep everyone engaged +**Persistent presence:** Agents maintain engagement between Mini App sessions + +**Real Examples:** + +• **Group Gaming:** Agent coordinates a multiplayer quiz, announces winners, and keeps score across sessions +• **Event Planning:** Agent shares planning Mini App when someone mentions meeting up, handles RSVPs and updates +• **Trading Competitions:** Agent creates trading challenges, shares leaderboards, and celebrates wins +• **Social Polls:** Agent launches polls when decisions need to be made, tallies results, and announces outcomes + +## How Integration Works + +### 1. Share Mini Apps in App + +Every Mini App has a shareable URL that agents can send directly in chat. When dropped into a conversation, the link automatically unfurls into a rich preview card that others can tap to launch instantly. + + +**Share Flow:** +1. **Agent triggers share** — Based on conversation context or user request +2. **Link generates preview** — Platform fetches Mini App metadata and creates rich embed +3. **Users engage** — Tap preview to launch Mini App directly in conversation +4. **Agent coordinates** — Continues engagement, shares updates, mentions participants + +### Technical Implementation + +**Basic Mini App Sharing:** + +```typescript +import { Client } from "@xmtp/node-sdk"; + +// Share a Mini App in response to a trigger +async function shareMiniApp(conversation, appUrl, description) { + await conversation.send(`${description}\n\n${appUrl}`); +} + +// Example usage +await shareMiniApp( + conversation, + "https://your-miniapp.com/quiz", + "🎯 Ready for a quick quiz challenge?" +); +``` + +**Advanced Integration with Context:** + +```typescript +// Agent detects conversation context and shares relevant Mini App +async function handleMessage(message, conversation) { + const text = message.content.toLowerCase(); + + if (text.includes("game") || text.includes("play")) { + await shareMiniApp( + conversation, + "https://your-miniapp.com/games", + "🎮 Let's play! Choose your game:" + ); + } else if (text.includes("vote") || text.includes("decide")) { + await shareMiniApp( + conversation, + "https://your-miniapp.com/poll", + "🗳️ Let's settle this with a poll:" + ); + } +} +``` + +## User Engagement & Mentions + +### 2. Engage & Mention Users + +Agents on XMTP only have access to the 0x addresses. If you're building a group chat experience with Mini Apps, you'll want to use human-readable mentions like display names (like @jesse) for a more social, intuitive experience. + +This API from Neynar will give your agent access to this data: + +**Getting Display Names:** + +```typescript +import { NeynarAPIClient } from "@neynar/nodejs-sdk"; + +const neynar = new NeynarAPIClient("YOUR_API_KEY"); + +async function getDisplayName(address) { + try { + const users = await neynar.lookupUserByVerification(address); + return users.result.users[0]?.display_name || address.slice(0, 8); + } catch (error) { + return address.slice(0, 8); // Fallback to truncated address + } +} + +// Usage in agent messages +async function announceWinner(conversation, winnerAddress, gameType) { + const displayName = await getDisplayName(winnerAddress); + await conversation.send(`🏆 @${displayName} wins the ${gameType}! Amazing job!`); +} +``` + +### Creating Social Group Experiences + +**Example: Multiplayer Game Coordination** + +```typescript +class GameAgent { + private activeGames = new Map(); + + async handleGameMessage(message, conversation) { + const gameId = conversation.id; + + if (message.content.includes("/start")) { + await this.startGame(conversation, gameId); + } else if (message.content.includes("/join")) { + await this.joinGame(conversation, gameId, message.senderAddress); + } else if (message.content.includes("/results")) { + await this.shareResults(conversation, gameId); + } + } + + async startGame(conversation, gameId) { + const gameUrl = `https://your-miniapp.com/game/${gameId}`; + this.activeGames.set(gameId, { players: [], started: Date.now() }); + + await conversation.send( + `🎮 New game starting! Tap to join:\n\n${gameUrl}\n\nType /join to register` + ); + } + + async joinGame(conversation, gameId, playerAddress) { + const game = this.activeGames.get(gameId); + if (!game.players.includes(playerAddress)) { + game.players.push(playerAddress); + + const displayName = await getDisplayName(playerAddress); + await conversation.send(`✅ @${displayName} joined the game! ${game.players.length} players ready`); + } + } + + async shareResults(conversation, gameId) { + const game = this.activeGames.get(gameId); + const results = await fetchGameResults(gameId); // Your game API + + let message = "🏆 Game Results:\n\n"; + for (let i = 0; i < results.length; i++) { + const displayName = await getDisplayName(results[i].address); + message += `${i + 1}. @${displayName} - ${results[i].score} points\n`; + } + + await conversation.send(message); + } +} +``` + +## Agent Spotlight + +[Squabble](https://github.com/builders-garden/squabble) uses Mini Apps in a social, fun way, with the agent coordinating the multiplayer experience. Check out their GitHub repo for guidance! + +The agent sends a Mini App to the group, and broadcasts to the group updates as people join and compete in the game. They mention the usernames of the people who have joined the game and won the game. The game happens inside the Mini App, which provides a more interactive, visual experience. + +### How Squabble Works + + + + Agent detects gaming context in conversation and shares the Squabble Mini App URL, which unfurls as a rich preview card. + + + + Users tap the preview to launch the Mini App, join the game, and start playing the interactive word game. + + + + Agent monitors game progress and broadcasts updates to the group as players join and compete. + + + + Agent mentions winners by username, shares final scores, and encourages replay for ongoing engagement. + + + +**Key Features:** +- **Rich mentions** — Agent uses display names (@username) instead of wallet addresses +- **Real-time updates** — Broadcasts join notifications and game progress to maintain group engagement +- **Social celebration** — Announces winners and achievements to drive continued participation +- **Contextual triggers** — Responds to gaming-related conversation with relevant Mini App shares + +## Content Types for Mini App Integration + +### Quick Actions for Mini App Engagement + +Use Quick Actions to create interactive buttons that guide users to your Mini Apps: + +```typescript +// Quick Actions for Mini App discovery +const gameActions = { + id: "game_selection", + description: "Choose your game mode:", + actions: [ + { + id: "solo_game", + label: "Solo Challenge", + style: "primary" + }, + { + id: "group_game", + label: "Group Battle", + style: "secondary" + }, + { + id: "tournament", + label: "Join Tournament", + style: "danger" + } + ] +}; + +await conversation.send("🎮 Ready to play?", gameActions); +``` + +### Transaction Integration + +Combine Mini Apps with transaction capabilities for seamless onchain experiences: + +```typescript +// Example: In-game purchase or reward distribution +const rewardTransaction = { + version: "1.0", + from: agentAddress, + chainId: 8453, // Base + calls: [{ + to: rewardContractAddress, + data: "0x...", // Encoded function call + value: "0" + }], + metadata: { + description: "Claim your game rewards", + hostname: "your-miniapp.com", + faviconUrl: "https://your-miniapp.com/favicon.ico", + title: "Game Rewards" + } +}; +``` + +## Best Practices + +### Agent Behavior + + + +- **Smart triggers** — Share Mini Apps when conversation context suggests engagement opportunity +- **Avoid spam** — Don't share the same Mini App repeatedly in short timeframes +- **Read the room** — Gauge group interest before introducing games or activities +- **Natural integration** — Make Mini App shares feel like helpful suggestions, not advertisements + + + +- **Direct messages** — Automatically share relevant Mini Apps based on user requests +- **Group messages** — Only share Mini Apps when mentioned (@agentname) or when replying to agent messages +- **React to acknowledge** — Use emoji reactions (👀, ⌛) to show message received while processing +- **Coordinate experience** — In groups, act as facilitator for multiplayer Mini App experiences + + + +- **Use display names** — Always resolve addresses to human-readable names for mentions +- **Celebrate achievements** — Announce wins, completions, and milestones to drive engagement +- **Maintain context** — Remember ongoing games/activities and provide relevant updates +- **Enable discovery** — Suggest related Mini Apps based on user interests and behavior + + + +### Technical Implementation + +- **Error handling** — Gracefully handle Mini App load failures or API timeouts +- **Rate limiting** — Respect XMTP message limits and avoid overwhelming conversations +- **State management** — Track active Mini App sessions and user participation across conversations +- **Privacy** — Only access necessary user data and respect privacy preferences + +## Implementation Guide + +### Setting Up Mini App Integration + + + + Set up your agent with a catalog of Mini App URLs and their associated triggers or contexts. + + ```typescript + const miniApps = { + games: "https://your-miniapp.com/games", + polls: "https://your-miniapp.com/polls", + trading: "https://your-miniapp.com/trading", + events: "https://your-miniapp.com/events" + }; + ``` + + + + Create logic to detect when sharing a Mini App would be valuable based on conversation content. + + ```typescript + function detectMiniAppContext(message) { + const content = message.content.toLowerCase(); + if (content.includes("game") || content.includes("play")) return "games"; + if (content.includes("vote") || content.includes("poll")) return "polls"; + return null; + } + ``` + + + + Integrate with Neynar API or similar service to resolve addresses to display names for better UX. + + + + Verify Mini App previews render correctly and agent coordination works across different conversation types. + + + +### Next Steps + +Ready to build your Mini App & Agent integration? + +- **Study the [Squabble example](https://github.com/builders-garden/squabble)** — See real implementation patterns +- **Review [Chat Agents guide](/base-app/agents/chat-agents)** — Understand XMTP agent fundamentals +- **Explore [Mini Apps documentation](/base-app/miniapps/mini-apps)** — Learn Mini App development +- **Join [XMTP community](https://community.xmtp.org/)** — Get help and share your builds + +By combining Mini Apps with intelligent agents, you create engaging, viral experiences that spread naturally through conversation while providing rich, interactive functionality that keeps users coming back. diff --git a/docs/base-app/agents/transaction-trays.mdx b/docs/base-app/agents/transaction-trays.mdx new file mode 100644 index 000000000..24f9010ba --- /dev/null +++ b/docs/base-app/agents/transaction-trays.mdx @@ -0,0 +1,39 @@ +--- +title: 'Custom Transaction Trays' +description: 'Learn how to customize transaction trays with agent branding and information' +sidebarTitle: 'Transaction Trays' +--- + +## Custom Transaction Trays + +If you would like to display agent information such as favicon and branded title, utilize the metadata property of wallet send calls data. + +#### **Example: Display agent information** + +```ts +// example of wallet send calls data shape{ + version: "1.0", + from: request.from as `0x${string}`, + chainId: this.networkConfig.chainId, + calls: [ + { + ... + metadata: { + description: "Transfer token", + ...other fields, + hostname: "tba.chat", + faviconUrl: "https://tba.chat/favicon", + title: "Your favorite Agent" + } + } + ] +} +``` + + + Transaction tray + + +You can send "GM" to **tbachat.base.eth** to get more details about message content types we support and get the firsthand experience on by interacting with our agent + +Our agent repo is found [here](https://github.com/siwan-cb/tba-chat-example-bot) diff --git a/docs/base-app/agents/why-agents.mdx b/docs/base-app/agents/why-agents.mdx new file mode 100644 index 000000000..87d545e08 --- /dev/null +++ b/docs/base-app/agents/why-agents.mdx @@ -0,0 +1,19 @@ +--- +title: 'Why Build Chat Agents?' +description: 'Discover the power of messaging agents and their potential impact on Base App' +sidebarTitle: 'Why Agents?' +--- + +## Why agents? + +Messaging is the largest use-case in the world, but it's more than just conversations—it's a secure, programmable channel for financial and social innovation. When combined with the onchain capabilities of Base App, builders have a new surface area to build 10X better messaging experiences not currently possible on legacy platforms like WhatsApp or Messenger. + +Real Examples: + +• Smart Payment Assistant: Text "split dinner $200 4 ways" and everyone gets paid instantly with sub-cent fees, no app switching or Venmo delays. + +• AI Trading Companion: Message "buy $100 ETH when it hits $3,000" and your agent executes trades 24/7 while you sleep. + +• Travel Planning Agent: "Book flight LAX to NYC under $300" and get instant booking with crypto payments, all in your group chat + +• Base App & XMTP are combining AI, crypto, and mini apps with secure messaging – to unlock use-cases never before possible. Secure group chats & DMs are the new surface area for developers. diff --git a/docs/base-app/agents/x402-agents.mdx b/docs/base-app/agents/x402-agents.mdx new file mode 100644 index 000000000..46d83e8a1 --- /dev/null +++ b/docs/base-app/agents/x402-agents.mdx @@ -0,0 +1,448 @@ +--- +title: 'Building Autonomous Payment Agents with x402' +description: Learn how to integrate x402 payment protocol with XMTP chat agents to enable autonomous economic transactions +sidebarTitle: 'x402 & Agents' +--- + +x402 is a revolutionary open payment protocol developed by Coinbase that enables instant, automatic stablecoin payments directly over HTTP. It empowers AI agents to autonomously pay for services without subscriptions or API keys, unlocking truly autonomous, monetized chat agents capable of reasoning, transacting, and delivering value seamlessly. + +## What is x402? + +x402 transforms how agents interact with paid services by embedding payments directly into HTTP requests. Instead of managing API keys or subscriptions, your agents can pay for exactly what they use, when they use it. + +### Key Benefits + +- **Autonomous economic transactions** - Agents can transact without human intervention +- **Pay-as-you-go monetization** - Only pay for the services you actually use +- **Minimal setup** - Often requires just one middleware line +- **Instant settlement** - Payments are verified on-chain in real-time + +## Protocol Flow + +Understanding the x402 payment flow is essential for building autonomous agents: + + + + Client requests access to a protected resource. + + + + Server returns HTTP 402 status with payment details including: + - Payment amount + - Recipient address + - Payment reference + + + + Client sends payment payload via HTTP header (e.g., `X-PAYMENT`). + + + The payment facilitator handles the on-chain settlement verification automatically. + + + + + Client retries the original request, and server responds with: + - Requested content + - Payment confirmation header (e.g., `X-PAYMENT-RESPONSE`) + + + +## Integrating x402 with XMTP Agents + +Combining x402 with XMTP chat agents creates powerful autonomous economic agents. Here's how to implement this integration: + +### Agent Architecture + +Your agent needs to handle three main scenarios: + +1. **Free requests** - Standard queries that don't require payment +2. **Payment-gated requests** - Premium features that trigger x402 payments +3. **Payment failures** - Graceful handling when payments fail + +### Basic Implementation + +Here's how your agent handles payment-gated requests: + +```typescript +// Agent receives user request for premium feature +async function handlePremiumRequest(message: string, conversation: any) { + try { + // Attempt to access premium service + const response = await fetch("/premium-api/nft-floor-price", { + method: "GET", + headers: { + "Content-Type": "application/json" + } + }); + + // Check if payment is required + if (response.status === 402) { + const paymentDetails = await response.json(); + + // Notify user about payment requirement + await conversation.send(`💰 This requires a payment of ${paymentDetails.amount} USDC. Processing...`); + + // Execute payment + const paymentPayload = await executePayment(paymentDetails); + + // Retry request with payment + const retryResponse = await fetch("/premium-api/nft-floor-price", { + headers: { + "X-PAYMENT": paymentPayload, + "Content-Type": "application/json" + } + }); + + if (retryResponse.ok) { + const data = await retryResponse.json(); + await conversation.send(`📊 NFT floor price: ${data.floorPrice} ETH`); + } else { + await conversation.send("❌ Payment processed but service unavailable. Please try again."); + } + } else if (response.ok) { + // Handle free-tier response + const data = await response.json(); + await conversation.send(`📊 NFT floor price: ${data.floorPrice} ETH`); + } + } catch (error) { + await conversation.send("❌ Unable to fetch data. Please try again later."); + } +} +``` + +### Payment Execution Function + +Implement the payment logic using Coinbase's x402 SDK: + +```typescript +import { PaymentFacilitator } from '@coinbase/x402-sdk'; + +async function executePayment(paymentDetails: any): Promise { + const facilitator = new PaymentFacilitator({ + privateKey: process.env.AGENT_PRIVATE_KEY, + network: 'base' // or 'mainnet' + }); + + try { + const payment = await facilitator.createPayment({ + amount: paymentDetails.amount, + recipient: paymentDetails.recipient, + reference: paymentDetails.reference, + currency: 'USDC' + }); + + return payment.payload; + } catch (error) { + throw new Error(`Payment failed: ${error.message}`); + } +} +``` + +### Complete Agent Example + +Here's a full implementation combining XMTP with x402: + + +```typescript Complete Agent +import { Client } from "@xmtp/node-sdk"; +import { PaymentFacilitator } from '@coinbase/x402-sdk'; + +class AutonomousPaymentAgent { + private client: Client; + private facilitator: PaymentFacilitator; + + constructor(client: Client, privateKey: string) { + this.client = client; + this.facilitator = new PaymentFacilitator({ + privateKey, + network: process.env.NETWORK || 'base' + }); + } + + async handleMessage(message: any) { + const conversation = await this.client.conversations.getConversationById( + message.conversationId + ); + + const content = message.content; + + // Route messages based on content + if (content.includes('floor price')) { + await this.handleFloorPriceRequest(conversation, content); + } else if (content.includes('market data')) { + await this.handleMarketDataRequest(conversation, content); + } else { + await conversation.send("Hi! I can help with:\n• NFT floor prices\n• Market data\n\nJust ask!"); + } + } + + async handleFloorPriceRequest(conversation: any, content: string) { + // Extract collection name from message + const collection = this.extractCollection(content); + + if (!collection) { + await conversation.send("Please specify an NFT collection name."); + return; + } + + try { + const response = await fetch(`/api/nft-floor/${collection}`); + + if (response.status === 402) { + await this.processPaymentAndRetry( + conversation, + `/api/nft-floor/${collection}`, + `📊 Floor price for ${collection}:` + ); + } else if (response.ok) { + const data = await response.json(); + await conversation.send(`📊 ${collection} floor price: ${data.floorPrice} ETH`); + } + } catch (error) { + await conversation.send("❌ Unable to fetch floor price data."); + } + } + + async processPaymentAndRetry(conversation: any, endpoint: string, successPrefix: string) { + try { + // Initial request to get payment details + const response = await fetch(endpoint); + const paymentDetails = await response.json(); + + // Notify user of payment + await conversation.send(`💰 Payment required: ${paymentDetails.amount} USDC. Processing...`); + + // Execute payment + const payment = await this.facilitator.createPayment({ + amount: paymentDetails.amount, + recipient: paymentDetails.recipient, + reference: paymentDetails.reference, + currency: 'USDC' + }); + + // Retry with payment + const retryResponse = await fetch(endpoint, { + headers: { "X-PAYMENT": payment.payload } + }); + + if (retryResponse.ok) { + const data = await retryResponse.json(); + await conversation.send(`${successPrefix} ${JSON.stringify(data, null, 2)}`); + } else { + await conversation.send("❌ Payment processed but service error occurred."); + } + } catch (error) { + await conversation.send(`❌ Payment failed: ${error.message}`); + } + } + + private extractCollection(content: string): string | null { + // Simple extraction logic - enhance based on your needs + const words = content.toLowerCase().split(' '); + const collectionIndex = words.findIndex(word => + ['cryptopunks', 'bayc', 'azuki', 'pudgypenguins'].includes(word) + ); + return collectionIndex !== -1 ? words[collectionIndex] : null; + } +} +``` + +```typescript Server Setup +import express from 'express'; +import { paymentMiddleware } from '@coinbase/x402-middleware'; + +const app = express(); + +// Apply x402 middleware +app.use(paymentMiddleware(process.env.PAYMENT_ADDRESS, { + "/api/nft-floor/*": "$0.01", + "/api/market-data/*": "$0.005" +})); + +// Protected endpoints +app.get('/api/nft-floor/:collection', (req, res) => { + // This endpoint is now payment-gated + const mockData = { + collection: req.params.collection, + floorPrice: (Math.random() * 10).toFixed(3), + currency: 'ETH', + timestamp: new Date().toISOString() + }; + + res.json(mockData); +}); + +app.get('/api/market-data/:symbol', (req, res) => { + const mockData = { + symbol: req.params.symbol, + price: (Math.random() * 1000).toFixed(2), + change24h: ((Math.random() - 0.5) * 20).toFixed(2), + volume: Math.floor(Math.random() * 1000000) + }; + + res.json(mockData); +}); + +app.listen(3000, () => { + console.log('x402-enabled API server running on port 3000'); +}); +``` + + +## Real-World Examples + +Several projects have successfully implemented x402 with XMTP agents: + +### Stableburn x402 + +A live implementation showcasing: +- XMTP v4 integration +- AgentKit functionality +- x402-based micro-payments ($0.001–$0.005 per request) +- Automatic revenue burning via on-chain token swaps + + + Autonomous agent that monetizes premium endpoints with micro-payments + + +### Crossmint Worldstore Agent + +An XMTP chat agent for shopping featuring: +- x402 payment integration +- EIP-3009 gasless USDC transactions +- Base network integration +- Interactive content types in chat + + + Shopping agent with embedded x402 payments and commerce functionality + + +## Best Practices + +### Payment UX Guidelines + + +Always inform users about payment requirements before executing transactions. Transparency builds trust with your agent. + + +- **Clear pricing** - Display exact costs upfront +- **Payment confirmation** - Confirm successful payments +- **Graceful failures** - Handle payment errors elegantly +- **Value communication** - Explain what users get for their payment + +### Error Handling + +Implement comprehensive error handling for common scenarios: + +```typescript +async function robustPaymentHandler(conversation: any, endpoint: string) { + try { + const response = await fetch(endpoint); + + if (response.status === 402) { + const paymentDetails = await response.json(); + + // Validate payment details + if (!paymentDetails.amount || !paymentDetails.recipient) { + await conversation.send("❌ Invalid payment request from service."); + return; + } + + // Check if amount is reasonable + if (parseFloat(paymentDetails.amount) > 1.0) { + await conversation.send(`⚠️ High payment amount: ${paymentDetails.amount} USDC. Skipping for safety.`); + return; + } + + // Process payment with timeout + const paymentPromise = this.executePayment(paymentDetails); + const timeoutPromise = new Promise((_, reject) => + setTimeout(() => reject(new Error('Payment timeout')), 30000) + ); + + const payment = await Promise.race([paymentPromise, timeoutPromise]); + + // Retry with payment + const retryResponse = await fetch(endpoint, { + headers: { "X-PAYMENT": payment } + }); + + if (retryResponse.ok) { + const data = await retryResponse.json(); + await conversation.send(`✅ Payment successful! ${JSON.stringify(data)}`); + } else { + await conversation.send("❌ Payment processed but service unavailable."); + } + } + } catch (error) { + if (error.message.includes('timeout')) { + await conversation.send("⏱️ Payment timed out. Please try again."); + } else if (error.message.includes('insufficient funds')) { + await conversation.send("💸 Insufficient funds for payment."); + } else { + await conversation.send(`❌ Payment error: ${error.message}`); + } + } +} +``` + +### Security Considerations + + +Never expose private keys in your code. Always use environment variables and secure key management practices. + + +- **Key management** - Use secure environment variables +- **Payment limits** - Set maximum payment thresholds +- **Rate limiting** - Prevent payment spam attacks +- **Audit trails** - Log all payment activities +- **Network validation** - Verify on-chain settlement + +## Resources + + + + Complete x402 protocol specification and implementation guide + + + + Official x402 protocol website with examples and tools + + + + Official Coinbase x402 SDK for JavaScript/TypeScript + + + + XMTP protocol documentation for messaging integration + + + +## Next Steps + +Ready to build your autonomous payment agent? Here's your roadmap: + + + + Install the x402 SDK and XMTP client libraries in your project. + + + + Start with a simple XMTP agent following our [Chat Agents guide](/base-app/agents/chat-agents). + + + + Integrate x402 payment handling using the examples above. + + + + Test payment flows on testnet before deploying to production. + + + + Deploy your agent and monitor payment transactions and user interactions. + + + +The future of autonomous agents is here. With x402 and XMTP, you can build agents that truly operate independently in the digital economy. diff --git a/docs/wallet-app/introduction/beta-faq.mdx b/docs/base-app/introduction/beta-faq.mdx similarity index 98% rename from docs/wallet-app/introduction/beta-faq.mdx rename to docs/base-app/introduction/beta-faq.mdx index 125d24222..685ac4dd7 100644 --- a/docs/wallet-app/introduction/beta-faq.mdx +++ b/docs/base-app/introduction/beta-faq.mdx @@ -110,7 +110,3 @@ Unfortunately, our invites are one time use. If you uninstall the app, we aren We will announce the official app launch date soon - thanks for being a part of the beta! - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/base-app/llms-full.txt b/docs/base-app/llms-full.txt new file mode 100644 index 000000000..fa6b93e35 --- /dev/null +++ b/docs/base-app/llms-full.txt @@ -0,0 +1,130 @@ +# https://docs.base.org/base-app/llms-full.txt + +## Base App — Deep Guide for LLMs + +> Base App (formerly Coinbase Wallet) is a social, onchain everything app. Builders can create Mini Apps and chat‑based agents that interact with users and safely execute onchain actions. + +### What you can build +- XMTP messaging agents with safe onchain actions (transaction trays) +- Mini Apps discoverable in Base App (via MiniKit and manifests) + - For full context of mini apps, review the [mini apps llms.txt file](../mini-apps/llms.txt) + +## Minimal Critical Code (agents) +```ts +// XMTP client skeleton used across examples +import { Client, type XmtpEnv, type Signer } from '@xmtp/node-sdk' + +const encryptionKey: Uint8Array = /* stable key for history */ +const signer: Signer = /* your wallet signer */ +const env: XmtpEnv = 'dev' + +const client = await Client.create(signer, { encryptionKey, env }) +await client.conversations.sync() +for await (const msg of await client.conversations.streamAllMessages()) { + if (msg?.senderInboxId === client.inboxId) continue + const convo = await client.conversations.getConversationById(msg.conversationId) + await convo.send('gm') +} +``` + +## Navigation (with brief descriptions) + +### Introduction +- [Beta FAQ](./introduction/beta-faq) — Beta scope, access, smart wallet notes + +### Chat Agents +- [Why Agents](./agents/why-agents) — Rationale and examples +- [Getting Started](./agents/getting-started) — Setup and first agent +- [Content Types](./agents/content-types) — Messaging schemas +- [Transaction Trays](./agents/transaction-trays) — Onchain UX in chat +- [Best Practices](./agents/best-practices) — Implementation guidance +- [Getting Featured](./agents/getting-featured) — Distribution checklist + + +## Quickstart (excerpts) + +Source: `https://docs.base.org/base-app/agents/getting-started` + +Create an XMTP agent that can receive messages and reply, then evolve it to surface transaction trays for safe onchain actions. + +Skeleton: + +```ts +import { Client, type XmtpEnv, type Signer } from '@xmtp/node-sdk' + +const signer: Signer = /* your signer */ +const encryptionKey: Uint8Array = /* stable key for history */ +const env: XmtpEnv = 'dev' + +const client = await Client.create(signer, { encryptionKey, env }) +await client.conversations.sync() +for await (const msg of await client.conversations.streamAllMessages()) { + if (msg?.senderInboxId === client.inboxId) continue + const convo = await client.conversations.getConversationById(msg.conversationId) + await convo.send('gm') +} +``` + +Source: `https://docs.base.org/base-app/agents/transaction-trays` + +Transaction trays let you surface predefined, safe actions. Users approve in‑app; your agent never holds keys. + + +## Key Concepts (excerpts) + +Source: `https://docs.base.org/base-app/agents/why-agents` + +- Safety: Agents propose actions; users approve in Base App with a smart wallet. No private keys leave the client. +- UX: Transaction trays standardize onchain actions, reduce phishing, and help users understand intent. +- Content types: Structured messages ensure predictable parsing and rendering across clients. + - Source: `https://docs.base.org/base-app/agents/content-types` +- Distribution: Follow the featuring checklist to maximize discovery. + - Source: `https://docs.base.org/base-app/agents/getting-featured` + + +## API and Schemas (pruned) + +Message content types (subset): +- Text, Button, Form, Transaction Tray + - Source: `https://docs.base.org/base-app/agents/content-types` + +Transaction tray schema (conceptual): + +```json +{ + "type": "transaction_tray", + "title": "Buy", + "actions": [ + { "label": "Confirm purchase", "calls": [{ "to": "0x...", "data": "0x..." }] } + ] +} +``` + + +## Examples (common flows) + +Example: Simple echo agent with guardrails + +Source: `https://docs.base.org/base-app/agents/best-practices` + +```ts +if (!isSupportedContentType(msg)) return +const text = parseText(msg) +if (text.length > MAX_LEN) return convo.send('Message too long') +await convo.send(text) +``` + +Example: Surface a checkout action as a transaction tray + +Source: `https://docs.base.org/base-app/agents/transaction-trays` + +```ts +await convo.send({ + type: 'transaction_tray', + title: 'Checkout', + actions: [ + { label: 'Pay', calls: [{ to: USDC, data: erc20.transfer(MERCHANT, AMOUNT) }] } + ] +}) +``` + diff --git a/docs/base-app/llms.txt b/docs/base-app/llms.txt new file mode 100644 index 000000000..2e89f5ce1 --- /dev/null +++ b/docs/base-app/llms.txt @@ -0,0 +1,20 @@ +# https://docs.base.org/base-app/llms.txt + +## Base App Documentation + +> Base App is a social, onchain everything app (formerly Coinbase Wallet) that lets users discover Mini Apps and chat with AI messaging agents, with built-in smart-wallet transactions. + +## Introduction +- [Beta FAQ](./introduction/beta-faq) — Beta overview, smart wallet behavior, and access + +## Chat Agents +- [Getting Started](./agents/getting-started) — Create, test, and deploy an XMTP agent for Base App +- [Content Types](./agents/content-types) — Message schemas and payload formats for agents +- [Transaction Trays](./agents/transaction-trays) — Surface safe onchain actions in chat +- [Best Practices](./agents/best-practices) — Guidance for robust, user-safe agents + +## Optional +- [Why Agents](./agents/why-agents) — Rationale and examples for messaging agents +- [Getting Featured](./agents/getting-featured) — How to get listed and discovered in Base App + + diff --git a/docs/base-chain/flashblocks/apps.mdx b/docs/base-chain/flashblocks/apps.mdx index 770a3ec67..3a5bbe7d5 100644 --- a/docs/base-chain/flashblocks/apps.mdx +++ b/docs/base-chain/flashblocks/apps.mdx @@ -116,8 +116,106 @@ curl https://sepolia-preconf.base.org -X POST -H "Content-Type: application/json ### Libraries -For popular libraries, they sometimes require additional RPC support for transaction preconfirmation to work properly. -We are working on adding Flashblocks support to the following libraries: +You will need to use a Flashblocks-aware RPC endpoint to use Flashblocks with the following libraries: + +#### [Wagmi](https://wagmi.sh) + +To use Flashblocks with Wagmi, you will need to use the `basePreconf` chain in the Wagmi Config (see `config.ts`). + + + +```tsx Example.tsx +import { useSendTransaction, useWaitForTransactionReceipt } from "wagmi"; + +function SendTransaction() { + const { data: hash, sendTransaction } = useSendTransaction(); + const { data: receipt } = useWaitForTransactionReceipt({ hash }); + + return ( +
+ + {hash &&
Hash: {hash}
} + {receipt &&
Included on block number: {receipt.blockNumber}
} +
+ ) +} +``` + +```tsx App.tsx +import { QueryClient, QueryClientProvider } from '@tanstack/react-query' +import { WagmiProvider, useAccount } from 'wagmi' +import { config } from './config' +import { Example } from './Example' + +const queryClient = new QueryClient() + +function App() { + return ( + + + + + + ) +} +``` + +```tsx config.ts +import { createConfig, http } from "wagmi"; +import { baseSepoliaPreconf } from "wagmi/chains"; +import { baseAccount } from "wagmi/connectors"; + +export const config = createConfig({ + chains: [baseSepoliaPreconf], + connectors: [baseAccount()], + transports: { + [baseSepoliaPreconf.id]: http(), + }, +}); +``` + +
+ +#### [Viem](https://viem.sh) + +```ts +import { createWalletClient, http, parseEther, publicActions } from "viem"; +import { privateKeyToAccount } from "viem/accounts"; +import { baseSepoliaPreconf } from "viem/chains"; + +// Create client with the Flashblocks-aware chain. +const account = privateKeyToAccount(`0x${process.env.PRIVATE_KEY}`); +const client = createWalletClient({ + account, + chain: baseSepoliaPreconf, + transport: http(), +}) + .extend(publicActions); + +const submissionTime = new Date(); +console.log(`Submitting transaction at: ${submissionTime.toISOString()}`); + +// Send transaction. +const hash = await client.sendTransaction({ + to: "0x...", + value: parseEther('0.0001'), +}); +console.log(`Transaction hash: ${hash}`); + +// Wait for transaction to be included. +const receipt = await client.waitForTransactionReceipt({ hash }); +const confirmTime = new Date(); + +console.log(`Transaction included at: ${confirmTime.toISOString()}`); +console.log(`Time difference: ${confirmTime - submissionTime}ms`); +``` #### [Ethers](https://github.com/ethers-io/ethers.js) @@ -150,66 +248,9 @@ We are working on adding Flashblocks support to the following libraries: console.log(`Time difference: ${confirmationTime - submissionTime}ms`); } ``` -You should see the confirmation time significantly lower than the standard RPC endpoint. - -#### [Viem/Wagmi](https://viem.sh) - -```jsx -// Create wallet client - const account = privateKeyToAccount(`0x${process.env.PRIVATE_KEY}`); - const walletClient = createWalletClient({ - account, - chain: { - id: 84532, - name: "base-sepolia", - network: "base-sepolia", - }, - transport: http( - "https://sepolia-preconf.base.org" - ), - }); - - // Create public client for waiting - const publicClient = createPublicClient({ - chain: { - id: 84532, - name: "base-sepolia", - network: "base-sepolia", - }, - transport: http( - "https://sepolia-preconf.base.org" - ), - }); - - try { - const submissionTime = new Date(); - console.log(`Submitting transaction at: ${submissionTime.toISOString()}`); - - // Send transaction - const hash = await walletClient.sendTransaction({ - to: "", - value: parseEther("0.0000001"), - }); - console.log(`Transaction hash: ${hash}`); - - // Wait for transaction to be mined - const receipt = await publicClient.waitForTransactionReceipt({ hash }); - const confirmTime = new Date(); - - console.log(`Transaction mined at: ${confirmTime.toISOString()}`); - console.log(`Time difference: ${confirmTime - submissionTime}ms`); - } catch (error) { - console.error("Error:", error); - } -``` - - +You should see the confirmation time significantly lower than the standard RPC endpoint. ## Support For feedback, support or questions about Flashblocks, please don't hesitate to contact us in the `#developer-chat` channel in the [Base Discord](https://base.org/discord). - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/base-chain/flashblocks/docs.mdx b/docs/base-chain/flashblocks/docs.mdx new file mode 100644 index 000000000..ea212dfdd --- /dev/null +++ b/docs/base-chain/flashblocks/docs.mdx @@ -0,0 +1,76 @@ +--- +title: "Flashblocks FAQ" +--- + +## Flashblocks Block Building + +### Are flashblocks optional? + +All Base blocks are built by the Flashblocks builder, meaning Flashblocks are always live. However, apps may choose not to rely on preconfirmations and are welcome to continue to have an integration that does not rely on Flashblocks. + +### Is there any difference in tx inclusion for flashblocks vs. 2s block? + +No particular differences except more frequent timing, both still pick txs based on tx fees ordering. You can read more about it here (https://docs.base.org/base-chain/network-information/block-building#flashblocks). + +### Is it possible for the sequencer to stop publishing flashblocks, if so what happens? + +The sequencer will not stop publishing flashblocks, unless an extreme circumstance arises, that causes running flashblocks to be unsafe. If this happens, the preconfirmation is disabled across the network and confirmation falls back to 2s blocks. + +### Why is my tx having trouble getting included now? + +It's possible that larger txs (> 15m gas limit) will have a harder time to land. This is due to how the builder allocates gas, it incrementally adds 1/10th of the total block gas limit to each flashblock. You can read more about it here (https://docs.base.org/base-chain/network-information/block-building#flashblocks). + +### How do I ensure my TX is in the first flashblock? + +Unfortunately there's no way to guarantee which flashblock a transaction lands in, similar to how it cannot be guaranteed that a transaction lands in a specific block. To ensure quick inclusion, you may set a higher priority fee on your transaction. + +### How frequently do flashblocks reorg happen? + +Flashblocks reorg currently happens once approx. every ~300 blocks. You can also view the stats by visiting https://www.base.org/stats (the flashblocks section at the bottom). + +### What does it mean when a flashblock is reorged? + +It means when a flashblock was streamed out as preconfirmation but it didn't end up getting included in that particular block. Currently applications building with flashblocks should have this expectation in mind but we are actively working on reducing reorgs to 0. + +## Flashblocks Websockets + +### Why are there 11 flashblocks? + +Index 0 only contains the system txs but doesn't use any gas limit, thus index 1-10 are the actual flashblocks that take pending txs from the txpool to build blocks. + +### Why sometimes there are less than 10 flashblocks? + +This is expected for now, it happens when the previous block takes too long to build the system then compensates by having less time to build the next block, resulting in less flashblocks. + +### What encoding format is the data in transactions? + +Those data are RLP encoded. + +### Why am I getting rate limited? + +We currently set the maximum number of connections to our public websocket. We are aiming to have websocket supported on the RPC nodes soon, thus will soon encourage everyone to connect to the RPC for websocket stream rather than connecting to the websocket proxy directly. + +## RPC + +### Why am I getting rate limited using mainnet-preconf.base.org? + +We set explicit rate limiting on the public endpoint. In order to not get rate limited, we strongly encourage utilizing third party node providers, most of which already support flashblocks aware RPCs on Base today. + +### What RPC methods do you currently support that have flashblocks enabled? + +Currently the ones that have flashblocks enabled are: +- eth_getBlockByNumber (with pending tag) +- eth_getBalance (with pending tag) +- eth_getTransactionReceipt +- eth_getTransactionByHash (with pending tag) +- eth_getTransactionCount (with pending tag) + +### What about eth_call, eth_estimate, etc? + +We will support these soon, ETA at the end of Q3 2025. + +## Node + +### How can I set up a RPC node that's flashblocks-aware? + +In order to set up a node that's flashblocks-aware you must use the reth binary that we provide under https://github.com/base/node/tree/main/reth. \ No newline at end of file diff --git a/docs/base-chain/flashblocks/node-providers.mdx b/docs/base-chain/flashblocks/node-providers.mdx index 632080431..0e297752f 100644 --- a/docs/base-chain/flashblocks/node-providers.mdx +++ b/docs/base-chain/flashblocks/node-providers.mdx @@ -143,7 +143,3 @@ To minimize the amount of data sent to nodes, each Flashblock only includes the For detailed information about node setup, including hardware requirements and additional configuration options, refer to the [Reth node README](https://github.com/base/node/tree/main/reth#readme). - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/base-chain/llms-full.txt b/docs/base-chain/llms-full.txt new file mode 100644 index 000000000..72173d600 --- /dev/null +++ b/docs/base-chain/llms-full.txt @@ -0,0 +1,184 @@ +# https://docs.base.org/base-chain/llms-full.txt + +## Base Chain — Deep Guide for LLMs + +> Base is a fast, low‑cost Ethereum L2 built on the OP Stack. This guide orients an LLM to deployment, connectivity, network details, tooling, node operations, and security across the Base Chain docs. + +### What you can do here +- Deploy smart contracts quickly (testnet and mainnet) +- Connect apps to read/write on Base +- Understand fees, contracts, and bridges +- Operate performant nodes +- Use ecosystem tools (faucets, explorers, oracles, onramps) +- Discover Flashblocks resources +- Follow security disclosure guidance + +## Navigation (with brief descriptions) + +### Quickstart +- [Why Base](./quickstart/why-base) — Platform value prop +- [Deploy on Base](./quickstart/deploy-on-base) — Deploy contracts (Foundry) +- [Connecting to Base](./quickstart/connecting-to-base) — App connectivity +- [Bridge Token](./quickstart/bridge-token) — Transfer assets to Base + +### Network Information +- [Base Contracts](./network-information/base-contracts) — Canonical addresses +- [Network Fees](./network-information/network-fees) — Fee model +- [Ecosystem Contracts](./network-information/ecosystem-contracts) — Third‑party addresses +- [Block Building](./network-information/block-building) — Block production +- [Diffs vs Ethereum](./network-information/diffs-ethereum-base) — Differences from L1 +- [Bridges (Mainnet)](./network-information/bridges-mainnet) — Bridges + +### Flashblocks +- [Apps](./flashblocks/apps) — Apps using Flashblocks +- [Node Providers](./flashblocks/node-providers) — Infra providers +- [Docs](./flashblocks/docs) — Documentation + +### Node Operators +- [Run a Base Node](./node-operators/run-a-base-node) — Node setup +- [Performance Tuning](./node-operators/performance-tuning) — Optimization +- [Snapshots](./node-operators/snapshots) — Snapshot sync +- [Troubleshooting](./node-operators/troubleshooting) — Common fixes + +### Tools +- [Base Products](./tools/base-products) — Product listing +- [Onchain Registry API](./tools/onchain-registry-api) — API reference +- [Node Providers](./tools/node-providers) — RPC endpoints +- [Block Explorers](./tools/block-explorers) — Explorers +- [Network Faucets](./tools/network-faucets) — Test ETH +- [Onboarding](./tools/onboarding) — New builder help +- [Data Indexers](./tools/data-indexers) — Indexers +- [Cross Chain](./tools/cross-chain) — Interop +- [Account Abstraction](./tools/account-abstraction) — AA tooling +- [Onramps](./tools/onramps) — Fiat onramps +- [Oracles](./tools/oracles) — Oracle providers +- [Tokens in Wallet](./tools/tokens-in-wallet) — Token inclusion + +### Security +- [Security Council](./security/security-council) — Security governance +- [Avoid Malicious Flags](./security/avoid-malicious-flags) — App‑blocklist +- [Report a Vulnerability](./security/report-vulnerability) — Reporting + + +## Quickstart (excerpts) + +Source: `https://docs.base.org/base-chain/quickstart/deploy-on-base` + +Deploy with Foundry to Base Sepolia: + +```bash +forge create src/Contract.sol:Contract \ + --rpc-url $BASE_SEPOLIA_RPC \ + --private-key $PRIVATE_KEY \ + --verify --verifier blockscout --verifier-url https://base-sepolia.blockscout.com/api +``` + +Source: `https://docs.base.org/base-chain/quickstart/connecting-to-base` + +Connect a client to Base: + +```ts +import { createPublicClient, http } from 'viem' +import { base, baseSepolia } from 'viem/chains' + +const client = createPublicClient({ chain: base, transport: http() }) +``` + + +## Key Concepts (excerpts) + +Source: `https://docs.base.org/base-chain/network-information/diffs-ethereum-base` + +- OP Stack rollup: Base batches L2 transactions and posts data to Ethereum L1, inheriting L1 security. +- Fees: Total includes L2 execution and L1 data costs. See fee breakdown and estimator. + - Source: `https://docs.base.org/base-chain/network-information/network-fees` +- Canonical contracts: Use published address lists for bridges, system contracts, and registry. + - Source: `https://docs.base.org/base-chain/network-information/base-contracts` +- Bridges: Official and third‑party bridge options for moving assets to/from Base. + - Source: `https://docs.base.org/base-chain/network-information/bridges-mainnet` + + +## APIs and Tooling (pruned) + +- Onchain Registry API — discover verified projects and data on Base + - Source: `https://docs.base.org/base-chain/tools/onchain-registry-api` +- Node Providers — RPC endpoints for Base and Base Sepolia + - Source: `https://docs.base.org/base-chain/tools/node-providers` +- Block Explorers — Basescan and Blockscout URLs + - Source: `https://docs.base.org/base-chain/tools/block-explorers` +- Faucets — Get test ETH for Base Sepolia + - Source: `https://docs.base.org/base-chain/tools/network-faucets` + + +## Node Operations (excerpts) + +Sources: +- `https://docs.base.org/base-chain/node-operators/run-a-base-node` +- `https://docs.base.org/base-chain/node-operators/performance-tuning` +- `https://docs.base.org/base-chain/node-operators/snapshots` +- `https://docs.base.org/base-chain/node-operators/troubleshooting` + +- Architecture: Operate a rollup (consensus) node paired with an execution client. Ensure both services are healthy and in sync. +- Provisioning: Use SSD storage, reliable network, and adhere to the current requirements listed in the run‑a‑node guide. Prefer containerized or systemd‑managed services for resilience. +- Sync strategy: Start from a published snapshot to reduce time‑to‑sync, or sync from genesis when required for auditing. Choose pruned vs. archive based on workload. +- Monitoring: Track head slot/number, peer count, p2p health, RPC latency, error rates, and disk pressure. Export metrics to your observability stack and set alerts. +- RPC best practices: Expose only necessary methods, enforce authentication and rate limits, and front with a TLS‑terminating proxy. Separate public and internal RPCs. +- Performance tuning: Tune DB/cache sizes, peer limits, and concurrency. Offload heavy queries. See the performance tuning guide for recommended flags and OS settings. +- Maintenance: Keep up with client releases and chain config updates. Rotate logs, verify backups, and periodically re‑validate snapshots. +- Troubleshooting: Use the troubleshooting guide for common sync stalls, p2p isolation, or corrupted DB recovery. + +Check sync status via JSON‑RPC: + +```bash +curl -s -X POST "$BASE_RPC" -H 'content-type: application/json' \ + --data '{"jsonrpc":"2.0","id":1,"method":"eth_syncing","params":[]}' | jq +``` + + +## Flashblocks (excerpts) + +Sources: +- `https://docs.base.org/base-chain/flashblocks/docs` +- `https://docs.base.org/base-chain/flashblocks/apps` +- `https://docs.base.org/base-chain/flashblocks/node-providers` + +- Overview: Flashblocks resources aggregate information for builders and infra partners who need reliable, low‑latency access patterns on Base. +- Ecosystem: Review apps that make use of Flashblocks‑related infra and the node providers who support relevant capabilities. +- When to consider: High‑frequency reads/writes, market‑sensitive UX, or services where propagation and data freshness are critical. +- Integration: Work with supported node providers from the listing; follow provider documentation and the Flashblocks docs for setup and operational guidance. +- Validation: Measure end‑to‑end latency and consistency across providers as part of your rollout plan. + +Simple latency probe (conceptual): + +```ts +const t0 = Date.now() +const block = await client.getBlock() +console.log('latencyMs', Date.now() - t0, 'number', block.number) +``` + + +## Examples (common flows) + +Example: Bridge a token to Base (conceptual) + +Source: `https://docs.base.org/base-chain/quickstart/bridge-token` + +```text +1) Visit the official bridge UI +2) Select network (Ethereum → Base) +3) Choose asset and amount +4) Review fees and confirm +``` + +Example: Read a contract on Base with Viem + +Source: `https://docs.base.org/base-chain/quickstart/connecting-to-base` + +```ts +import { createPublicClient, http } from 'viem' +import { base } from 'viem/chains' + +const client = createPublicClient({ chain: base, transport: http() }) +const totalSupply = await client.readContract({ address: USDC, abi, functionName: 'totalSupply' }) +``` + diff --git a/docs/base-chain/llms.txt b/docs/base-chain/llms.txt new file mode 100644 index 000000000..0677e248d --- /dev/null +++ b/docs/base-chain/llms.txt @@ -0,0 +1,35 @@ +# https://docs.base.org/base-chain/llms.txt + +## Base Chain Documentation + +> Base is a fast, low-cost Ethereum L2 for building global onchain apps; start here to deploy, connect, and operate reliably. + +## Quickstart +- [Deploy on Base](./quickstart/deploy-on-base) — Set up Foundry, configure RPCs, and deploy your first contract +- [Connecting to Base](./quickstart/connecting-to-base) — Configure providers and clients to read/write on Base + +## Network Information +- [Network Fees](./network-information/network-fees) — L2 execution and L1 data costs on Base +- [Base Contracts](./network-information/base-contracts) — Core contracts and addresses + +## Flashblocks +- [FAQ](./flashblocks/docs) — Frequently asked questions about Flashblocks +- [Apps](./flashblocks/apps) — How apps integrate with Flashblocks + +## Node Operators +- [Run a Base Node](./node-operators/run-a-base-node) — Setup and operations guide +- [Performance Tuning](./node-operators/performance-tuning) — Optimize node performance + +## Tools +- [Onchain Registry API](./tools/onchain-registry-api) — Query Base registry data +- [Network Faucets](./tools/network-faucets) — Obtain testnet ETH for Base Sepolia + +## Security +- [Report a Vulnerability](./security/report-vulnerability) — Security contact and disclosure +- [Security Council](./security/security-council) — Governance and process overview + +## Optional +- [Block Explorers](./tools/block-explorers) — Inspect contracts and transactions on Base +- [Ecosystem Contracts](./network-information/ecosystem-contracts) — Common ecosystem contract addresses + + diff --git a/docs/base-chain/network-information/base-contracts.mdx b/docs/base-chain/network-information/base-contracts.mdx index 456630066..c12f74ff4 100644 --- a/docs/base-chain/network-information/base-contracts.mdx +++ b/docs/base-chain/network-information/base-contracts.mdx @@ -129,7 +129,3 @@ Certain contracts are mandatory according to the [OP Stack SDK](https://stack.op | Challenger | [0x8b8c52B04A38f10515C52670fcb23f3C4C44474F](https://sepolia.etherscan.io/address/0x8b8c52B04A38f10515C52670fcb23f3C4C44474F) | EOA managed by Coinbase Technologies | | System config owner | [0x0fe884546476dDd290eC46318785046ef68a0BA9](https://sepolia.etherscan.io/address/0x0fe884546476dDd290eC46318785046ef68a0BA9) | Gnosis Safe | | Guardian | [0xA9FF930151130fd19DA1F03E5077AFB7C78F8503](https://sepolia.etherscan.io/address/0xA9FF930151130fd19DA1F03E5077AFB7C78F8503) | EOA managed by Coinbase Technologies | - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/base-chain/network-information/block-building.mdx b/docs/base-chain/network-information/block-building.mdx index 2408534d1..c40b5b6b4 100644 --- a/docs/base-chain/network-information/block-building.mdx +++ b/docs/base-chain/network-information/block-building.mdx @@ -13,8 +13,8 @@ The Base networks are currently configured in the following ways: | Network | Current Configuration | Upcoming Deployments | |--------------|-----------------------------|------------------------| -| Base Mainnet | [Flashblocks](#flashblocks) | None | -| Base Sepolia | [Flashblocks](#flashblocks) | None | +| Base Mainnet | [Flashblocks](#flashblocks) | Sep 10: [Flashblocks](#flashblocks) + [Per-Transaction Gas Max](#per-transaction-gas-maximum) | +| Base Sepolia | [Flashblocks](#flashblocks) | Sep 3: [Flashblocks](#flashblocks) + [Per-Transaction Gas Max](#per-transaction-gas-maximum) | ## Configurations @@ -38,10 +38,40 @@ And so on for subsequent Flashblocks Consequently, transactions with large gas requirements must wait for later Flashblocks with sufficient gas capacity. For example, a transaction exceeding 1/10 of the block's gas limit cannot be included in Flashblock 1 and must wait for Flashblock 2 or later. +### Per-Transaction Gas Maximum + + +On **September 10**, we’ll begin enforcing a per‑transaction gas cap of 16,777,216 gas (2^24) on Base. This aligns Base with the [in‑flight L1 proposal](https://eips.ethereum.org/EIPS/eip-7825), improves network predictability, and has no effect on fees for typical users. We’ll activate it first on Base Sepolia on **September 3**, then activate it on Base mainnet temporarily for one day on **September 10** as a warning. This will be activated permanently on Base mainnet on **September 17**. + +#### What’s changing + +Today, a single transaction can request any gas up to the block gas limit. With this change, transactions that specify gas > 16,777,216 will be rejected by the mempool before inclusion. + +#### Why we’re doing this + +* Predictability & resilience. Bounding single‑tx execution reduces extreme outliers and makes block building more stable. +* Consistency with Ethereum. The L1 community is converging on a 2^24 per‑tx cap; we’re aligning early so builders don’t have to juggle different rules across layers. +* No practical impact for most builders. The overwhelming majority of Base transactions are far below this cap. + +#### Action items for developers + +* If you manually set large gas limits, update your code to stay ≤ 16,777,216. +* For batch jobs or complex on‑chain work, split large jobs into smaller calls. +* If you maintain custom tooling, surface a clear message when the cap hits. + + +#### FAQ + +* **Does this change the block gas limit or fees?** No. This is a per‑transaction guardrail. +* **Will contract deployments break?** Typical deployments are well below the cap. If yours isn’t, consider slimming bytecode or chunking initialization. + + +Base enforces a per-transaction gas maximum of **16,777,216 gas (2^24)**. Transactions that specify a gas limit above this value are **rejected by the mempool before inclusion**. `eth_sendTransaction` or `eth_sendRawTransaction` will return a JSON-RPC error (for example: `exceeds maximum per-transaction gas limit`). This cap does **not** change the block gas limit or the block validity conditions (that will come later with [EIP 7825](https://eips.ethereum.org/EIPS/eip-7825)). + ### Vanilla Blocks are built every 2s by [op-geth](https://github.com/ethereum-optimism/op-geth). Transactions within those blocks are ordered by -priority fee, see the ([code](https://github.com/ethereum-optimism/op-geth/blob/optimism/miner/worker.go#L627). +priority fee, see the ([code](https://github.com/ethereum-optimism/op-geth/blob/optimism/miner/worker.go#L627)). ## Changelog @@ -49,7 +79,3 @@ priority fee, see the ([code](https://github.com/ethereum-optimism/op-geth/blob/ * 15th May: Ended testing Flashblocks on Base Mainnet * 15th May: Started testing Flashblocks on Base Mainnet * 25th Feb: Enabled Flashblocks on Base Sepolia - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - diff --git a/docs/base-chain/network-information/bridges-mainnet.mdx b/docs/base-chain/network-information/bridges-mainnet.mdx index 39771c478..34f99629a 100644 --- a/docs/base-chain/network-information/bridges-mainnet.mdx +++ b/docs/base-chain/network-information/bridges-mainnet.mdx @@ -84,7 +84,3 @@ The [Base Discord](https://base.org/discord) community is available around the c assistance and support! You can create a support ticket in the #general-support channel. - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/base-chain/network-information/diffs-ethereum-base.mdx b/docs/base-chain/network-information/diffs-ethereum-base.mdx index 1b9020e69..01d44bdee 100644 --- a/docs/base-chain/network-information/diffs-ethereum-base.mdx +++ b/docs/base-chain/network-information/diffs-ethereum-base.mdx @@ -14,7 +14,3 @@ These minor differences include: - [Blocks](https://stack.optimism.io/docs/releases/bedrock/differences/#blocks) - [Network specifications](https://stack.optimism.io/docs/releases/bedrock/differences/#network-specifications) - [Transaction costs](https://stack.optimism.io/docs/releases/bedrock/differences/#transaction-costs) - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/base-chain/network-information/ecosystem-contracts.mdx b/docs/base-chain/network-information/ecosystem-contracts.mdx index 36d82c757..b01df452c 100644 --- a/docs/base-chain/network-information/ecosystem-contracts.mdx +++ b/docs/base-chain/network-information/ecosystem-contracts.mdx @@ -76,7 +76,3 @@ Two community projects, [BaseX](https://basex-test.vercel.app/swap?currencyA=ETH | :-------- | :---------------------------------------------------------------------------------------------------------------------------- | | `Factory` | [0x7Ae58f10f7849cA6F5fB71b7f45CB416c9204b1e](https://sepolia.basescan.org/address/0x7Ae58f10f7849cA6F5fB71b7f45CB416c9204b1e) | | `Router` | [0x1689E7B1F10000AE47eBfE339a4f69dECd19F602](https://sepolia.basescan.org/address/0x1689E7B1F10000AE47eBfE339a4f69dECd19F602) | - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/base-chain/network-information/network-fees.mdx b/docs/base-chain/network-information/network-fees.mdx index dfd0df6a9..f735b60d3 100644 --- a/docs/base-chain/network-information/network-fees.mdx +++ b/docs/base-chain/network-information/network-fees.mdx @@ -26,6 +26,3 @@ For additional details about fee calculation on Base, please refer to the [op-stack developer documentation](https://docs.optimism.io/stack/transactions/fees). -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - diff --git a/docs/base-chain/node-operators/performance-tuning.mdx b/docs/base-chain/node-operators/performance-tuning.mdx index 72930a6eb..d445c1621 100644 --- a/docs/base-chain/node-operators/performance-tuning.mdx +++ b/docs/base-chain/node-operators/performance-tuning.mdx @@ -102,6 +102,3 @@ LDB_COMPACTION_TOTAL_SIZE="41943040" # 40 MiB total compaction size (default: 8 LDB_DEBUG_OPTIONS="1" # Emit LevelDB debug logs ``` -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - diff --git a/docs/base-chain/node-operators/run-a-base-node.mdx b/docs/base-chain/node-operators/run-a-base-node.mdx index b50f2d3b5..f105468e4 100644 --- a/docs/base-chain/node-operators/run-a-base-node.mdx +++ b/docs/base-chain/node-operators/run-a-base-node.mdx @@ -112,6 +112,3 @@ echo Latest synced block behind by: $((($(date +%s)-$( \ You'll also know that the sync hasn't completed if you get `Error: nonce has already been used` if you try to deploy using your node. -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - diff --git a/docs/base-chain/node-operators/snapshots.mdx b/docs/base-chain/node-operators/snapshots.mdx index c09cc9056..c30693bcb 100644 --- a/docs/base-chain/node-operators/snapshots.mdx +++ b/docs/base-chain/node-operators/snapshots.mdx @@ -77,6 +77,3 @@ These steps assume you are in the cloned `node` directory (the one containing `d 6. **Verify and Clean Up**: Monitor the node logs (`docker compose logs -f `) or use the [sync monitoring](/base-chain/node-operators/run-a-base-node#monitoring-sync-progress) command to ensure the node starts syncing from the snapshot's block height. Once confirmed, you can safely delete the downloaded snapshot archive (`.tar.gz` file) to free up disk space. -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - diff --git a/docs/base-chain/node-operators/troubleshooting.mdx b/docs/base-chain/node-operators/troubleshooting.mdx index a1fef8832..fd1bbf257 100644 --- a/docs/base-chain/node-operators/troubleshooting.mdx +++ b/docs/base-chain/node-operators/troubleshooting.mdx @@ -128,7 +128,3 @@ If you’ve followed this guide and are still encountering issues, seek help fro - **Discord**: Join the [Base Discord](https://discord.gg/buildonbase) and post in the `🛠|node-operators` channel, providing details about your setup, the issue, and relevant logs. - **GitHub**: Check the [Base Node repository issues](https://github.com/base-org/node/issues) or open a new one if you suspect a bug. - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/base-chain/quickstart/bridge-token.mdx b/docs/base-chain/quickstart/bridge-token.mdx index c35f30099..4810fcba1 100644 --- a/docs/base-chain/quickstart/bridge-token.mdx +++ b/docs/base-chain/quickstart/bridge-token.mdx @@ -26,6 +26,3 @@ Follow the instructions in the [Github repository](https://github.com/ethereum-o Reviews are regularly conducted by the Base team and you should receive a reply within 24-72 hours (depending on if the PR is opened on a weekday, weekend or holiday). -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - diff --git a/docs/base-chain/quickstart/connecting-to-base.mdx b/docs/base-chain/quickstart/connecting-to-base.mdx index 5391e33be..6effd5960 100644 --- a/docs/base-chain/quickstart/connecting-to-base.mdx +++ b/docs/base-chain/quickstart/connecting-to-base.mdx @@ -121,7 +121,3 @@ To add Base Sepolia as a custom network in MetaMask: 6. Tap the Save button to save Base Sepolia as a network. You should now be able to connect to the Base testnet by selecting it from the network selection dropdown menu. - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/base-chain/quickstart/deploy-on-base.mdx b/docs/base-chain/quickstart/deploy-on-base.mdx index 3a6178bf7..abd935349 100644 --- a/docs/base-chain/quickstart/deploy-on-base.mdx +++ b/docs/base-chain/quickstart/deploy-on-base.mdx @@ -141,7 +141,4 @@ This will return the initial value of the Counter contract's `number` storage va - Use [Onchainkit](https://onchainkit.com) to connect your frontend to your contracts! Onchainkit is a library of ready-to-use React components and Typescript utilities. - Learn more about interacting with your contracts in the command line using Foundry from our [Foundry tutorial](/learn/foundry/deploy-with-foundry). -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - diff --git a/docs/base-chain/quickstart/why-base.mdx b/docs/base-chain/quickstart/why-base.mdx index 308b911e8..9de93f85c 100644 --- a/docs/base-chain/quickstart/why-base.mdx +++ b/docs/base-chain/quickstart/why-base.mdx @@ -41,7 +41,7 @@ But there’s more: Base apps can be launched in hours, not days or weeks, thank Some of the tooling that makes this possible: -- **Smart Wallets:** Onboard your users quickly and securely. Users never have to worry about seed phrases again. +- **Base Account:** Onboard your users quickly and securely. Users never have to worry about seed phrases again. - **Coinbase Developer Platform:** Access specialized developer services for onchain development, such as free node software, sponsored transactions, and other tools to help you fine-tune your application. - **OnchainKit:** A React component library (TypeScript) to help you build apps faster. - **Basenames:** More than just human-readable text to replace an address—Basenames are the front page to a builder’s onchain profile. @@ -77,7 +77,3 @@ Base offers builders access to a high-growth, high-signal community across multi [Moonwell]: https://moonwell.fi/discover [Clanker]: https://www.clanker.world/ [Retroactive Public Goods Funding (RetroPGF)]: https://round3.optimism.io/projects?after=undefined&display=grid&sort=mostAwarded&search=&seed=1738341430276&categories= - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/base-chain/security/avoid-malicious-flags.mdx b/docs/base-chain/security/avoid-malicious-flags.mdx index df3e17e45..8b4e676c3 100644 --- a/docs/base-chain/security/avoid-malicious-flags.mdx +++ b/docs/base-chain/security/avoid-malicious-flags.mdx @@ -32,6 +32,3 @@ Coinbase Wallet may report false positives when flagging apps. To avoid false po --- -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - diff --git a/docs/base-chain/security/bug-bounty.mdx b/docs/base-chain/security/bug-bounty.mdx index 405e06d30..d5ad135d5 100644 --- a/docs/base-chain/security/bug-bounty.mdx +++ b/docs/base-chain/security/bug-bounty.mdx @@ -2,6 +2,3 @@ title: 'Bug Bounty' --- -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - diff --git a/docs/base-chain/security/report-vulnerability.mdx b/docs/base-chain/security/report-vulnerability.mdx index 731a1c371..b304821b3 100644 --- a/docs/base-chain/security/report-vulnerability.mdx +++ b/docs/base-chain/security/report-vulnerability.mdx @@ -18,6 +18,3 @@ In line with our strategy of being the safest way for users to access crypto: For more information on reporting vulnerabilities and our HackerOne bug bounty program, view our [security program policies](https://hackerone.com/coinbase?view_policy=true). -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - diff --git a/docs/base-chain/security/security-council.mdx b/docs/base-chain/security/security-council.mdx index 1693517a3..e94eb73fc 100644 --- a/docs/base-chain/security/security-council.mdx +++ b/docs/base-chain/security/security-council.mdx @@ -140,6 +140,3 @@ innovation, creativity, and freedom—on a foundation that everyone can rely on. Base is for everyone. -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - diff --git a/docs/base-chain/tools/account-abstraction.mdx b/docs/base-chain/tools/account-abstraction.mdx index f8b1dec5e..4ef628c55 100644 --- a/docs/base-chain/tools/account-abstraction.mdx +++ b/docs/base-chain/tools/account-abstraction.mdx @@ -51,7 +51,3 @@ WalletKit is compatible with most EVM chains, including Base. You can check out ## ZeroDev [ZeroDev](https://zerodev.app) is an embedded wallet powered by account abstraction. It offers you the ability to create self-custody wallets for your users, sponsor gas, and simplify user flows by batching and automating transactions. - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/base-chain/tools/base-products.mdx b/docs/base-chain/tools/base-products.mdx index df9ba91e0..2ed352c97 100644 --- a/docs/base-chain/tools/base-products.mdx +++ b/docs/base-chain/tools/base-products.mdx @@ -7,7 +7,7 @@ title: "Base Products" Ready-to-use, full-stack components to make building onchain faster and easier.
- + Build mini apps to increase your distribution and find traction. @@ -23,8 +23,8 @@ title: "Base Products" Account Abstraction endpoints to send transactions and sponsor gas. + + End-to-end testing framework for blockchain applications. + -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - diff --git a/docs/base-chain/tools/block-explorers.mdx b/docs/base-chain/tools/block-explorers.mdx index b7931ecc4..ae0423f27 100644 --- a/docs/base-chain/tools/block-explorers.mdx +++ b/docs/base-chain/tools/block-explorers.mdx @@ -58,6 +58,3 @@ A testnet explorer for [Base Sepolia](https://sepolia.basescan.org/) is also ava With the [Tenderly](https://tenderly.co/) developer explorer you can get unparalleled visibility into your smart contract code. You can easily view detailed transaction information, spot bugs in your code, and optimize gas spend. Supporting Base mainnet and Base Sepolia testnet, Tenderly Explorer helps you track your smart contracts while providing visibility on a granular level. -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - diff --git a/docs/base-chain/tools/cross-chain.mdx b/docs/base-chain/tools/cross-chain.mdx index a94ce0412..371d8a944 100644 --- a/docs/base-chain/tools/cross-chain.mdx +++ b/docs/base-chain/tools/cross-chain.mdx @@ -75,7 +75,3 @@ For more information on integrating Wormhole, visit their [documentation](https: - [Base Mainnet](https://docs.wormhole.com/wormhole/blockchain-environments/evm#base) - [Base Sepolia](https://docs.wormhole.com/wormhole/blockchain-environments/evm#base) (Testnet) - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/base-chain/tools/data-indexers.mdx b/docs/base-chain/tools/data-indexers.mdx index e896758f2..c1a93ac22 100644 --- a/docs/base-chain/tools/data-indexers.mdx +++ b/docs/base-chain/tools/data-indexers.mdx @@ -207,7 +207,3 @@ To get started, visit the [documentation](https://docs.flair.dev) or clone the [ - [Base Mainnet](https://docs.flair.dev/reference/manifest.yml) - [Base Sepolia](https://docs.flair.dev/reference/manifest.yml) (Testnet) - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/base-chain/tools/network-faucets.mdx b/docs/base-chain/tools/network-faucets.mdx index cdb1be744..4097cb5fd 100644 --- a/docs/base-chain/tools/network-faucets.mdx +++ b/docs/base-chain/tools/network-faucets.mdx @@ -47,6 +47,14 @@ Requests to Alchemy's Base Sepolia Faucet are limited to one claim per 24 hours. Requests to Bware Labs Faucet are limited to one claim per 24 hours. +## Chainstack Faucet + +[Chainstack Faucet](https://faucet.chainstack.com/) dispenses Base ETH based on your Chainstack platform API key. + + +Chainstack faucet drips 0.5 ETH every 24 hours. + + ## QuickNode Faucet @@ -76,4 +84,5 @@ Each wallet is restricted to receiving 0.5 ETH from this faucet every 24 hours. import PolicyBanner from "/snippets/PolicyBanner.mdx"; - \ No newline at end of file + + diff --git a/docs/base-chain/tools/node-providers.mdx b/docs/base-chain/tools/node-providers.mdx index 69a07e37f..7bca9aa7c 100644 --- a/docs/base-chain/tools/node-providers.mdx +++ b/docs/base-chain/tools/node-providers.mdx @@ -182,7 +182,7 @@ import {HeaderNoToc} from "/snippets/headerNoToc.mdx"; ## Unifra -[Unifra](https://base.unifra.io/) is a Web3 developer platform that provides tools, APIs, and node infrastructure, and provides access to Base nodes that are nodes are reliable, scalable, and easy to use. +[Unifra](https://www.unifra.io) is a Web3 developer platform that provides tools, APIs, and node infrastructure, and provides access to Base nodes that are nodes are reliable, scalable, and easy to use. @@ -196,6 +196,3 @@ import {HeaderNoToc} from "/snippets/headerNoToc.mdx"; - Base Mainnet -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - diff --git a/docs/base-chain/tools/onboarding.mdx b/docs/base-chain/tools/onboarding.mdx index 26c213524..3f3bb955b 100644 --- a/docs/base-chain/tools/onboarding.mdx +++ b/docs/base-chain/tools/onboarding.mdx @@ -46,6 +46,12 @@ WalletKit is compatible with most EVM chains, including Base. It has integrated You can check out the [WalletKit documentation here](https://docs.walletkit.com). Start building for free on the Base testnet today. + +## Dreamspace + +[DreamSpace](https://dreamspace.xyz) is the first vibe coding canvas for crypto creatives, allowing you to build and launch your dream app in minutes and monetize it onchain, with no coding required. Effortlessly drag, drop, chat, and create your frontend, generate, audit, and deploy secure smart contracts on any EVM chain including Base, integrate data and charts from [Space and Time](https://app.spaceandtime.ai), and connect to external APIs to bring your app to life. + import PolicyBanner from "/snippets/PolicyBanner.mdx"; - \ No newline at end of file + + diff --git a/docs/base-chain/tools/onchain-registry-api.mdx b/docs/base-chain/tools/onchain-registry-api.mdx index f28677ea3..9738a4852 100644 --- a/docs/base-chain/tools/onchain-registry-api.mdx +++ b/docs/base-chain/tools/onchain-registry-api.mdx @@ -155,7 +155,3 @@ This endpoint will display a single Onchain Registry entry that is being activel ## Terms & Conditions We grant third parties a non-exclusive, worldwide, royalty-free license to use the Onchain Registry API solely for the purpose of integrating it into their applications or services. This license does not extend to any data or content accessed through the Onchain API, which remains the sole responsibility of the third party. By using the Onchain Registry API, third parties agree to comply with our license terms and any applicable laws and regulations as set forth in Coinbase Developer Platform Terms of Service. We make no warranties regarding the Onchain Registry API, and users accept all risks associated with its use. The Onchain App Registry API is an Early Access Product per Section 18 of the [Coinbase Developer Platform Terms of Service](https://www.coinbase.com/legal/developer-platform/terms-of-service) and the Coinbase [Prohibited Use Policy](https://www.coinbase.com/legal/prohibited_use), and all terms and conditions therein govern your use of the Onchain Registry API. - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/base-chain/tools/onramps.mdx b/docs/base-chain/tools/onramps.mdx index 357c90381..0ca03ec32 100644 --- a/docs/base-chain/tools/onramps.mdx +++ b/docs/base-chain/tools/onramps.mdx @@ -26,7 +26,3 @@ description: Documentation for fiat-to-crypto onramps for the Base network. ## Alchemy Pay [Alchemy Pay](https://ramp.alchemypay.org/) (ACH) is a payment solutions provider that seamlessly connects fiat and crypto economies for global consumers, merchants, developers, and institutions. - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/base-chain/tools/oracles.mdx b/docs/base-chain/tools/oracles.mdx index da48646be..145af07b7 100644 --- a/docs/base-chain/tools/oracles.mdx +++ b/docs/base-chain/tools/oracles.mdx @@ -155,7 +155,3 @@ Visit the Supra [documentation](https://supraoracles.com/docs/) to learn more ab - Base Mainnet - Base Sepolia (Testnet) - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/base-chain/tools/tokens-in-wallet.mdx b/docs/base-chain/tools/tokens-in-wallet.mdx index 79dbe2585..2a347309f 100644 --- a/docs/base-chain/tools/tokens-in-wallet.mdx +++ b/docs/base-chain/tools/tokens-in-wallet.mdx @@ -131,7 +131,3 @@ If you’re encountering an issue with token support on Coinbase Wallet, submit [image3]: [image4]: - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/cookbook/accept-crypto-payments.mdx b/docs/cookbook/accept-crypto-payments.mdx index cdfd8348e..dd293311d 100644 --- a/docs/cookbook/accept-crypto-payments.mdx +++ b/docs/cookbook/accept-crypto-payments.mdx @@ -392,7 +392,3 @@ Congratulations! You've successfully integrated Coinbase Commerce and OnchainKit Your application now supports the future of global payments. Happy building on Base! - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/cookbook/ai-assisted-documentation-reading.mdx b/docs/cookbook/ai-assisted-documentation-reading.mdx new file mode 100644 index 000000000..39465b03c --- /dev/null +++ b/docs/cookbook/ai-assisted-documentation-reading.mdx @@ -0,0 +1,78 @@ +--- +title: AI-Assisted Documentation Reading +description: Develop strategies for using AI tools to understand complex technical documentation and troubleshoot development challenges +--- + +# Techniques for understanding documentation + +AI tools excel at breaking down complex technical content into understandable explanations and practical guidance tailored to your specific learning needs and project requirements. Below are three techniques that you can use to leverage AI to help you understand documentation. + +### Used tailored prompts + +The "Explain Like I'm a Vibe Coder" approach involves asking AI to simplify technical concepts while maintaining practical applicability. This technique is particularly effective for understanding blockchain concepts, API documentation, and complex development patterns. The key is providing context about your current knowledge level and specific goals rather than asking for generic explanations. + + +``` +I'm looking at this API documentation but finding it confusing. +I want to implement the `Checkout` component on the checkout.tsx page of my website. +Please explain this like I'm a Vibe Coder (someone new to blockchain development but familiar with basic web development): +``` + +```typescript Checkout.tsx +const chargeHandler = async () => { + const response = await fetch('/createCharge', { method: 'POST' }); + const { id } = await response.json(); + return id; // Return charge ID +}; + + + +; +``` + + + +### Use Screenshots + +Sharing a screenshot with AI enhances its ability to understand your problem significantly. When you encounter confusing documentation sections, interfaces, or error messages, including screenshots in your AI prompts provides visual context that pure text cannot convey. Most AI tools can analyze images and provide specific guidance based on what they observe in your screenshots. + + +``` +I'm looking at this API documentation but finding it confusing. I want to implement the `Checkout` component on the checkout.tsx page of my website. + +I have attached two screenshots. The first screenshot is the page I would like to implement the `Checkout` component on. The second screenshot is the API documentation I am looking at. + +Please explain this like I'm a Vibe Coder (someone new to blockchain development but familiar with basic web development): + +[Screenshot 1] + +[Screenshot 2] + +``` + + + +### Use code snippets + +Code snippet analysis is another powerful technique. When you find example code in documentation but don't understand how it applies to your situation, you can paste the code into an AI prompt along with your specific requirements. The AI can explain the code's purpose, modify it for your needs, and highlight potential issues or improvements. + + +``` + +I'm looking at this API documentation but finding it confusing. Please explain this like I'm a Vibe Coder (someone new to blockchain development but familiar with basic web development): + +[PASTE DOCUMENTATION SECTION HERE] + +Specifically help me understand: + +1. What this API does in simple terms +2. When I would use it in my Mini App +3. What the key parameters mean +4. A practical example with my specific use case: [DESCRIBE YOUR USE CASE] + +Break it down step-by-step and include a working code example I can copy and modify. + +``` + + +``` diff --git a/docs/cookbook/ai-powered-development-fundamentals.mdx b/docs/cookbook/ai-powered-development-fundamentals.mdx new file mode 100644 index 000000000..323989bbc --- /dev/null +++ b/docs/cookbook/ai-powered-development-fundamentals.mdx @@ -0,0 +1,46 @@ +--- +title: Vibe Coding Fundamentals +description: Combine traditional web development with AI-powered code generation +--- + +# Vibe Coding Mini Apps + +Mini Apps are just web apps—with added capabilities. If you've built a website, you're already halfway there. The difference is that Mini Apps are designed to work seamlessly inside social feeds, come with built-in wallets, and connect to open, onchain identity. + +The easiest way to get started is with MiniKit, a toolkit that gives you ready-to-go templates. These templates handle the heavy lifting—wallet integration, social feed support, and identity management—so you can focus on what your app actually does. + +You can also build using AI tools that turn ideas into code. Tools like [Vercel V0](https://v0.dev/), [Claude Code](https://www.anthropic.com/claude-code), and [Loveable](https://lovable.dev/) let you describe your app in plain language and generate working code in minutes. They're perfect for creators, vibe coders, and anyone who wants to build without spending weeks learning a new framework. + +In this guide, we'll use AI to help build a Mini App that also works as a standalone website. That means what you build can live on Base, show up in TBA, and still work on the open web. + +## Vibe Coding Elements + +Vibe coding is a powerful way to bring your idea to life especially for non-developers. + +Below are the elements that you will need to consider when vibe coding a mini app. + + + +Clarify the app's purpose, target audience, and core features. Decide on the minimum viable product (MVP) and ensure it's achievable within your available time and resources. + + + + Map out the user journey, key screens, and interactions. Choose the tech + stack, plan integrations (APIs, onchain features, etc.), and decide on the + app's overall architecture. + + + + Implement the primary functionality first, focusing on the MVP. Keep + components modular for easier testing and iteration. + + + + Run functional, performance, and user tests to catch bugs and improve the + experience. Incorporate feedback and make necessary adjustments. + + + +Deploy to your hosting platform (e.g., Vercel, Fleek). Share the app with your intended audience, gather real-world feedback, and iterate as needed. + + diff --git a/docs/cookbook/ai-prompting.mdx b/docs/cookbook/ai-prompting.mdx index b219b4cab..6c5725ec4 100644 --- a/docs/cookbook/ai-prompting.mdx +++ b/docs/cookbook/ai-prompting.mdx @@ -7,7 +7,3 @@ description: How to use AI-powered IDEs to generate code for OnchainKit. import AiPowered from "/snippets/ai-powered.mdx"; - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/cookbook/base-app-coins.mdx b/docs/cookbook/base-app-coins.mdx new file mode 100644 index 000000000..c52c5eb82 --- /dev/null +++ b/docs/cookbook/base-app-coins.mdx @@ -0,0 +1,326 @@ +--- +sidebarTitle: Base App Coins +title: 'Find and load metadata for all coins created via Base App' +description: 'Learn how to use onchain data to index all Uniswap v4 pools created for coins created via Zora and Base App and load metadata including available liquidity, current prices, token information, and more' +--- + +With the launch of Base App, users can post on the network and get paid by coining their content. The content coins are created via Zora and a Uniswap v4 pool is automatically created and initialized for the coin. In this starter guide we will cover how you can index onchain data from Uniswap to identify all pools containing Zora ecosystem tokens, filtering Base App tokens from them, and loading metadata for those pools. + +## Overview + +The full code for this starter implementation can be found [here](https://github.com/base/demos/tree/master/base-app-coins). The guide below explains the core components, how it works, and what parts you may need to customize to your needs. + +The sample is a Bun + TypeScript project that uses `viem` for interacting with Base Chain via standard Ethereum JSON-RPC methods. It also utilizes Uniswap's SDKs to make some calculations easier. + +## Core Components + +#### 1. Event Monitoring (`index.ts`) +The main entry point scans Uniswap V4 `Initialize` events within a specified block range to discover newly created pools. + +```typescript +const logs = await publicClient.getContractEvents({ + abi: UniswapV4ABI, + address: UniswapV4PoolManager, + fromBlock: START_BLOCK_NUMBER, + toBlock: END_BLOCK_NUMBER, + eventName: "Initialize" +}) + +const poolKeys = logs.map((log) => { + return { + currency0: log.args.currency0, + currency1: log.args.currency1, + fee: log.args.fee, + tickSpacing: log.args.tickSpacing, + hooks: log.args.hooks + } +}) as PoolKey[] +``` + +**Key aspects:** +- Uses `publicClient.getContractEvents()` to fetch pool initialization events +- Filters events from the Uniswap V4 PoolManager contract +- Extracts pool keys (currency0, currency1, fee, tickSpacing, hooks) from event logs + +**Customizations:** +- Adjust `START_BLOCK_NUMBER` and `END_BLOCK_NUMBER` for your needs +- If you'd like to index these events in real-time, use `viem`s `watchContractEvent` instead + + +#### 2. Pool Data Loading (`utils.ts`) +Contains utilities for enriching pool data with on-chain information. + +```typescript +export async function loadData(key: PoolKey) { + // Load information about each token (name, symbol, decimals) + const [currency0, currency1] = await Promise.all([ + getCurrency(key.currency0), + getCurrency(key.currency1) + ]) + + const poolId = Pool.getPoolId(currency0, currency1, key.fee, key.tickSpacing, key.hooks) as `0x${string}`; + // Load the current price of the pool + const [sqrtPriceX96, tick, _protocolFee, _lpFee] = await stateView.read.getSlot0([poolId]); + // Load the total amount of liquidity available in the pool + const liquidity = await stateView.read.getLiquidity([poolId]) + + // + const pool = new Pool( + currency0, + currency1, + key.fee, + key.tickSpacing, + key.hooks, + sqrtPriceX96.toString(), + liquidity.toString(), + tick, + ) + return pool; +} +``` + + +**Currency Resolution:** +```typescript +export async function getCurrency(address: string): Promise { + if (address === zeroAddress) { + return Ether.onChain(base.id); + } + + const erc20 = getContract({ + abi: erc20Abi, + address: address as `0x${string}`, + client: publicClient + }) + + const [name, symbol, decimals] = await Promise.all([ + erc20.read.name(), + erc20.read.symbol(), + erc20.read.decimals() + ]) + + return new Token(base.id, address, decimals, symbol, name) +} +``` + +**Technical details:** +- Uses Uniswap V4 StateView contract for efficient state queries +- Handles both ERC20 tokens and native ETH (zero address) + + +#### 3. Token Classification Logic + +```typescript +let coinType: string | undefined; +if (key.hooks === "0xd61A675F8a0c67A73DC3B54FB7318B4D91409040") { + coinType = "ZORA_CREATOR_COIN" +} else if (key.hooks === "0x9ea932730A7787000042e34390B8E435dD839040") { + coinType = "ZORA_V4_COIN" +} + +if (!coinType) continue; + +// Detect if the coin is coming from Base App or Zora +const appType = await categorizeAppType(pool); +``` + +**Base App Token Detection:** +```typescript + +export async function categorizeAppType(pool: Pool) { + async function tryGetPlatformReferrer(address: string) { + const zoraBaseCoin = getContract({ + abi: parseAbi([ + "function platformReferrer() view returns (address)", + ]), + address: address as `0x${string}`, + client: publicClient + }) + + try { + const platformReferrer = await zoraBaseCoin.read.platformReferrer() + return platformReferrer + } catch (error) { + return ADDRESS_ZERO + } + } + + // Try to fetch `platformReferrer()` on both currencies in the Pool + // falling back to ADDRESS_ZERO if the function does not exist (currency is not a Zora coin) + const [currency0PlatformReferrer, currency1PlatformReferrer] = await Promise.all([ + tryGetPlatformReferrer(pool.currency0.wrapped.address), + tryGetPlatformReferrer(pool.currency1.wrapped.address) + ]) + + // If either of the currencies has the Base App referrer address, + // the coin is coming from the Base App + if ([currency0PlatformReferrer, currency1PlatformReferrer].includes(BASE_PLATFORM_REFERRER)) { + return "TBA" + } + + return "ZORA" +} +``` + +Since the coins are created via Zora, filtering down to which ones are from Base App is a matter of looking at what the platform referrer address is on the Zora coin. We don't know if the Zora coin is necessarily `currency0` or `currency1` in the pool - so we attempt to fetch the platform referrer address for both. For tokens like WETH which don't have that view function available, it will just fall back to the zero address. If either of the currencies return a valid platform referrer address that also matches the referrer address used by the Base App, we classify the coin as having come from the Base App. + + +#### 4. Liquidity Calculations + +```typescript +const priceUpper = TickMath.getSqrtRatioAtTick(TickMath.MAX_TICK) +const priceLower = TickMath.getSqrtRatioAtTick(TickMath.MIN_TICK) + +const amount0 = SqrtPriceMath.getAmount0Delta(pool.sqrtRatioX96, priceUpper, pool.liquidity, true); +const amount1 = SqrtPriceMath.getAmount1Delta(priceLower, pool.sqrtRatioX96, pool.liquidity, true) + +const amount0HumanReadable = formatUnits(BigInt(amount0.toString()), pool.currency0.decimals); +const amount1HumanReadable = formatUnits(BigInt(amount1.toString()), pool.currency1.decimals); +``` + +Given the `liquidity` amount we loaded previously for a pool, we utilize Uniswap's SDK to do some math and get human-friendly versions of how much of each token is available in the pool as total liquidity. + + +## Alternative Implementation Approaches + +### Event Data Sources + +This implementation uses direct JSON-RPC calls via `viem`, but you can adapt it for other data sources: + +**Subgraphs**: If you're already using The Graph Protocol, modify the event fetching logic to query a Uniswap V4 subgraph instead of making direct RPC calls. Replace the `getContractEvents` call with GraphQL queries. + +**Indexing Services**: For projects using third-party indexing services with their own APIs, substitute their event APIs while maintaining the same pool key extraction logic. + +**Real-time Monitoring**: Convert from batch processing to real-time by setting up WebSocket subscriptions to new block events and processing pools as they're created. + +### Data Storage + +The current implementation prints metadata to console, but you might want to: +- Store results in a database for persistent analysis +- Send data to external APIs or webhooks +- Cache results to avoid re-processing known pools + +## Output + + +The output metadata object contains the following fields: + +```typescript +const metadata = { + id: pool.poolId, + key: pool.poolKey, + currency0: { + name: pool.currency0.name, + symbol: pool.currency0.symbol, + decimals: pool.currency0.decimals, + address: pool.currency0.wrapped.address, + }, + currency1: { + name: pool.currency1.name, + symbol: pool.currency1.symbol, + decimals: pool.currency1.decimals, + address: pool.currency1.wrapped.address, + }, + sqrtPriceX96: pool.sqrtRatioX96.toString(), + tick: pool.tickCurrent, + liquidity: pool.liquidity.toString(), + liquidityCurrency0: amount0.toString(), + liquidityCurrency1: amount1.toString(), + liquidityCurrency0HumanReadable: `${amount0HumanReadable} ${pool.currency0.symbol}`, + liquidityCurrency1HumanReadable: `${amount1HumanReadable} ${pool.currency1.symbol}`, + currency0Price, + currency1Price, + currency0PriceHumanReadable: `1 ${pool.currency0.symbol} = ${currency0Price} ${pool.currency1.symbol}`, + currency1PriceHumanReadable: `1 ${pool.currency1.symbol} = ${currency1Price} ${pool.currency0.symbol}`, + coinType, + appType +} +``` + +### Pool Identifiers +- `id`: Unique pool identifier hash +- `key`: Complete pool key object with currencies, fee, tickSpacing, and hooks + +### Currency Information +- `currency0/currency1.name`: Human-readable token name +- `currency0/currency1.symbol`: Token symbol (e.g., "USDC", "WETH") +- `currency0/currency1.decimals`: Token decimal places for formatting +- `currency0/currency1.address`: Contract address + +### Price Data +- `sqrtPriceX96`: Current pool price in Uniswap's sqrt format +- `tick`: Current tick (logarithmic price representation) +- `currency0Price`: Price of currency0 in terms of currency1 +- `currency1Price`: Price of currency1 in terms of currency0 +- `currency0PriceHumanReadable`: Formatted price string +- `currency1PriceHumanReadable`: Formatted price string + +### Liquidity Metrics +- `liquidity`: Total pool liquidity in Uniswap's internal format +- `liquidityCurrency0`: Amount of currency0 in the pool (raw) +- `liquidityCurrency1`: Amount of currency1 in the pool (raw) +- `liquidityCurrency0HumanReadable`: Formatted amount with symbol +- `liquidityCurrency1HumanReadable`: Formatted amount with symbol + +### Classification +- `coinType`: Type of Zora token ("ZORA_CREATOR_COIN" or "ZORA_V4_COIN") +- `appType`: Application ecosystem ("ZORA" or "TBA") + +## Use Cases for Metadata + +### Analytics & Monitoring +- **Price Tracking**: Monitor token prices and price movements over time +- **Liquidity Analysis**: Track total value locked (TVL) in various token pools +- **Market Discovery**: Identify new tokens entering the ecosystem + +### Trading & DeFi +- **Arbitrage Detection**: Compare prices across different pools or DEXs +- **Liquidity Provider Analysis**: Evaluate pool attractiveness for LP positions +- **Volume Analysis**: Track trading activity in specific token categories + +### Ecosystem Analysis +- **Token Categorization**: Understand which tokens belong to which ecosystems +- **Adoption Metrics**: Monitor growth of Zora and Base App token usage +- **Cross-chain Comparison**: Compare activity across different networks + +### Integration Projects +- **Portfolio Tracking**: Include Zora/Base App tokens in portfolio management apps +- **Wallet Integration**: Enhance wallet UIs with ecosystem-specific token information +- **DeFi Protocols**: Build lending, staking, or yield farming products around these tokens + +## Configuration + +### Environment Variables +- `RPC_URL`: Base chain RPC endpoint (required) + +### Block Range +Modify `START_BLOCK_NUMBER` and `END_BLOCK_NUMBER` in `index.ts` to scan different ranges or implement continuous monitoring. + +### Hook Addresses +Add new hook addresses to the classification logic as new Zora contracts are deployed. + +## Getting Started + +```bash +# Install dependencies +bun install + +# Set your RPC URL +export RPC_URL="your-base-rpc-endpoint" + +# Run the scanner +bun run index.ts +``` + +The output will display metadata for each discovered pool that matches the classification criteria. + +## Extensions & Modifications + +This starter guide can be extended in many ways: +- Add support for additional hook contracts as they're deployed +- Include historical price and volume data +- Add alerts for significant liquidity changes +- Build web interfaces for browsing discovered pools +- Integrate with portfolio tracking or trading applications + +The modular structure makes it easy to adapt individual components while maintaining the core pool discovery and classification logic. \ No newline at end of file diff --git a/docs/cookbook/base-builder-mcp.mdx b/docs/cookbook/base-builder-mcp.mdx index 4b299f3a9..04206c5fc 100644 --- a/docs/cookbook/base-builder-mcp.mdx +++ b/docs/cookbook/base-builder-mcp.mdx @@ -8,7 +8,3 @@ description: Learn practical AI prompting techniques to enhance your coding work import AiPrompt from "/snippets/prompt-library.mdx"; - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/cookbook/converting-customizing-mini-apps.mdx b/docs/cookbook/converting-customizing-mini-apps.mdx new file mode 100644 index 000000000..d3aebbd10 --- /dev/null +++ b/docs/cookbook/converting-customizing-mini-apps.mdx @@ -0,0 +1,160 @@ +--- +title: Make Your Web App a Mini App +description: Convert your downloaded frontend into a Mini App by integrating MiniKit, Smart Wallet, and Paymaster +--- + +# Add MiniKit to Your App + +You have a clean frontend. Now turn it into a Mini App. Vibe coding tools are great for UI, but they do not yet handle onchain pieces perfectly. Download your code, open it in Cursor or Claude Code, and add MiniKit to enable Smart Wallet, gasless transactions, and Base App integration. + + + Before you prompt your AI IDE, list where you want onchain interactions to + happen. Examples: mint a collectible from the Create screen, tip from the + Feed, swap on the Trade screen. This helps the AI place MiniKit components in + the right files. + + + + + Launch Cursor or Claude Code and open your Next.js project. Ensure it runs + locally first with npm run dev so the AI can follow a working + baseline. + + + Use the MiniKit existing-app integration page. Use the site dropdown to + “Copy page as Markdown for LLMs”, then paste that markdown into your AI IDE + as context. + + + Have the AI map your routes, layout, and component structure. Ask it to + propose exact integration points for MiniKit provider and hooks. + + + Add the MiniKit provider in app/layout.tsx (or your top-level + layout), wire basic hooks, and confirm no “login” button is added. Mini Apps + should feel native and sessionless. + + + Configure Coinbase Developer Platform Paymaster with environment variables. + Update your action flows to sponsor transactions, then test locally. + + + Run your app, test every onchain touchpoint, and commit changes with clear + messages. You are ready to deploy. + + + +## Helpful Prompts: + + + **Shortcut:** On any of the Base docs pages, use the dropdown to “Copy page as + Markdown for LLMs” and paste it directly into your AI chat. This gives the + model precise instructions and reduces hallucinations. + + + + +``` +Guide me through converting my existing web application into a Mini App: + +EXISTING APP ANALYSIS: + +My app is built with [DESCRIBE YOUR TECH STACK] + +Current features include: [LIST MAIN FEATURES] + +User authentication currently uses: [DESCRIBE AUTH METHOD] + +Data is stored using: [DESCRIBE DATA STORAGE] + +INTEGRATION REQUIREMENTS: + +Add MiniKit provider and Smart Wallet in app/layout.tsx + +Integrate Coinbase Paymaster for gasless transactions + +Place onchain actions on these screens: [LIST SCREENS] + +Configure env vars for Base and CDP + +Avoid adding any login button + +DEPLOYMENT CONFIGURATION: + +Prepare .env.local for local and Vercel for production + +Deploy with Vercel CLI + +Test Base App integration flows + +TROUBLESHOOTING SETUP: + +Log wallet and sponsorship states + +Add clear error toasts + +Provide rollbacks for provider or env misconfig + +Provide step-by-step instructions with exact code changes and file paths. Include troubleshooting tips for common conversion issues. +``` + + + + +Paste this into your AI IDE. Replace bracketed parts with your details. Include the MiniKit “existing app integration” markdown from docs.base.org as an additional message. + +``` +You are assisting me in converting a downloaded Next.js app into a Mini App for the Base App (TBA). + +ANALYZE THE PROJECT + +Inspect my Next.js structure (app/, components/, utils/) and identify the best integration points for: + +MiniKit provider in app/layout.tsx + +Smart Wallet setup + +Any hooks needed to access Base App context + +List every file you plan to modify and why. + +INTEGRATION REQUIREMENTS + +Use MiniKit as per the docs I will paste. + +Use Coinbase Developer Platform (CDP) Paymaster for gasless transactions. + +Use Smart Wallet. + +Do NOT create a login button. + +Keep the current routing and UI intact. + +ONCHAIN TOUCHPOINTS IN MY APP + +I want onchain interactions in these places: +[Describe screens and actions, e.g., Create screen: mint item; Feed: tip creator; Trade: swap token] + +IMPLEMENTATION PLAN + +Propose exact code changes with file paths and code blocks. + +- Add required env vars and tell me where to put them (.env.local). +- Update any server actions or API routes needed to call Paymaster safely. + +VALIDATION + +Provide a test checklist to verify: +- Provider mounts without errors +- Smart Wallet available + +Paymaster sponsoring a sample transaction + +No login button rendered + +Include troubleshooting tips for common issues (env vars, build errors, missing providers). + +Now request any files you need to see to proceed, and confirm assumptions before editing. +``` + + diff --git a/docs/cookbook/defi-your-app.mdx b/docs/cookbook/defi-your-app.mdx index b8c4deaaa..3d330fa65 100644 --- a/docs/cookbook/defi-your-app.mdx +++ b/docs/cookbook/defi-your-app.mdx @@ -10,7 +10,7 @@ When businesses and individuals make financial transactions, it often includes s ## Ready-to-use components - [``](/onchainkit/swap/swap): Swap assets directly within your app. - [``](/onchainkit/earn/earn): Generate yield directly within your app. -- [``](/onchainkit/fund/fund-card): Fund their wallets with fiat (via USDC, Apple Pay, or debit card) without leaving your app. +- [``](/onchainkit/fund/fund-card): Fund their wallets with fiat (via USDC, Apple Pay, or debit card) without leaving your appi - [``](/onchainkit/buy/buy): Purchase tokens directly within your app. @@ -324,7 +324,3 @@ If you're using these components, its likely you'll benefit from the following c ### Go Gasless For the `` and `` components, you can enable gasless transactions by setting the [`isSponsored`](/onchainkit/buy/buy#sponsor-gas-with-paymaster) prop to `true`. - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/cookbook/deploy-a-chain.mdx b/docs/cookbook/deploy-a-chain.mdx index b94accb03..aeeb299b3 100644 --- a/docs/cookbook/deploy-a-chain.mdx +++ b/docs/cookbook/deploy-a-chain.mdx @@ -81,7 +81,7 @@ Dedicated block explorer for your chain Maintain access to Base's **users**, **liquidity**, and **developer tools** while getting dedicated performance. Your Appchain integrates seamlessly with Smart Wallet, Paymaster, OnchainKit, and other Base ecosystem tools. - + Enable seamless account abstraction across Base Mainnet and your Appchain with unified user experiences. @@ -284,7 +284,3 @@ Discover the full ecosystem of Base developer tools and integrations **Next Steps**: After joining the waitlist, explore Base's developer documentation to understand how Appchains integrate with Smart Wallet, Paymaster, OnchainKit, and other ecosystem tools. - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/cookbook/essential-documentation-resources.mdx b/docs/cookbook/essential-documentation-resources.mdx new file mode 100644 index 000000000..a7f8a108f --- /dev/null +++ b/docs/cookbook/essential-documentation-resources.mdx @@ -0,0 +1,44 @@ +--- +title: Essential Documentation Resources +description: Navigate key documentation sources and understand when to use each resource for specific development needs +--- + +# Developer Resources + +When building a Mini App for the Base App (TBA) and Farcaster, there are five main documentation sources you’ll use again and again. These aren’t just docs—they’re tools that will help you solve problems, debug issues, and keep shipping. + +Don’t worry if they feel intimidating at first. You’re not expected to memorize them. Instead, you’ll learn to reference and search these docs as needed—and prompt your AI assistant to help you understand and use them. (We’ll show you how to do that in the next section.) + +Here’s the key idea: + +- **Base Docs** focus on the Base chain and app platform—everything from MiniKit to OnchainKit to smart account tools. +- **Coinbase Developer Platform (CDP)** gives you access to infrastructure tools—wallet APIs, Paymaster, and fiat onramps. Think of it as the backend services layer behind your app. + +Use the table below to get a feel for when to reach for each one: + +| Documentation | Helps with | When to use it | Example prompt | +| :-------------------------------- | :------------------------------------------------------- | :---------------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Base Docs | Base Chain, Base Account, Base App, OnchainKit, Cookbook | Finding MiniKit templates; sponsoring transactions via Base Account | `Using the Base documentation at docs.base.org, help me implement wallet connection in my Mini App. I need to: 1) Connect to a user's wallet, 2) Display their Basename if available, 3) Show their account balance. Provide complete React code using MiniKit SDK with TypeScript and explain each step.` | +| Coinbase Developer Platform (CDP) | Wallet services, Paymaster, onramps | Obtaining API keys; using the Paymaster endpoint; creating fiat onramps | `I want to sponsor transactions for my Mini App users using Coinbase Paymaster. Based on the CDP documentation, provide: 1) Setup instructions for API keys, 2) Complete code example for sponsoring a transaction, 3) Error handling best practices. Include both frontend and any required backend code.` | +| Next.js Docs | App router, page rendering, project structure | Deciding where to store assets; understanding routing and SSR/SSG | `Using Next.js 14 App Router, help me structure my Mini App with: 1) A main app page, 2) A settings page, 3) API routes for data fetching, 4) Proper file organization for components and assets. Provide the complete folder structure and explain routing patterns.` | +| Wagmi / Viem | Wallet integration and on-chain data access | Retrieving connected wallet address; reading on-chain data | `Using Wagmi v2 and Viem, create a React hook that: 1) Connects to the user's wallet, 2) Reads their ETH balance on Base, 3) Fetches their last 5 transactions, 4) Handles loading and error states. Include TypeScript types and proper error handling.` | +| Vercel V0 Docs | AI-powered UI generation with V0 | Adding environment variables; downloading generated code | `I generated a Mini App UI with V0 and want to deploy it. Help me: 1) Add environment variables for my API keys, 2) Download and integrate the V0 code into my local MiniKit project, 3) Deploy to Vercel with proper configuration. Provide step-by-step instructions.` | + +--- + +## Example Prompt for Understanding Key Tools + +Use prompts like the one below to help an AI assistant explain concepts from documentation: + +``` +Help me understand blockchain frontend development by explaining these concepts in simple terms: + + 1. What is Wagmi and how does it simplify wallet connections in React? + 2. How does Viem handle low-level blockchain operations? + 3. What's the difference between reading blockchain data and writing transactions? + 4. How do these tools work together in a typical Mini App? + +Focus on the Base network and provide practical examples for each concept. Include code snippets where helpful. +``` + +In the next section, we’ll show you how to prompt AI tools to make sense of all this documentation—so even the complex stuff becomes easier to use. diff --git a/docs/cookbook/go-gasless.mdx b/docs/cookbook/go-gasless.mdx index 4023208c2..c28f67d8c 100644 --- a/docs/cookbook/go-gasless.mdx +++ b/docs/cookbook/go-gasless.mdx @@ -404,7 +404,3 @@ This approach can greatly improve your dApp’s user experience by removing gas [simple NFT contract]: https://basescan.org/token/0x83bd615eb93ee1336aca53e185b03b54ff4a17e8 **Happy Building on Base!** - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/cookbook/introduction-to-mini-apps.mdx b/docs/cookbook/introduction-to-mini-apps.mdx new file mode 100644 index 000000000..5105fe78b --- /dev/null +++ b/docs/cookbook/introduction-to-mini-apps.mdx @@ -0,0 +1,28 @@ +--- +title: Introduction to Mini Apps +description: Mini Apps represent a paradigm shift in application development and distribution +--- + +# Vibe Coding a Mini App + +In the following sections you will be guided through the best practices for vibe coding a mini app game starting with te fundamentals of prompting, what documentation to leverage, to deployment and posting. + +Mini Apps are lightweight web applications that run across Farcaster clients like Farcaster and TBA. Posting your mini app and TBA will populate on Farcaster and visa versa + +# Introduction to Mini Apps + +Mini Apps are a new way to build and share apps—designed for the internet we actually use today: fast, social, and always on. They're not "mini" because they're small in impact, but because they're lightweight, easy to create, and instantly accessible. + +Instead of requiring users to download a full app or sign up for a new account, Mini Apps work directly inside the Base App. That means someone can open your app from a social feed, use it immediately, and share it with friends—all in one flow. Whether it's a poll, a marketplace, or a game, Mini Apps are designed to spread through networks, not app stores. + +They also come with powerful features out of the box: decentralized identity, built-in payments, and seamless social connection. You don't need to worry about distribution rules or platform lock-in. Your app lives on open infrastructure—and your users own their experience. + +# What is The Base App (TBA)? + +The Base App (TBA) is your new home onchain—a place where you can post, message, pay, trade, and build. It brings together everything people love about the internet, but without the walls of traditional platforms. And it's built entirely on open protocols, so anyone with an internet connection can join or create. + +TBA feels like a familiar social app, but underneath, it's a new kind of operating system for the onchain world. You can create content, earn from it, chat with friends, discover Mini Apps, and even launch your own—all in one place. It's multiplayer by default and designed for everyday people, not just crypto pros. + +Creators can earn directly from their posts through "coined content," where likes, shares, and interactions come with real upside. App developers can publish Mini Apps that are instantly discoverable in the social feed and inside chat. And users get a universal wallet, human-readable identity (Basenames), and gasless payments from the start. + +TBA isn't just a product—it's a protocol anyone can build on. You own your content, your apps, and your relationships. No gatekeepers, no downloads, no limits. diff --git a/docs/cookbook/launch-ai-agents.mdx b/docs/cookbook/launch-ai-agents.mdx index 04257b571..07a1cc93d 100644 --- a/docs/cookbook/launch-ai-agents.mdx +++ b/docs/cookbook/launch-ai-agents.mdx @@ -458,7 +458,3 @@ Congratulations! You've successfully launched an AI agent on Base with full onch Your AI agent is now part of the onchain economy, ready to operate 24/7 in the world of decentralized finance. Happy building on Base! - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/cookbook/launch-tokens.mdx b/docs/cookbook/launch-tokens.mdx index 27c6865dc..868e4f81b 100644 --- a/docs/cookbook/launch-tokens.mdx +++ b/docs/cookbook/launch-tokens.mdx @@ -421,7 +421,3 @@ Remember to always prioritize security, transparency, and community value when d --- Whether you choose a platform-based approach for speed and convenience, or custom development for maximum control, Base provides a robust foundation for token launches. Start with the approach that best fits your technical expertise and project requirements, and leverage Base's growing ecosystem to build successful token projects. - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/cookbook/llms-full.txt b/docs/cookbook/llms-full.txt new file mode 100644 index 000000000..3d5a60966 --- /dev/null +++ b/docs/cookbook/llms-full.txt @@ -0,0 +1,51 @@ +# https://docs.base.org/cookbook/llms-full.txt + +## Cookbook — Deep Guide for LLMs + +> Task‑oriented recipes for shipping features on Base. Use these guides to implement specific outcomes quickly. + +### What you can do here +- Implement end‑to‑end use cases (payments, social, tokens, AI agents) +- Follow AI‑assisted workflows and prompt patterns +- Build Mini Apps rapidly with MiniKit + prompts + +## Navigation (with brief descriptions) + +### Use Cases +- [Onboard Any User](./onboard-any-user) — UX patterns to remove friction +- [Accept Crypto Payments](./accept-crypto-payments) — Payments flows +- [Launch AI Agents](./launch-ai-agents) — Agent patterns and tooling +- [Launch Tokens](./launch-tokens) — Responsible token launch +- [Deploy a Chain](./deploy-a-chain) — OP Stack deployment +- [Onchain Social](./onchain-social) — Social growth mechanics +- [DeFi Your App](./defi-your-app) — Add finance features +- [Go Gasless](./go-gasless) — Sponsor gas +- [Base App Coins](./base-app-coins) — Coins in Base App +- [Testing Onchain Apps](./testing-onchain-apps) — Testing strategies + +### Build with AI +- [AI Prompting](./ai-prompting) — Prompt patterns +- [Base Builder MCP](./base-builder-mcp) — Build‑on‑Base MCP tool + +### Vibe Code a Mini App +- [Foundations](./introduction-to-mini-apps) — Mini Apps concepts +- [AI Fundamentals](./ai-powered-development-fundamentals) — AI workflows +- [Prompt Engineering](./mastering-ai-prompt-engineering) — Advanced prompts +- [Docs & Reading](./essential-documentation-resources) — How to read docs +- [AI‑Assisted Reading](./ai-assisted-documentation-reading) — Faster comprehension +- [Successful MiniApps in TBA](./successful-miniapps-in-tba) — Patterns +- [Build with Prompt](./minikit/build-your-mini-app-with-prompt) — Prompt‑driven build +- [Convert & Customize](./converting-customizing-mini-apps) — Adapt templates +- [Fork & Customize](./minikit/fork-and-customize) — Start from template +- [MiniKit: Install](./minikit/install) — Install +- [MiniKit: Add](./minikit/add-minikit) — Integrate +- [MiniKit: Configure Env](./minikit/configure-environment) — Env +- [MiniKit: Manifest CLI](./minikit/manifest-cli) — CLI +- [MiniKit: Create Manifest](./minikit/create-manifest) — Manifest +- [MiniKit: Add Frame Metadata](./minikit/add-frame-metadata) — Frames +- [MiniKit: Test & Deploy](./minikit/test-and-deploy) — Go live + +## Minimal Critical Code +None — recipe‑style docs; see product sections for code. + + diff --git a/docs/cookbook/llms.txt b/docs/cookbook/llms.txt new file mode 100644 index 000000000..5407c5376 --- /dev/null +++ b/docs/cookbook/llms.txt @@ -0,0 +1,43 @@ +# https://docs.base.org/cookbook/llms.txt + +## Cookbook Documentation + +> Practical, task‑oriented guides for building on Base: use cases, AI workflows, and MiniKit quickstarts. + +## Use Cases +- [Onboard Any User](./onboard-any-user) — Reduce friction and improve conversion +- [Accept Crypto Payments](./accept-crypto-payments) — Add payments flows +- [Launch AI Agents](./launch-ai-agents) — Ship messaging agents +- [Launch Tokens](./launch-tokens) — Responsible token launch patterns +- [Deploy a Chain](./deploy-a-chain) — OP Stack deployment guide +- [Onchain Social](./onchain-social) — Social growth mechanics +- [DeFi Your App](./defi-your-app) — Financial features +- [Go Gasless](./go-gasless) — Sponsor user gas +- [Base App Coins](./base-app-coins) — Coins in Base App +- [Testing Onchain Apps](./testing-onchain-apps) — Testing strategies + +## Build with AI +- [AI Prompting](./ai-prompting) — Prompt patterns +- [Base Builder MCP](./base-builder-mcp) — Build-on-Base MCP guide + +## Vibe Code a Mini App +- [Foundations](./introduction-to-mini-apps) — Mini Apps concepts +- [AI Fundamentals](./ai-powered-development-fundamentals) — AI workflows +- [Prompt Engineering](./mastering-ai-prompt-engineering) — Advanced prompting +- [Docs & Reading](./essential-documentation-resources) — Reading workflows +- [AI‑Assisted Reading](./ai-assisted-documentation-reading) — Faster comprehension +- [Successful MiniApps in TBA](./successful-miniapps-in-tba) — Patterns +- [Build with Prompt](./minikit/build-your-mini-app-with-prompt) — Prompt‑driven build +- [Convert & Customize](./converting-customizing-mini-apps) — Adapt templates +- [Fork & Customize](./minikit/fork-and-customize) — Start from templates +- [MiniKit: Install](./minikit/install) — Install +- [MiniKit: Add](./minikit/add-minikit) — Integrate +- [MiniKit: Configure Env](./minikit/configure-environment) — Env setup +- [MiniKit: Manifest CLI](./minikit/manifest-cli) — CLI +- [MiniKit: Create Manifest](./minikit/create-manifest) — Manifest +- [MiniKit: Add Frame Metadata](./minikit/add-frame-metadata) — Frames +- [MiniKit: Test & Deploy](./minikit/test-and-deploy) — Go live + +## Optional +- [Deploy a Chain](./deploy-a-chain) — Chain setup overview + diff --git a/docs/cookbook/mastering-ai-prompt-engineering.mdx b/docs/cookbook/mastering-ai-prompt-engineering.mdx new file mode 100644 index 000000000..a128ea76f --- /dev/null +++ b/docs/cookbook/mastering-ai-prompt-engineering.mdx @@ -0,0 +1,122 @@ +--- +title: Master Prompt Engineering +description: Learn best practices for writing effective prompts that generate useful code and UI components for your Mini App +--- + +## What makes a good prompt + +Prompting well is a core skill—it unlocks faster results, better apps, and more creativity. Here's how to improve: + +- **Start with a clear goal.** Be specific about what the app should do, who it's for, and how users will interact with it. +- **Give context.** Tell the AI what platform you're building for (TBA), what tools you're using (MiniKit, React, Tailwind), and what kind of experience you want to create. +- **Iterate in small steps.** Don't try to get everything perfect in one go. Run your prompt, review the output, and refine your request to get closer to your vision. +- **Use `llms.txt` files.** These are AI-friendly docs provided by many blockchain tools. Including their contents (or linking to them) gives the AI better reference data. +- **Read your prompt out loud.** If it sounds confusing to you, it'll confuse the AI too. +- **Save good prompts.** Treat them like building blocks. You'll reuse them across projects. + +## What makes a prompt effective + +- **State the goal and audience** so the model knows what to optimize. This keeps answers focused on the right use case instead of generic solutions. +- **Use sections and lists** to structure thinking and outputs, making it easier for the model to organize and for you to read. +- **Name users, roles, and permissions** to anchor behavior and prevent gaps in access planning. +- **List core features as outcomes** rather than vague ideas so results are actionable. +- **Define data entities and fields** to guide consistent responses and align on what's being stored or displayed. +- **Call out non-functional needs** like security and performance so they aren't forgotten in planning. +- **Provide tech preferences and constraints** to narrow options and avoid irrelevant suggestions. +- **Specify deliverables and format** so outputs are ready to use without heavy rework. +- **Phase the plan** to keep scope lean and shippable. _Example:_ Phase 1 = basic employee profiles and login, Phase 2 = payroll and payslips, Phase 3 = attendance and reviews. +- **Exclude out-of-scope items** to prevent feature creep and keep the project realistic. +- **Invite assumptions** when details are missing so progress continues without waiting on answers. + + + + ``` + I want to build a Mini App for the Base App (TBA)—a social platform where users can post, trade, message, and earn. Please create a responsive React component that includes: + + CORE FUNCTIONALITY: + + [Briefly describe your app's purpose, e.g., "a mood tracker that lets users log their feelings and share their vibe"] + + [List 2–3 features the app should include, like: mood selection, daily recap, emoji reactions] + + [Mention if the app needs to store or display any user data] + + TECH REQUIREMENTS: + + Use React with TypeScript + + Integrate wallet connection using MiniKit SDK + + Allow users to post to the TBA social feed + + Use Tailwind CSS for styling + + Ensure it's mobile-responsive + + VISUAL STYLE: + + Clean, modern design + + [Optional: Specify colors or mood — e.g., "calming blues and purples"] + + Include clear call-to-action buttons + + Optimize layout for mobile users + + SOCIAL FEATURES: + + Show user's Basename if connected + + Let users react to each other's posts (e.g., emoji or stickers) + + Enable easy sharing or reposting + + Please return complete, working code with clear comments that explain each part. + ``` + + + + + Create an effective prompt from a weak one using the template: + + ``` + You are an expert prompt engineer. I will give you (A) my rough/weak prompt and (B) a proven prompt template. Rewrite (A) to fully conform to (B), filling required sections with best-guess placeholders where my info is missing, and adding only what the template structure requires. + + Constraints: + - Keep my original intent, audience, and scope. + - Use clear sections and bullet points. + - Specify deliverables and output format. + - List assumptions you made at the end. + + Inputs: + (A) ROUGH_PROMPT: + --- + + --- + + (B) TEMPLATE: + --- + + --- + + Output: + - Final improved prompt that follows the template + - Short list of assumptions (if any) + ``` + + + + +## Additional Resources + +Here are essential resources to support your Mini App development journey: + +- [AI Prompting Guide](https://docs.base.org/onchainkit/guides/ai-prompting-guide#developers-guide-to-effective-ai-prompting) – Strategies for better AI-assisted development +- [MiniKit Documentation](https://docs.base.org/base-app/guides) – Complete guide to Mini App tools and APIs +- [Base Documentation](https://docs.base.org) – Technical documentation for the Base blockchain +- [OnchainKit Components](https://onchainkit.xyz) – Pre-built React components for onchain functionality +- [Vercel V0 Documentation](https://v0.vercel.com/docs) – Build UIs with natural language +- [Base Community Discord](https://discord.gg/buildonbase) – Connect with other builders +- [Farcaster Dev Resources](https://docs.farcaster.xyz) – Build with Farcaster social protocols +- [Base App Developer Portal](https://developers.base.org) – Tutorials, guides, and tools for Base developers +- https://v0.app/chat/design-planning-for-team-management-site-jazkKQyN4Ok diff --git a/docs/cookbook/minikit/add-frame-metadata.mdx b/docs/cookbook/minikit/add-frame-metadata.mdx new file mode 100644 index 000000000..90ea48e33 --- /dev/null +++ b/docs/cookbook/minikit/add-frame-metadata.mdx @@ -0,0 +1,45 @@ +--- +title: Add Frame Metadata +description: Define fc:frame metadata to render rich embeds with launch buttons +--- + +Metadata is critical for your app to be discovered. It enables rich embeds shared in the social feed and allows it to be properly indexed.Add `fc:frame` metadata so shared links render an embed with a launch button. + + +Image of social feed with Mini Apps + + + +Place the meta tag in `` and ensure all referenced assets use HTTPS. + + +### Next.js (generateMetadata) + +```ts app/layout.tsx +export async function generateMetadata(): Promise { + const URL = process.env.NEXT_PUBLIC_URL as string; + return { + title: process.env.NEXT_PUBLIC_ONCHAINKIT_PROJECT_NAME, + description: 'Generated by `create-onchain --mini`', + other: { + 'fc:frame': JSON.stringify({ + version: 'next', + imageUrl: process.env.NEXT_PUBLIC_APP_HERO_IMAGE, + button: { + title: `Launch ${process.env.NEXT_PUBLIC_ONCHAINKIT_PROJECT_NAME}`, + action: { + type: 'launch_frame', + name: process.env.NEXT_PUBLIC_ONCHAINKIT_PROJECT_NAME, + url: URL, + splashImageUrl: process.env.NEXT_PUBLIC_SPLASH_IMAGE, + splashBackgroundColor: process.env.NEXT_PUBLIC_SPLASH_BACKGROUND_COLOR, + }, + }, + }), + }, + }; +} +``` + + + diff --git a/docs/cookbook/minikit/add-minikit.mdx b/docs/cookbook/minikit/add-minikit.mdx new file mode 100644 index 000000000..77109244d --- /dev/null +++ b/docs/cookbook/minikit/add-minikit.mdx @@ -0,0 +1,68 @@ +--- +title: Add MiniKit +description: Wrap your app with MiniKitProvider and initialize the frame +--- + +Add the provider and initialize MiniKit in your main page. + +## Add MiniKitProvider + +Create `providers/MiniKitProvider.tsx` and wrap `app/layout.tsx`. + +```jsx providers/MiniKitProvider.tsx +'use client'; +import { MiniKitProvider } from '@coinbase/onchainkit/minikit'; +import { ReactNode } from 'react'; +import { base } from 'wagmi/chains'; + +export function MiniKitContextProvider({ children }: { children: ReactNode }) { + return ( + + {children} + + ); +} +``` + +Wrap your root layout: + +```jsx app/layout.tsx +import { MiniKitContextProvider } from '@/providers/MiniKitProvider'; + +export default function RootLayout({ children }: { children: React.ReactNode }) { + return ( + + + {children} + + + ); +} +``` + +## Initialize MiniKit in your page + +Use `useMiniKit()` to call `setFrameReady()` when your app is ready. + +```jsx app/page.tsx +'use client'; +import { useEffect } from 'react'; +import { useMiniKit } from '@coinbase/onchainkit/minikit'; + +export default function HomePage() { + const { setFrameReady, isFrameReady } = useMiniKit(); + + useEffect(() => { + if (!isFrameReady) setFrameReady(); + }, [isFrameReady, setFrameReady]); + + return
Your app content goes here
; +} +``` + + +The provider configures wagmi and react‑query and uses the Farcaster connector when available. + + + + diff --git a/docs/cookbook/minikit/build-your-mini-app-with-prompt.mdx b/docs/cookbook/minikit/build-your-mini-app-with-prompt.mdx new file mode 100644 index 000000000..fdaee4a90 --- /dev/null +++ b/docs/cookbook/minikit/build-your-mini-app-with-prompt.mdx @@ -0,0 +1,76 @@ +--- +title: Build Your Mini App With a Prompt +description: Use AI to draft your Mini App’s UI quickly—then iterate on layout, flows, and components before adding onchain features +--- + +# Your First Mini App Prompt + +Now that you’ve seen how to write effective prompts and where to find help, it’s time to build your first interface. In this section we’ll focus on the visuals: screens, flows, buttons, and layout. No onchain features yet—just getting the look and feel right so you can move fast. + + + Use a split-screen setup if your display allows. Keep **Vercel V0** open in + one window for generating UI, and a second window for **ChatGPT (or another + LLM)** plus a **Google Doc** for drafting and iterating on your prompt, and + skimming docs as needed. This reduces context switching and speeds up + iteration. + + + + + Start in your notes or a Google Doc. Write a short, focused prompt that + describes the core user journey, key screens, and any visual preferences. + Keep it specific and concise. + + + Arrange two windows side by side. Left: Vercel V0 for UI generation. Right: + your LLM and notes for refining the prompt and searching documentation. + + + Paste your prompt into V0 and generate an initial interface. Review the + output carefully: layout, component structure, naming, and accessibility. + + + Identify issues or gaps, refine your prompt, and regenerate targeted parts + (not always the entire UI). Repeat until the flow matches your intent. + + + When the interface feels right, download the code from V0. You’ll wire up + functionality and any onchain features later in your local environment. + + + +## Example: Frontend-Only Prompt Template + +Use (and adapt) this prompt to have V0 generate a clean starting interface for your Mini App. Keep it UI-first—no wallet hooks or chain calls yet. + +``` +I’m designing the first version of a Mini App UI. Please generate React + TypeScript components with Tailwind CSS that focus on layout and flow only (no data fetching, no blockchain code). + +## GOAL +- A simple interface that lets users complete a primary task in under 3 taps/clicks. + +## SCREENS + +- Home: brief header, primary call to action, and a simple list/grid of recent items. +- Create: a form with 2–3 inputs and a prominent submit button. +- Activity: a read-only feed/timeline showing recent actions in clean cards. + +## COMPONENTS + +- Reusable Button, Input, Card, and EmptyState components. +- A top-level Layout with responsive header and mobile navigation. + +## UX & STYLE + +- Mobile-first, accessible, keyboard-friendly. +- Clear hierarchy, generous spacing, and concise copy. +- Subtle loading states and disabled states for buttons. + +## DELIVERABLES + +- A small component tree with sensible file names. +- Minimal state management with placeholder handlers. +- Inline comments describing where to wire real logic later. + +Return complete, working code and explain key decisions briefly at the top of the file. +``` diff --git a/docs/cookbook/minikit/configure-environment.mdx b/docs/cookbook/minikit/configure-environment.mdx new file mode 100644 index 000000000..6efe5b3c6 --- /dev/null +++ b/docs/cookbook/minikit/configure-environment.mdx @@ -0,0 +1,119 @@ +--- +title: Configure Environment +description: Add required and optional environment variables for MiniKit +--- + +Add required variables to your local and deployment environments. + + + +These variables are essential for your MiniKit app to function: + + + The name of your Mini App as it appears to users + + + + The deployed URL of your application (must be HTTPS) + + + + Your Coinbase Developer Platform API key + + + + Generated during manifest creation for account association + + + + Generated during manifest creation for account association + + + + Generated during manifest creation for account association + + + + +These variables enhance your app's appearance and metadata: + + + URL to your app's icon (recommended: 48x48px PNG) + + + + Brief subtitle shown in app listings + + + + Detailed description of your app's functionality + + + + URL to splash screen image shown during app loading + + + + Hex color code for splash screen background (e.g., "#000000") + + + + Primary category for app discovery (e.g., "social", "gaming", "utility") + + + + Hero image URL displayed in cast previews + + + + Short, compelling tagline for your app + + + + Open Graph title for social sharing + + + + Open Graph description for social sharing + + + + Open Graph image URL for social media previews + + + + +### Copy-paste .env example + +```bash Terminal +# Required +NEXT_PUBLIC_ONCHAINKIT_PROJECT_NAME=YourAppName +NEXT_PUBLIC_URL=https://your-app.vercel.app +NEXT_PUBLIC_ONCHAINKIT_API_KEY=your_cdp_client_api_key + +# Generated by `npx create-onchain --manifest` +FARCASTER_HEADER=base64_header +FARCASTER_PAYLOAD=base64_payload +FARCASTER_SIGNATURE=hex_signature + +# Optional (appearance and metadata) +NEXT_PUBLIC_APP_ICON=https://your-app.vercel.app/icon.png +NEXT_PUBLIC_APP_SUBTITLE=Short subtitle +NEXT_PUBLIC_APP_DESCRIPTION=Describe what your app does +NEXT_PUBLIC_APP_SPLASH_IMAGE=https://your-app.vercel.app/splash.png +NEXT_PUBLIC_SPLASH_BACKGROUND_COLOR=#000000 +NEXT_PUBLIC_APP_PRIMARY_CATEGORY=social +NEXT_PUBLIC_APP_HERO_IMAGE=https://your-app.vercel.app/og.png +NEXT_PUBLIC_APP_TAGLINE=Play instantly +NEXT_PUBLIC_APP_OG_TITLE=Your App +NEXT_PUBLIC_APP_OG_DESCRIPTION=Fast, fun, social +NEXT_PUBLIC_APP_OG_IMAGE=https://your-app.vercel.app/og.png +``` + + +Ensure all referenced assets are publicly accessible via HTTPS. + + + + + diff --git a/docs/cookbook/minikit/create-manifest.mdx b/docs/cookbook/minikit/create-manifest.mdx new file mode 100644 index 000000000..83b3671a8 --- /dev/null +++ b/docs/cookbook/minikit/create-manifest.mdx @@ -0,0 +1,58 @@ +--- +title: Create Manifest +description: Expose the required /.well-known/farcaster.json endpoint +--- + +Your Mini App's Manifest proves ownership of your app and powers search, discovery, and rich embeds in the Base App. + +Below we'll create a Next.js route at `app/.well-known/farcaster.json/route.ts` that returns your accountAssociation and frame metadata. + + +Visit `https://yourdomain.com/.well-known/farcaster.json` to verify JSON output. + + + +```ts app/.well-known/farcaster.json/route.ts +function withValidProperties(properties: Record) { + return Object.fromEntries( + Object.entries(properties).filter(([_, value]) => (Array.isArray(value) ? value.length > 0 : !!value)) + ); +} + +export async function GET() { + const URL = process.env.NEXT_PUBLIC_URL as string; + return Response.json({ + accountAssociation: { + header: process.env.FARCASTER_HEADER, + payload: process.env.FARCASTER_PAYLOAD, + signature: process.env.FARCASTER_SIGNATURE, + }, + frame: withValidProperties({ + version: '1', + name: process.env.NEXT_PUBLIC_ONCHAINKIT_PROJECT_NAME, + subtitle: process.env.NEXT_PUBLIC_APP_SUBTITLE, + description: process.env.NEXT_PUBLIC_APP_DESCRIPTION, + screenshotUrls: [], + iconUrl: process.env.NEXT_PUBLIC_APP_ICON, + splashImageUrl: process.env.NEXT_PUBLIC_APP_SPLASH_IMAGE, + splashBackgroundColor: process.env.NEXT_PUBLIC_SPLASH_BACKGROUND_COLOR, + homeUrl: URL, + webhookUrl: `${URL}/api/webhook`, + primaryCategory: process.env.NEXT_PUBLIC_APP_PRIMARY_CATEGORY, + tags: [], + heroImageUrl: process.env.NEXT_PUBLIC_APP_HERO_IMAGE, + tagline: process.env.NEXT_PUBLIC_APP_TAGLINE, + ogTitle: process.env.NEXT_PUBLIC_APP_OG_TITLE, + ogDescription: process.env.NEXT_PUBLIC_APP_OG_DESCRIPTION, + ogImageUrl: process.env.NEXT_PUBLIC_APP_OG_IMAGE, + // use only while testing + noindex: true, + }), + }); +} +``` + +Review the full [Manifest guide](/mini-apps/features/manifest) and update all fields. Be sure to update your deployment environment with these values. + + + diff --git a/docs/cookbook/minikit/fork-and-customize.mdx b/docs/cookbook/minikit/fork-and-customize.mdx new file mode 100644 index 000000000..c621804c7 --- /dev/null +++ b/docs/cookbook/minikit/fork-and-customize.mdx @@ -0,0 +1,79 @@ +--- +title: Fork and Customize (Optional) +description: Learn how to fork a reference Mini App, customize it with your own flows and branding, and deploy it to production +--- + +## Fork, Customize and Deploy + +Forking a reference Mini App can save time, then you can layer in your specific flows and branding. + + + + Choose an example close to your use case. Update the + `/.well-known/farcaster.json` file, metadata in `app/layout.tsx` and + environment variables. + + + Adjust theme tokens, copy, and any contract addresses or endpoints required + for your flows. + + + Deploy with Vercel, test sharing and discovery, and validate wallet and + Paymaster flows end to end. + + + +To fork a reference Mini App, open your terminal and run the following command: + +```bash +git clone https://github.com/base/demos.git +cd demos/minikit/three-card-monte +npm install +npm run dev +``` + + + +``` +Help me fork and customize an existing Mini App for my specific needs: + +PROJECT REQUIREMENTS: + +I want to build: [DESCRIBE YOUR MINI APP IDEA] + +Target audience: [DESCRIBE YOUR USERS] + +Key differentiators: [WHAT MAKES YOUR APP UNIQUE] + +Branding requirements: [COLORS, STYLE, MESSAGING] + +CUSTOMIZATION TASKS: + +Fork appropriate Base Mini App repository + +Update miniapp.config.json metadata + +Customize theme and copy + +Configure contract addresses and env vars + +DEPLOYMENT PROCESS: + +Deploy with Vercel + +Set custom domain if needed + +Test Mini App behavior within Base App + +VERIFICATION CHECKLIST: + +Social sharing displays correctly + +Smart Wallet and gasless flows work + +All custom features function as expected + +Provide a file-by-file checklist, exact commands, and a short troubleshooting section. +``` + + diff --git a/docs/cookbook/minikit/install.mdx b/docs/cookbook/minikit/install.mdx new file mode 100644 index 000000000..4f2044a11 --- /dev/null +++ b/docs/cookbook/minikit/install.mdx @@ -0,0 +1,37 @@ +--- +title: Install +description: Add MiniKit to an existing Next.js app +--- + +Install MiniKit (part of OnchainKit) into your existing Next.js App Router project. + +## Prerequisites + + + +Your project uses the `app/` directory (App Router). + + +Your app is deployed and publicly accessible over HTTPS (e.g., Vercel). + + +You have access to your Farcaster custody wallet for manifest signing. + + +Sign in to Coinbase Developer Platform to get a Client API key. + + + +## Install dependencies + +```bash +npm install @coinbase/onchainkit +``` + + +Verify `@coinbase/onchainkit` appears in your `package.json`. + + + + + diff --git a/docs/cookbook/minikit/manifest-cli.mdx b/docs/cookbook/minikit/manifest-cli.mdx new file mode 100644 index 000000000..5cea173e2 --- /dev/null +++ b/docs/cookbook/minikit/manifest-cli.mdx @@ -0,0 +1,20 @@ +--- +title: Manifest (CLI) +description: Generate account association credentials with the CLI +--- + +Generate your Farcaster account association credentials. + +```bash Terminal +npx create-onchain --manifest +``` + +Follow the prompts to connect your Farcaster custody wallet, add your deployed URL, and sign. The CLI writes `FARCASTER_HEADER`, `FARCASTER_PAYLOAD`, and `FARCASTER_SIGNATURE` to your `.env`. + + + +While testing, set `noindex: true` in your manifest to avoid indexing. + + + + diff --git a/docs/cookbook/minikit/test-and-deploy.mdx b/docs/cookbook/minikit/test-and-deploy.mdx new file mode 100644 index 000000000..8f20fc13f --- /dev/null +++ b/docs/cookbook/minikit/test-and-deploy.mdx @@ -0,0 +1,35 @@ +--- +title: Test & Deploy +description: Validate your manifest and embed configuration, then share +--- + +Before sharing your Mini App, validate everything works. + +## Pre‑deployment checklist + +- App is deployed at a public HTTPS domain +- Environment variables are set on your deployment platform +- `/.well-known/farcaster.json` returns valid JSON +- `fc:frame` metadata renders a launch button when shared + +## Validation tools + +- Manifest Tool: `https://farcaster.xyz/~/developers/mini-apps/manifest` +- Embed Tool: `https://farcaster.xyz/~/developers/mini-apps/embed` + +## Share and test + +1. Create a cast with your app’s URL +2. Verify preview and launch button +3. Test launch and frame readiness + + + + Follow the launch checklist to get the most out of your mini app. + + + Common problems and fixes + + + + diff --git a/docs/cookbook/onboard-any-user.mdx b/docs/cookbook/onboard-any-user.mdx index aac3d7a2f..f5c73bcaa 100644 --- a/docs/cookbook/onboard-any-user.mdx +++ b/docs/cookbook/onboard-any-user.mdx @@ -141,7 +141,3 @@ Smart Wallet has advanced features such as sponsored transactions, spend permiss ## Conclusion By integrating ``, you offer a robust and user-friendly wallet onboarding experience. First-time users benefit from a seamless Smart Wallet creation flow, while returning users can quickly connect their wallets to get started with your onchain app. - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/cookbook/onchain-social.mdx b/docs/cookbook/onchain-social.mdx index 29a325615..94a3b8c9d 100644 --- a/docs/cookbook/onchain-social.mdx +++ b/docs/cookbook/onchain-social.mdx @@ -302,7 +302,7 @@ Mini Apps offer developers direct access to social distribution - you're buildin Transform your existing Next.js application into a Mini App without major restructuring. The process is straightforward and doesn't require rebuilding your entire application. - + Follow our comprehensive guide for integrating MiniKit into existing applications with step-by-step instructions, environment setup, and testing procedures. @@ -313,7 +313,7 @@ Follow our comprehensive guide for integrating MiniKit into existing application - Configure environment variables and deployment -For new projects, use the [MiniKit CLI](/wallet-app/build-with-minikit/quickstart) for automatic setup with all features pre-configured. +For new projects, use the [MiniKit CLI](/base-app/build-with-minikit/quickstart) for automatic setup with all features pre-configured. ## Advanced MiniKit Features @@ -321,16 +321,16 @@ For new projects, use the [MiniKit CLI](/wallet-app/build-with-minikit/quickstar Once you have your basic Mini App running, explore advanced capabilities: - + Send push notifications to users who have added your Mini App - + Implement Farcaster authentication for secure, persistent sessions - + Navigate users to Farcaster profiles and build social connections - + Allow users to save your Mini App for easy access @@ -340,27 +340,27 @@ Once you have your basic Mini App running, explore advanced capabilities: Building successful Mini Apps requires understanding social-specific patterns and common pitfalls: - + Design patterns and best practices for building social Mini Apps - + Common issues and solutions when developing Mini Apps - + Optimize loading times and user experience for mobile social environments - - Specific guidance for optimizing Mini Apps in Coinbase Wallet + + Specific guidance for optimizing Mini Apps in Base App ## Resources and Community - + Complete documentation for building Mini Apps with MiniKit - + Get started with MiniKit in under 10 minutes @@ -370,7 +370,3 @@ Building successful Mini Apps requires understanding social-specific patterns an Connect with other developers building onchain social apps - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/cookbook/successful-miniapps-in-tba.mdx b/docs/cookbook/successful-miniapps-in-tba.mdx new file mode 100644 index 000000000..5c936fa43 --- /dev/null +++ b/docs/cookbook/successful-miniapps-in-tba.mdx @@ -0,0 +1,82 @@ +--- +title: Mini App Successes in TBA +description: Understand how to leverage Base features strategically to create Mini Apps that thrive within the Base App ecosystem +--- + +# Optimizing for Base App Success + +Mini Apps succeed when they create the smoothest possible user experience. **MiniKit**, powered by **Base Account**, lets people use your app without having to sign in or build a separate "connect wallet" flow. It makes interacting with your app feel snappy and familiar. **Paymaster** removes first-time friction by covering gas so users can act right away. **Batched transactions** reduce pop-ups and approvals to a single, clear confirmation. Together, these components make Mini Apps feel cohesive and keep quality high across the Base ecosystem. + +| Component | Optimization Strategy | Implementation Focus | Success Metrics | +| :------------------- | :-------------------------------- | :------------------------------------ | :--------------------------- | +| Smart Accounts | Leverage universal wallet support | Design simplified onboarding flows | User conversion rates | +| OnchainKit | Import proven components | Customize for specific use cases | Development velocity | +| Paymaster (Gasless) | Strategic transaction sponsorship | Optimize cost vs. experience balance | User engagement rates | +| Batched Transactions | Reduce interaction complexity | Bundle related operations efficiently | Transaction completion rates | + + + + If you are using a custom wallet connection flow, you can replace it with + MiniKit + Base Account. + + + Evaluate which OnchainKit components can replace custom implementations to + align with Base UI patterns and speed up development. + + + If you app requires a user to mint a NFT or submit a transaction onchain, + ensure it is gasless by making that component interact with a Paymaster. + + + If you are using doing multiple transactions in a row, you can use batched + transactions to reduce the number of popups and approvals. + + + +Below is a prompt that will help you optimize your Mini App for maximum success in the Base App ecosystem. + +``` +Help me optimize my Mini App for maximum success in the Base App ecosystem: + +CURRENT APP ANALYSIS: + +- App type: [DESCRIBE YOUR MINI APP] +- Current user journey: [OUTLINE KEY USER STEPS] +- Main friction points: [IDENTIFY USER EXPERIENCE ISSUES] +- Target metrics: [DEFINE SUCCESS MEASUREMENTS] + +SMART ACCOUNT OPTIMIZATION: + +- Simplify user onboarding to leverage universal wallet support +- Remove unnecessary wallet complexity from user flows +- Design authentication that feels like traditional app login +- Optimize for users who don't understand blockchain concepts + +ONCHAINKIT INTEGRATION: + +- Identify which custom components can be replaced with OnchainKit +- Optimize component customization for brand consistency +- Implement proper error handling and loading states +- Ensure accessibility compliance for all components + +PAYMASTER STRATEGY: + +- Analyze which transactions should be sponsored for maximum impact +- Calculate sustainable sponsorship budget based on user volume +- Implement intelligent sponsorship rules and fallback options +- Design user communication about gasless benefits + +BATCHED TRANSACTION OPTIMIZATION: + +- Identify operations that can be combined for better UX +- Design single-signature flows for complex operations +- Handle partial failures and edge cases gracefully +- Optimize gas efficiency while maintaining reliability + +Provide specific implementation recommendations with code examples and measurable optimization targets for each component. + +``` + +``` + +``` diff --git a/docs/cookbook/testing-onchain-apps.mdx b/docs/cookbook/testing-onchain-apps.mdx new file mode 100644 index 000000000..bd42f12b9 --- /dev/null +++ b/docs/cookbook/testing-onchain-apps.mdx @@ -0,0 +1,297 @@ +--- +title: "Writing Tests" +description: "Learn how to write comprehensive blockchain tests with OnchainTestKit" +--- + +This guide covers everything you need to know about writing tests with OnchainTestKit, from basic wallet connections to complex transaction scenarios. NOTE that some of these examples may be different from what you implement depending on your frontend code. + +## Available Fixtures + +OnchainTestKit provides several fixtures for your tests: + + +Fixtures are automatically injected into your test functions and handle setup/teardown. + + +| Fixture | Type | Description | +|---------|------|-------------| +| `page` | `Page` | Playwright page object for browser automation | +| `metamask` | `MetaMask` | MetaMask wallet automation interface | +| `coinbase` | `CoinbaseWallet` | Coinbase wallet automation interface | +| `node` | `LocalNodeManager` | Local blockchain node manager | +| `smartContractManager` | `SmartContractManager` | Smart contract deployment and interaction | + +## Basic Wallet Operations + +### Connecting a Wallet + + + +```typescript +test("connect MetaMask", async ({ page, metamask }) => { + if (!metamask) throw new Error("MetaMask not initialized") + + // Open wallet connect modal + await page.getByTestId("ockConnectButton").first().click() + + // Select MetaMask from wallet options + await page + .getByTestId("ockModalOverlay") + .first() + .getByRole("button", { name: "MetaMask" }) + .click() + + // Handle MetaMask connection request + await metamask.handleAction(BaseActionType.CONNECT_TO_DAPP) +}) +``` + + + +```typescript +test("connect Coinbase Wallet", async ({ page, coinbase }) => { + if (!coinbase) throw new Error("Coinbase not initialized") + + // Open wallet connect modal + await page.getByTestId("ockConnectButton").first().click() + + // Select Coinbase from wallet options + await page + .getByTestId("ockModalOverlay") + .first() + .getByRole("button", { name: "Coinbase" }) + .click() + + // Handle Coinbase connection request + await coinbase.handleAction(BaseActionType.CONNECT_TO_DAPP) +}) +``` + + + +### Network Switching + +```typescript +test("switch networks", async ({ page, metamask }) => { + // Connect wallet first + await connectWallet(page, metamask) + + // Switch to Base Sepolia + await page.getByTestId("switch-to-base-sepolia").click() + + // Handle network switch in wallet + await metamask.handleAction(BaseActionType.SWITCH_NETWORK) +}) +``` + +## Transaction Testing + +### Basic Transaction + +```typescript +test("send transaction", async ({ page, metamask }) => { + // Connect wallet + await connectWallet(page, metamask) + + // Ideally, you have some purchase button + + // Submit transaction + await page.getByTestId("purchase-button").click() + + // Approve transaction in wallet + await metamask.handleAction(BaseActionType.HANDLE_TRANSACTION, { + approvalType: ActionApprovalType.APPROVE, + }) + + // Wait for confirmation + await expect(page.getByText("Transaction confirmed!")).toBeVisible() +}) +``` + +### Rejecting Transactions + +```typescript +test("reject transaction", async ({ page, metamask }) => { + await connectWallet(page, metamask) + + // Trigger transaction + await page.getByTestId("purchase-button").click() + + // Reject in wallet + await metamask.handleAction(BaseActionType.HANDLE_TRANSACTION, { + approvalType: ActionApprovalType.REJECT, + }) + + // Verify rejection handled + await expect(page.getByText("Transaction rejected")).toBeVisible() +}) +``` + +## Advanced Testing Patterns + +### Parallel Test Execution + +```typescript +test.describe.parallel("Parallel tests", () => { + test("test 1", async ({ page, metamask, node }) => { + console.log(`Test 1 using port: ${node?.port}`) + // Each test gets its own isolated node + }) + + test("test 2", async ({ page, metamask, node }) => { + console.log(`Test 2 using port: ${node?.port}`) + // Different port, isolated environment + }) +}) +``` + +## Best Practices + + + + +Always wait for UI updates after wallet actions: + +```typescript +// Good +await metamask.handleAction(BaseActionType.CONNECT_TO_DAPP) +await page.waitForSelector('[data-testid="wallet-connected"]') + +// Bad - might be flaky +await metamask.handleAction(BaseActionType.CONNECT_TO_DAPP) +expect(page.getByText("Connected")).toBeVisible() // Might fail +``` + + + +Always include error scenarios in your tests: + +```typescript +test("handle wallet rejection", async ({ page, metamask }) => { + try { + await metamask.handleAction(BaseActionType.CONNECT_TO_DAPP, { + approvalType: ActionApprovalType.REJECT, + }) + } catch (error) { + // Verify error is handled in UI + await expect(page.getByText("Connection rejected")).toBeVisible() + } +}) +``` + + + +## Debugging Tests + +### Visual Debugging + +```bash +# Run tests in headed mode +yarn playwright test --headed + +# Use Playwright Inspector +yarn playwright test --debug + +# Slow down execution +yarn playwright test --slow-mo=1000 +``` + +### Console Logs + +```typescript +test("debug test", async ({ page, metamask }) => { + // Log page errors + page.on('pageerror', error => { + console.error('Page error:', error) + }) + + // Log console messages + page.on('console', msg => { + console.log('Console:', msg.text()) + }) + + // Your test code +}) +``` + +## CI/CD Integration + +### GitHub Actions Example + +```yaml +name: E2E Tests + +on: + push: + branches: [main, develop] + pull_request: + branches: [main] + +jobs: + e2e: + runs-on: ubuntu-latest + timeout-minutes: 30 + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '18' + cache: 'npm' + + - name: Set up Corepack + yarn + run: | + npm install -g corepack + yarn set version 4.9.2 + + - name: Install root dependencies + run: yarn + + - name: Install Foundry + uses: foundry-rs/foundry-toolchain@v1 + + - name: Build contracts + run: | + cd smart-contracts + forge install foundry-rs/forge-std + forge install OpenZeppelin/openzeppelin-contracts + forge build + + - name: Install Playwright browsers + run: yarn playwright install --with-deps + + - name: Prepare wallet extensions + run: | + yarn prepare-metamask + yarn prepare-coinbase + + - name: Build application + run: | + echo "E2E_TEST_SEED_PHRASE=${{ secrets.E2E_TEST_SEED_PHRASE }}" > .env + echo "E2E_CONTRACT_PROJECT_ROOT=../smart-contracts" >> .env + yarn build + + - name: Install xvfb + run: sudo apt-get update && sudo apt-get install -y xvfb + + - name: Run E2E tests + env: + NODE_OPTIONS: '--dns-result-order=ipv4first' + run: xvfb-run --auto-servernum --server-args="-screen 0 1920x1080x24" yarn test:e2e +``` + +## Next Steps + +Start testing your onchain application today: + +1. Install OnchainTestKit in your project +2. Write your first test following the examples above +3. Integrate tests into your CI/CD pipeline +4. Expand test coverage as you build new features +5. See [example tests](https://github.com/coinbase/onchaintestkit/tree/master/example/frontend/e2e) +6. Access the [full docs here](https://onchaintestkit.xyz/) + +Remember: comprehensive testing leads to more reliable onchain applications and better user experiences. \ No newline at end of file diff --git a/docs/cookie-policy.mdx b/docs/cookie-policy.mdx index 91fb940ec..d1f094b6c 100644 --- a/docs/cookie-policy.mdx +++ b/docs/cookie-policy.mdx @@ -93,7 +93,3 @@ your request to privacy@base.org. We may update this Cookie Policy from time to time to reflect, for example, changes to the cookies we use or for other operational, legal or regulatory reasons. You can also revisit this page if you wish to keep yourself informed. - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/custom.css b/docs/custom.css index e11340b99..2b1a04f47 100644 --- a/docs/custom.css +++ b/docs/custom.css @@ -32,3 +32,8 @@ .home_header div p { margin-top: 10px; } + +/* Callout heading contrast in dark mode */ +.dark .callout :is(h1, h2, h3, h4, h5, h6) { + color: inherit; +} diff --git a/docs/docs.json b/docs/docs.json index 4a05c2896..5d2d26605 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -9,20 +9,14 @@ }, "favicon": "/logo/favicon.png", "contextual": { - "options": [ - "copy", - "chatgpt", - "claude" - ] + "options": ["copy", "chatgpt", "claude"] }, "api": { "playground": { "display": "simple" }, "examples": { - "languages": [ - "javascript" - ] + "languages": ["javascript"] } }, "navigation": { @@ -32,16 +26,11 @@ "groups": [ { "group": "Introduction", - "pages": [ - "get-started/base" - ] + "pages": ["get-started/base"] }, { "group": "Browse by", - "pages": [ - "get-started/products", - "get-started/use-cases" - ] + "pages": ["get-started/products", "get-started/use-cases"] }, { "group": "Quickstart", @@ -57,15 +46,13 @@ "pages": [ "get-started/get-funded", "get-started/base-services-hub", + "get-started/base-mentorship-program", "get-started/country-leads-and-ambassadors" ] }, { "group": "Build with AI", - "pages": [ - "get-started/ai-prompting", - "get-started/prompt-library" - ] + "pages": ["get-started/ai-prompting", "get-started/prompt-library"] } ], "global": { @@ -114,7 +101,8 @@ "group": "Flashblocks", "pages": [ "base-chain/flashblocks/apps", - "base-chain/flashblocks/node-providers" + "base-chain/flashblocks/node-providers", + "base-chain/flashblocks/docs" ] }, { @@ -171,7 +159,7 @@ }, { "anchor": "Explorer", - "href": "https://basescan.com/", + "href": "https://basescan.org/", "icon": "magnifying-glass" }, { @@ -187,14 +175,13 @@ "groups": [ { "group": "Introduction", - "pages": [ - "base-account/overview/what-is-base-account" - ] + "pages": ["base-account/overview/what-is-base-account"] }, { "group": "Quickstart", "pages": [ "base-account/quickstart/web", + "base-account/quickstart/web-react", "base-account/quickstart/mobile-integration" ] }, @@ -203,58 +190,116 @@ "pages": [ "base-account/guides/authenticate-users", "base-account/guides/accept-payments", + "base-account/improve-ux/batch-transactions", + "base-account/improve-ux/sponsor-gas/paymasters", + "base-account/improve-ux/sub-accounts", + "base-account/improve-ux/spend-permissions", + "base-account/improve-ux/magic-spend", + "base-account/guides/sign-and-verify-typed-data", + "base-account/improve-ux/sponsor-gas/erc20-paymasters" + ] + }, + { + "group": "Framework Integrations", + "pages": [ { - "group": "Sponsor gas", + "group": "Wagmi", "pages": [ - "base-account/improve-ux/sponsor-gas/paymasters", - "base-account/improve-ux/sponsor-gas/erc20-paymasters" + "base-account/framework-integrations/wagmi/setup", + "base-account/framework-integrations/wagmi/sign-in-with-base", + "base-account/framework-integrations/wagmi/base-pay", + "base-account/framework-integrations/wagmi/other-use-cases" ] }, { - "group": "Improve UX", + "group": "Privy", "pages": [ - "base-account/improve-ux/sub-accounts", - "base-account/improve-ux/spend-permissions", - "base-account/improve-ux/batch-transactions", - "base-account/improve-ux/magic-spend" + "base-account/framework-integrations/privy/setup", + "base-account/framework-integrations/privy/sub-accounts" ] - } - ] - }, - { - "group": "Framework Integrations", - "pages": [ - "base-account/framework-integrations/nextjs-with-wagmi", - "base-account/framework-integrations/nextjs-with-dynamic", - "base-account/framework-integrations/nextjs-with-privy" + }, + "base-account/framework-integrations/nextjs-with-dynamic" ] }, { "group": "Reference", "pages": [ { - "group": "Base Pay", + "group": "Account SDK", "pages": [ + "base-account/reference/core/createBaseAccount", "base-account/reference/base-pay/pay", "base-account/reference/base-pay/getPaymentStatus", - "base-account/reference/base-pay/DatacallbackURL" + "base-account/reference/core/getProvider", + "base-account/reference/spend-permission-utilities/requestSpendPermission", + "base-account/reference/spend-permission-utilities/prepareSpendCallData", + "base-account/reference/spend-permission-utilities/fetchPermissions", + "base-account/reference/spend-permission-utilities/getPermissionStatus", + "base-account/reference/spend-permission-utilities/requestRevoke", + "base-account/reference/spend-permission-utilities/prepareRevokeCallData", + "base-account/reference/core/generateKeyPair", + "base-account/reference/core/getKeypair", + "base-account/reference/core/getCryptoKeyAccount" ] }, { - "group": "Core", + "group": "Provider", "pages": [ - "base-account/reference/core/getProvider", - "base-account/reference/core/sdk-utilities", { - "group": "Provider RPC Methods", + "group": "Methods", "pages": [ "base-account/reference/core/provider-rpc-methods/request-overview", - "base-account/reference/core/provider-rpc-methods/wallet_sendCalls", "base-account/reference/core/provider-rpc-methods/wallet_connect", + "base-account/reference/core/provider-rpc-methods/wallet_sendCalls", + "base-account/reference/core/provider-rpc-methods/wallet_getCallsStatus", + "base-account/reference/core/provider-rpc-methods/wallet_getCapabilities", "base-account/reference/core/provider-rpc-methods/wallet_addSubAccount", "base-account/reference/core/provider-rpc-methods/wallet_getSubAccounts", "base-account/reference/core/provider-rpc-methods/coinbase_fetchPermissions", - "base-account/reference/core/provider-rpc-methods/standard-rpc-methods" + "base-account/reference/core/provider-rpc-methods/eth_accounts", + "base-account/reference/core/provider-rpc-methods/eth_requestAccounts", + "base-account/reference/core/provider-rpc-methods/eth_chainId", + "base-account/reference/core/provider-rpc-methods/eth_blockNumber", + "base-account/reference/core/provider-rpc-methods/eth_coinbase", + "base-account/reference/core/provider-rpc-methods/eth_getBalance", + "base-account/reference/core/provider-rpc-methods/eth_getTransactionCount", + "base-account/reference/core/provider-rpc-methods/eth_getTransactionByHash", + "base-account/reference/core/provider-rpc-methods/eth_getTransactionReceipt", + "base-account/reference/core/provider-rpc-methods/eth_getBlockByNumber", + "base-account/reference/core/provider-rpc-methods/eth_getBlockByHash", + "base-account/reference/core/provider-rpc-methods/eth_getBlockTransactionCountByNumber", + "base-account/reference/core/provider-rpc-methods/eth_getBlockTransactionCountByHash", + "base-account/reference/core/provider-rpc-methods/eth_sendTransaction", + "base-account/reference/core/provider-rpc-methods/eth_sendRawTransaction", + "base-account/reference/core/provider-rpc-methods/eth_estimateGas", + "base-account/reference/core/provider-rpc-methods/eth_gasPrice", + "base-account/reference/core/provider-rpc-methods/eth_feeHistory", + "base-account/reference/core/provider-rpc-methods/eth_getCode", + "base-account/reference/core/provider-rpc-methods/eth_getStorageAt", + "base-account/reference/core/provider-rpc-methods/eth_getLogs", + "base-account/reference/core/provider-rpc-methods/eth_getProof", + "base-account/reference/core/provider-rpc-methods/personal_sign", + "base-account/reference/core/provider-rpc-methods/eth_signTypedData_v4", + "base-account/reference/core/provider-rpc-methods/wallet_addEthereumChain", + "base-account/reference/core/provider-rpc-methods/wallet_switchEthereumChain", + "base-account/reference/core/provider-rpc-methods/wallet_watchAsset", + "base-account/reference/core/provider-rpc-methods/eth_getTransactionByBlockHashAndIndex", + "base-account/reference/core/provider-rpc-methods/eth_getTransactionByBlockNumberAndIndex", + "base-account/reference/core/provider-rpc-methods/eth_getUncleCountByBlockHash", + "base-account/reference/core/provider-rpc-methods/eth_getUncleCountByBlockNumber", + "base-account/reference/core/provider-rpc-methods/web3_clientVersion" + ] + }, + { + "group": "Capabilities", + "pages": [ + "base-account/reference/core/capabilities/overview", + "base-account/reference/core/capabilities/signInWithEthereum", + "base-account/reference/core/capabilities/atomic", + "base-account/reference/core/capabilities/flowControl", + "base-account/reference/core/capabilities/paymasterService", + "base-account/reference/core/capabilities/auxiliaryFunds", + "base-account/reference/core/capabilities/datacallback" ] } ] @@ -327,6 +372,137 @@ ] } }, + { + "tab": "Base App", + "groups": [ + { + "group": "Introduction", + "pages": ["base-app/introduction/beta-faq"] + }, + { + "group": "Chat Agents", + "pages": [ + "base-app/agents/why-agents", + "base-app/agents/chat-agents", + "base-app/agents/building-quality-agents", + "base-app/agents/getting-started", + "base-app/agents/content-types", + "base-app/agents/transaction-trays", + "base-app/agents/deeplinks", + "base-app/agents/x402-agents", + "base-app/agents/mini-apps-and-agents", + "base-app/agents/best-practices", + "base-app/agents/getting-featured" + ] + } + ] + }, + { + "tab": "Mini Apps", + "groups": [ + { + "group": "Introduction", + "pages": ["mini-apps/overview"] + }, + { + "group": "Quickstart", + "pages": [ + { + "group": "New Apps", + "pages": [ + "mini-apps/quickstart/new-apps/install", + "mini-apps/quickstart/new-apps/deploy", + "mini-apps/quickstart/new-apps/create-manifest", + "mini-apps/quickstart/new-apps/features" + ] + }, + { + "group": "Existing Apps", + "pages": [ + "mini-apps/quickstart/existing-apps/install", + "mini-apps/quickstart/existing-apps/add-minikit", + "mini-apps/quickstart/existing-apps/configure-environment", + "mini-apps/quickstart/existing-apps/manifest-cli", + "mini-apps/quickstart/existing-apps/create-manifest", + "mini-apps/quickstart/existing-apps/add-frame-metadata", + "mini-apps/quickstart/existing-apps/test-and-deploy" + ] + }, + "mini-apps/quickstart/launch-checklist" + ] + }, + { + "group": "Design Guidelines", + "pages": [ + "mini-apps/design-ux/best-practices", + "mini-apps/design-ux/onchainkit" + ] + }, + { + "group": "Growth Playbook", + "pages": [ + "mini-apps/growth/optimize-onboarding", + "mini-apps/growth/build-viral-mini-apps", + "mini-apps/growth/data-driven-growth", + "mini-apps/growth/rewards" + ] + }, + { + "group": "Features", + "pages": [ + "mini-apps/features/overview", + "mini-apps/features/manifest", + "mini-apps/features/authentication", + "mini-apps/features/context", + "mini-apps/features/embeds-and-previews", + "mini-apps/features/search-and-discovery", + "mini-apps/features/sharing-and-social-graph", + "mini-apps/features/notifications", + "mini-apps/features/links" + ] + }, + { + "group": "Troubleshooting", + "pages": [ + "mini-apps/troubleshooting/common-issues", + "mini-apps/troubleshooting/base-app-compatibility" + ] + }, + { + "group": "Technical Reference", + "pages": [ + { + "group": "MiniKit", + "pages": [ + "mini-apps/technical-reference/minikit/overview", + "mini-apps/technical-reference/minikit/provider-and-initialization", + { + "group": "Hooks", + "pages": [ + "mini-apps/technical-reference/minikit/hooks/useMiniKit", + "mini-apps/technical-reference/minikit/hooks/useOpenUrl", + "mini-apps/technical-reference/minikit/hooks/useClose", + "mini-apps/technical-reference/minikit/hooks/usePrimaryButton", + "mini-apps/technical-reference/minikit/hooks/useViewProfile", + "mini-apps/technical-reference/minikit/hooks/useComposeCast", + "mini-apps/technical-reference/minikit/hooks/useViewCast", + "mini-apps/technical-reference/minikit/hooks/useAuthenticate", + "mini-apps/technical-reference/minikit/hooks/useAddFrame", + "mini-apps/technical-reference/minikit/hooks/useNotification" + ] + } + ] + } + ] + }, + { + "group": "Resources", + "pages": [ + "mini-apps/resources/resources" + ] + } + ] + }, { "tab": "OnchainKit", "groups": [ @@ -362,7 +538,8 @@ "onchainkit/guides/themes", "onchainkit/guides/use-basename-in-onchain-app", "onchainkit/guides/using-ai-powered-ides", - "onchainkit/guides/ai-prompting-guide" + "onchainkit/guides/ai-prompting-guide", + "onchainkit/guides/testing-with-onchaintestkit" ] }, { @@ -378,27 +555,19 @@ "pages": [ { "group": "Appchain", - "pages": [ - "onchainkit/appchain/bridge" - ] + "pages": ["onchainkit/appchain/bridge"] }, { "group": "Buy", - "pages": [ - "onchainkit/buy/buy" - ] + "pages": ["onchainkit/buy/buy"] }, { "group": "Checkout", - "pages": [ - "onchainkit/checkout/checkout" - ] + "pages": ["onchainkit/checkout/checkout"] }, { "group": "Earn", - "pages": [ - "onchainkit/earn/earn" - ] + "pages": ["onchainkit/earn/earn"] }, { "group": "Fund", @@ -479,15 +648,11 @@ }, { "group": "Token", - "pages": [ - "onchainkit/api/get-tokens" - ] + "pages": ["onchainkit/api/get-tokens"] }, { "group": "Wallet", - "pages": [ - "onchainkit/api/get-portfolios" - ] + "pages": ["onchainkit/api/get-portfolios"] } ] }, @@ -547,9 +712,7 @@ }, { "group": "Token", - "pages": [ - "onchainkit/token/format-amount" - ] + "pages": ["onchainkit/token/format-amount"] }, { "group": "Wallet", @@ -606,35 +769,6 @@ ] } }, - { - "tab": "Base App", - "groups": [ - { - "group": "Introduction", - "pages": [ - "wallet-app/introduction/getting-started", - "wallet-app/introduction/mini-apps", - "wallet-app/introduction/beta-faq" - ] - }, - { - "group": "Build with MiniKit", - "pages": [ - "wallet-app/build-with-minikit/overview", - "wallet-app/build-with-minikit/quickstart", - "wallet-app/build-with-minikit/existing-app-integration", - "wallet-app/build-with-minikit/debugging" - ] - }, - { - "group": "Guides", - "pages": [ - "wallet-app/guides/thinking-social", - "wallet-app/guides/chat-agents" - ] - } - ] - }, { "tab": "Cookbook", "groups": [ @@ -648,32 +782,68 @@ "cookbook/deploy-a-chain", "cookbook/onchain-social", "cookbook/defi-your-app", - "cookbook/go-gasless" + "cookbook/go-gasless", + "cookbook/base-app-coins", + "cookbook/testing-onchain-apps" ] }, { "group": "Build with AI", + "pages": ["cookbook/ai-prompting", "cookbook/base-builder-mcp"] + }, + { + "group": "Vibe Code a Mini App", "pages": [ - "cookbook/ai-prompting", - "cookbook/base-builder-mcp" + { + "group": "Foundations", + "pages": [ + "cookbook/introduction-to-mini-apps", + "cookbook/ai-powered-development-fundamentals", + "cookbook/mastering-ai-prompt-engineering" + ] + }, + { + "group": "Documentation & Reading", + "pages": [ + "cookbook/essential-documentation-resources", + "cookbook/ai-assisted-documentation-reading" + ] + }, + { + "group": "Building", + "pages": [ + "cookbook/successful-miniapps-in-tba", + "cookbook/minikit/build-your-mini-app-with-prompt", + "cookbook/converting-customizing-mini-apps", + "cookbook/minikit/fork-and-customize" + ] + }, + { + "group": "Add MiniKit to Your App", + "pages": [ + "cookbook/minikit/install", + "cookbook/minikit/add-minikit", + "cookbook/minikit/configure-environment", + "cookbook/minikit/manifest-cli", + "cookbook/minikit/create-manifest", + "cookbook/minikit/add-frame-metadata", + "cookbook/minikit/test-and-deploy" + ] + } ] } ] }, { "tab": "Showcase", - "pages": [ - "showcase" - ] + "pages": ["showcase"] }, { "tab": "Learn", "groups": [ { "group": "Building Onchain", - "pages": [ - "learn/welcome" - ] + "pages": ["learn/welcome"] }, { "group": "Onchain Concepts", @@ -762,9 +932,7 @@ }, { "group": "Deploy with Fleek", - "pages": [ - "learn/onchain-app-development/deploy-with-fleek" - ] + "pages": ["learn/onchain-app-development/deploy-with-fleek"] } ] }, @@ -914,15 +1082,11 @@ }, { "group": "Events", - "pages": [ - "learn/events/hardhat-events-sbs" - ] + "pages": ["learn/events/hardhat-events-sbs"] }, { "group": "Address and Payable", - "pages": [ - "learn/address-and-payable/address-and-payable" - ] + "pages": ["learn/address-and-payable/address-and-payable"] } ] }, @@ -1058,9 +1222,7 @@ }, { "group": "Exercise Contracts", - "pages": [ - "learn/exercise-contracts" - ] + "pages": ["learn/exercise-contracts"] } ] } @@ -1095,6 +1257,7 @@ "socials": { "x": "https://x.com/base", "github": "https://github.com/base", + "reddit": "https://www.reddit.com/r/BASE/", "linkedin": "https://linkedin.com/company/coinbase" }, "links": [ @@ -1122,6 +1285,10 @@ ] }, "redirects": [ + { + "source": "/privacy-policy-2025", + "destination": "/privacy-policy" + }, { "source": "/", "destination": "/get-started/base" @@ -1140,23 +1307,23 @@ }, { "source": "/builderkits/minikit/debugging", - "destination": "/wallet-app/build-with-minikit/debugging" + "destination": "/base-app/build-with-minikit/debugging" }, { "source": "/builderkits/minikit/existing-app-integration", - "destination": "/wallet-app/build-with-minikit/existing-app-integration" + "destination": "/base-app/build-with-minikit/existing-app-integration" }, { "source": "/builderkits/minikit/overview", - "destination": "/wallet-app/build-with-minikit/overview" + "destination": "/base-app/build-with-minikit/overview" }, { "source": "/builderkits/minikit/quickstart", - "destination": "/wallet-app/build-with-minikit/quickstart" + "destination": "/base-app/build-with-minikit/quickstart" }, { "source": "/builderkits/minikit/thinking-social", - "destination": "/wallet-app/guides/thinking-social" + "destination": "/base-app/guides/thinking-social" }, { "source": "/builderkits/onchainkit/:slug*", @@ -1480,7 +1647,7 @@ }, { "source": "/cookbook/growth/deploy-to-vercel", - "destination": "/wallet-app/build-with-minikit/quickstart#deploying-to-vercel" + "destination": "/base-app/build-with-minikit/quickstart#deploying-to-vercel" }, { "source": "/cookbook/growth/email-campaigns", @@ -1592,7 +1759,7 @@ }, { "source": "/cookbook/use-case-guides/deploy-to-vercel", - "destination": "/wallet-app/build-with-minikit/quickstart#deploying-to-vercel" + "destination": "/base-app/build-with-minikit/quickstart#deploying-to-vercel" }, { "source": "/cookbook/use-case-guides/finance/access-real-time-asset-data-pyth-price-feeds", @@ -1880,7 +2047,7 @@ }, { "source": "/smart-wallet/concepts/features/optional/profiles", - "destination": "/base-account/improve-ux/sub-accounts" + "destination": "/base-account/reference/core/capabilities/datacallback" }, { "source": "/smart-wallet/concepts/usage-details/popups", @@ -1948,7 +2115,7 @@ }, { "source": "/smart-wallet/guides/profiles", - "destination": "/base-account/improve-ux/sub-accounts" + "destination": "/base-account/reference/core/capabilities/datacallback" }, { "source": "/smart-wallet/technical-reference/sdk", @@ -1968,7 +2135,7 @@ }, { "source": "/smart-wallet/technical-reference/profiles-reference", - "destination": "/base-account/improve-ux/sub-accounts" + "destination": "/base-account/reference/core/capabilities/datacallback" }, { "source": "/smart-wallet/basenames/:slug*", @@ -2184,24 +2351,116 @@ }, { "source": "/wallet-app/beta-faq", - "destination": "/wallet-app/introduction/beta-faq" + "destination": "/base-app/introduction/beta-faq" }, { "source": "/wallet-app/getting-started", - "destination": "/wallet-app/introduction/getting-started" + "destination": "/base-app/introduction/getting-started" }, { "source": "/wallet-app/mini-apps", - "destination": "/wallet-app/introduction/mini-apps" + "destination": "/base-app/introduction/mini-apps" }, { "source": "/wallet-app/chat-agents", "destination": "/wallet-app/guides/chat-agents" + }, + { + "source": "/base-account/framework-integrations/nextjs-with-wagmi", + "destination": "/base-account/framework-integrations/wagmi/setup" + }, + { + "source": "/base-account/framework-integrations/nextjs-with-privy", + "destination": "/base-account/framework-integrations/privy/setup" + }, + { + "source": "/wallet-app/:slug*", + "destination": "/base-app/:slug*" + }, + { + "source": "/base-app/introduction/what-are-mini-apps", + "destination": "/mini-apps/overview" + }, + { + "source": "/base-app/introduction/why-mini-apps", + "destination": "/mini-apps/overview" + }, + { + "source": "/base-app/miniapps/overview", + "destination": "/mini-apps/technical-reference/minikit/overview" + }, + { + "source": "/base-app/build-with-minikit/overview", + "destination": "/mini-apps/technical-reference/minikit/overview" + }, + { + "source": "/base-app/miniapps/existing-app-integration", + "destination": "/mini-apps/quickstart/existing-apps/install" + }, + { + "source": "/base-app/build-with-minikit/existing-app-integration", + "destination": "/mini-apps/quickstart/existing-apps/install" + }, + { + "source": "/base-app/miniapps/quickstart", + "destination": "/mini-apps/quickstart/new-apps/install" + }, + { + "source": "/base-app/build-with-minikit/quickstart", + "destination": "/mini-apps/quickstart/new-apps/install" + }, + { + "source": "/base-app/miniapps/mini-apps", + "destination": "/mini-apps/overview" + }, + { + "source": "/base-app/build-with-minikit/mini-apps", + "destination": "/mini-apps/overview" + }, + { + "source": "/base-app/miniapps/search-and-discovery", + "destination": "/mini-apps/features/search-and-discovery" + }, + { + "source": "/base-app/build-with-minikit/search-and-discovery", + "destination": "/mini-apps/features/search-and-discovery" + }, + { + "source": "/base-app/miniapps/sharing-your-miniapp", + "destination": "/mini-apps/features/sharing-and-social-graph" + }, + { + "source": "/base-app/build-with-minikit/sharing-your-miniapp", + "destination": "/mini-apps/features/sharing-and-social-graph" + }, + { + "source": "/base-app/miniapps/how-manifest-work", + "destination": "/mini-apps/features/manifest" + }, + { + "source": "/base-app/build-with-minikit/how-manifest-work", + "destination": "/mini-apps/features/manifest" + }, + { + "source": "/base-app/miniapps/thinking-social", + "destination": "/mini-apps/growth/build-viral-mini-apps" + }, + { + "source": "/base-app/build-with-minikit/thinking-social", + "destination": "/mini-apps/growth/build-viral-mini-apps" + }, + { + "source": "/base-app/miniapps/debugging", + "destination": "/mini-apps/troubleshooting/common-issues" + }, + { + "source": "/base-app/build-with-minikit/debugging", + "destination": "/mini-apps/troubleshooting/common-issues" } ], "integrations": { "ga4": { - "measurementId": "G-TKCM02YFWN" + "measurementId": "G-TKCM02YFWN" } -} + } } diff --git a/docs/get-started/ai-prompting.mdx b/docs/get-started/ai-prompting.mdx index b219b4cab..6c5725ec4 100644 --- a/docs/get-started/ai-prompting.mdx +++ b/docs/get-started/ai-prompting.mdx @@ -7,7 +7,3 @@ description: How to use AI-powered IDEs to generate code for OnchainKit. import AiPowered from "/snippets/ai-powered.mdx"; - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/get-started/base-mentorship-program.mdx b/docs/get-started/base-mentorship-program.mdx new file mode 100644 index 000000000..9573b860a --- /dev/null +++ b/docs/get-started/base-mentorship-program.mdx @@ -0,0 +1,35 @@ +--- +title: Base Mentorship Program +description: Connect with experienced builders and industry leaders to accelerate your journey on Base +--- + +## Program Purpose + +The Base Mentorship Program connects builders with top operators and investors who have been there before, sharing lessons learned, making introductions, and helping you avoid costly mistakes. Mentors work closely with the Base team to ensure their advice is actionable and aligned with your stage and goals. + +## Base Mentors + +Base Mentors are experienced founders, operators, and subject matter experts across key verticals who volunteer their time to support builders in the Base ecosystem. They offer guidance on topics like product strategy, go-to-market, technical architecture, growth, fundraising, and more. We work directly with builders to pair them with the right mentor for their needs. + +### Current Mentors + +| Name | Affiliation | +| ----- | ----- | +| Alok Vasudev | [Standard Crypto](https://www.standardcrypto.vc/) | +| Dariush Aghai | [Study Hall Creative](%20https://studyhall.design/about) | +| David Espinel | [Social Graph Ventures](https://www.socialgraph.vc/team) | +| Ian Dutra | [a16z crypto](https://a16zcrypto.com/team/ian-dutra/) | +| Jack Gorman | [Variant](https://variant.fund/people/jack-gorman/) | +| Jakub Rusiecki | [1kx](https://1kx.network/team/jakub-rusiecki) | +| Jay Drain Jr | [a16z crypto](https://a16zcrypto.com/team/jay-drain/) | +| Jonathan Wu | [Asylum Ventures](https://www.asylum.vc/) | +| Katie Chiou | [Archetype](https://www.archetype.fund/team) | +| Maria Shen | [Electric Capital](https://www.electriccapital.com/team/maria-shen-c) | +| Mark Beylin | [Haun Ventures](https://www.haun.co/team/mark-beylin) | +| Mike Tomaino | [1confirmation](https://www.1confirmation.com/about) | +| Nina Suthers | [Variant](https://variant.fund/people/nina-suthers-2/) | +| Nick Tomaino | [1confirmation](https://www.1confirmation.com/about) | +| Simone “Limone” Staffa | [Builders Garden](https://www.builders.garden/#mission) | +| Winnie Lau | [Strobe Ventures](https://www.strobe.fund/team/winnie-lau) | + +**Program Disclaimer**: *The Base Mentorship Program is an educational/networking initiative only. Participation does not constitute investment advice, a recommendation, an offer or solicitation to buy/sell any security, or an endorsement by Base/Coinbase of any project, token, or investment. Base does not arrange, negotiate, or receive compensation for investments and is not acting as a broker, dealer, or finder. Mentors participate in a personal capacity. If a mentor later chooses to invest in a project, any discussion must occur independently of this program and without Base involvement.* diff --git a/docs/get-started/base-services-hub.mdx b/docs/get-started/base-services-hub.mdx index 7d751d940..5fb24b50c 100644 --- a/docs/get-started/base-services-hub.mdx +++ b/docs/get-started/base-services-hub.mdx @@ -22,24 +22,25 @@ Thank you to all the teams supporting the Base ecosystem and its builders! If yo | [Api3](https://www.api3.org/) | Oracles / Data Infrastructure | API3 is an oracle service that delivers Real World Price Feeds to your smart contract. The price feeds provided allow dapps to regain lost value with Oracle Extractable Value built in to the feed. | If you are a lending dapp deploying on BASE, stable coin, morpho curator, borrow/lending dapp we will provide oracle services to your markets. | If you are a lending dapp deploying on BASE, stable coin, morpho curator, borrow/lending dapp we will provide oracle services to your markets.If you are a lending dapp deploying on BASE, stable coin, morpho curator, borrow/lending dapp we will provide oracle services to your markets. | Contact [BillyJitsu](http://t.me/billyjitsu) or apply here | | [Artemis](http://artemis.xyz/) | Onchain Analytics | Artemis standardizes digital finance data into a single open data platform. Metrics that matter for digital finance. All in one place. | Artemis is offering free, out-of-the-box onchain metrics dashboards for Base builder's applications. | Please fill out this [Google Form](https://forms.gle/ZDS9LkxSBJVJonR36) with your application metadata and contract information.

Artemis will contact your email once your application dashboard has been created. | | [Cantina](https://cantina.xyz/welcome) | Security | Cantina is the one-stop shop for the highest quality security researchers and solutions. Reduce the likelihood of hacks, time spent, and context lost. | 10% off all services including audits, audit competitions, pen-testing, architecture reviews, fuzzing/unit/e2e testing 50% off of bug bounty hosting for the first year. | Submit info to: cantina.xyz/introduction/base-cantina | -| [Dune](https://www.dune.com/home) | Data Analytics - Data API - Developer Tools | Dune is a web3 data platform that lets anyone query, visualize, and share blockchain data. It’s used by analysts, builders, and communities to make onchain insights accessible and actionable. | 20% on any annual plans. | Email support@dune.com with your company/project name using your work email. | -| [Dynamic](https://Dynamic.xyz) | Wallet Infrastructure | Dynamic combines authentication, smart wallets, and secure key management into one flexible SDK. Get the most multi-chain coverage across chains and third-party wallets. | Base builders can get 3 months free of our $99/month Growth plan, which supports up to 2,000 MAUs. | Fill out this form in detail. Once the team receives your app, we'll review and get in touch. Note: One discount available per team. https://d9hc0.share.hsforms.com/2CIpNaX14T1Cv1erD_2ou1A | -| [FailSafe](https://www.getfailsafe.com) | Infrastructure, CyberSecurity, Audits | FailSafe provides real-time blockchain risk monitoring and smart contract audit solutions for protocols, stablecoins, and digital asset platforms across global markets. | Get $3,000 off your first smart contract audit or monitoring subscription with FailSafe. Ideal for Base stablecoin issuers, or DeFi platforms looking to strengthen on-chain security and protect funds | Email wui@getfailsafe.com with github repo of codebase for a quote on a security audit. discount will be applied once a commercial contract is signed. | +| [Dune](www.dune.com/home) | Data Analytics - Data API - Developer Tools | Dune is a web3 data platform that lets anyone query, visualize, and share blockchain data. It’s used by analysts, builders, and communities to make onchain insights accessible and actionable. | 20% on any annual plans. | Email support@dune.com with your company/project name using your work email. | +| [Dynamic](Dynamic.xyz) | Wallet Infrastructure | Dynamic combines authentication, smart wallets, and secure key management into one flexible SDK. Get the most multi-chain coverage across chains and third-party wallets. | Base builders can get 3 months free of our $99/month Growth plan, which supports up to 2,000 MAUs. | Fill out this form in detail. Once the team receives your app, we'll review and get in touch. Note: One discount available per team. https://d9hc0.share.hsforms.com/2CIpNaX14T1Cv1erD_2ou1A | +| [FailSafe](www.getfailsafe.com) | Infrastructure, CyberSecurity, Audits | FailSafe provides real-time blockchain risk monitoring and smart contract audit solutions for protocols, stablecoins, and digital asset platforms across global markets. | Get $3,000 off your first smart contract audit or monitoring subscription with FailSafe. Ideal for Base stablecoin issuers, or DeFi platforms looking to strengthen on-chain security and protect funds | Email wui@getfailsafe.com with github repo of codebase for a quote on a security audit. discount will be applied once a commercial contract is signed. | | [Fjord Foundry](https://www.fjordfoundry.com/) | Fundraising / Token Sale | Connecting innovative projects and community backers through on-chain capital formation, with over $1bn raised since 2021. | Free Premium Marketing | To claim this offer, simply tell us you discovered it through the Base Builder Services Hub when you apply. If your project passes our due‑diligence review and is selected as a launch partner, you’ll be eligible.



Link to apply https://swunul13vyp.typeform.com/fjord-request | | [FLock.io](https://www.flock.io/) | AI | FLock.io is the first decentralized AI training platform combining Federated Learning and blockchain to enable secure, privacy-preserving model training. | Base ecosystem projects get up to 50% off Qwen tokens using FLock.io-trained models or other major Qwen variants, plus: 1 free FLock.io training task and 1hr free AI consultation. | Please fill out this Google Form (https://forms.gle/N8We623NQdAppEj26) with your application and contract information.



FLock.io team will contact you once we receive your application. | -| [Hexens](https://Hexens.io) | Security | At Hexens, we provide security audits to protect the future of Web3. We directly secure $120B+ in assets, working with industry leaders like Lido, EigenLayer, LayerZero, 1inch, Ava Labs, and Polygon. | Hexens will provide a discount of 15% for smart contract audits and 10% for services like pentest’s, and social engineering. Full triage will be provided for our bug bounty [r.xyz] for 3 months. | Please send your audit request to alice.rigby@hexens.io or @alicerigby on Telegram. | +| [Hexens](Hexens.io) | Security | At Hexens, we provide security audits to protect the future of Web3. We directly secure $120B+ in assets, working with industry leaders like Lido, EigenLayer, LayerZero, 1inch, Ava Labs, and Polygon. | Hexens will provide a discount of 15% for smart contract audits and 10% for services like pentest’s, and social engineering. Full triage will be provided for our bug bounty [r.xyz] for 3 months. | Please send your audit request to alice.rigby@hexens.io or @alicerigby on Telegram. | | [Hypernative](https://www.hypernative.io/) | Security | Hypernative is the leading real-time security and threat prevention platform trusted by over 200 projects—including Ethena, Uniswap, Ethereum Foundation, Morpho, Chainlink, Solana, and Kraken. | Receive a discounted rate for the first year for Hypernative's real-time threat prevention platform. | Email marshall@hypernative.io to begin your trial and claim your offer. | +| [Immunefi](https://immunefi.com/) | AI & Security | Immunefi — One Platform. Unified Security Operations. Complete Onchain Protection. Over $180B of user funds protected across 500+ protocols. | 15%+ discount on Immunefi Audits, Audit Competitions, Vulnerability Detection/PR Reviews, Onchain Monitoring/Threat Prevention, Cloud Based Formal Verification, Brand Protection and Bug Bounty. | Please fill in this form, and our Sales Team will review your submission and contact you shortly:



https://calendly.com/d/cwsd-82q-rpj | | [Meow](http://meow.com/) | Treasury Management & Yield | Meow helps web3 teams earn yield, send/receive USDC, and automate treasury via FDIC-insured accounts with free USDC transactions on Base—no wallets, prefunding, or exchange risk. | Base ecosystem projects get up to 3.5% interest on checking. | Sign up [here](https://app.meow.com/signup?referral=Base)

Or list “Base Ecosystem” under “How did you hear about us?” during signup | +| [NodeOps](https://nodeops.network/) | Cloud & Infrastructure | NodeOps Cloud is a permissionless infrastructure platform that delivers the most affordable compute power on the market. | 500 USD NodeOps Cloud Credit per Project (No-Questions Asked) & 10,000 USD and above NodeOps Cloud Credit per Project (After validation) | Users must complete the BuildOnNodeOps Grant Form, and NodeOps’ BD team will contact them if their projects require assistance. Alternatively, projects can reach out to the NodeOps Team via business@nodeops.xyz.



Here’s the link to the application form: https://forms.zohopublic.in/parthnod1/form/BuildOnNodeOpsGrantProgram/formperma/A0j6q-ChMzEUldiqrQNDrkyk7iIaIwmb_6swORtbUqc | | [Octane Security](https://www.octane.security/) | Security/Developer Tooling | Octane is an AI-powered smart contract security tool that integrates into your CI/CD pipeline, auto-generates code diffs, fixes and catches bugs missed in traditional audits! | 15% discount for Octane services | Fill out the Inbound form: https://www.octane.security/schedule-demo.



YOU MUST SPECIFY UNDER THE COMPANY INPUT YOUR COMPANY NAME AND [Base Builder] AFTER THE COMPANY NAME | +| [Onchain](https://onchain.org) | Research | Onchain's Research-as-a-Service delivers onchain insights via custom reports, ecosystem analysis & dashboards, guiding protocols, builders, VCs & startups in the Base Ecosystem to informed decisions. | 10% off total services - $1-$10,000 15% off total services - $10,001-$25,000 20% off total services - $25,001+ | Discounts apply only to research services that center on your core company or BASE-bound contract. Projects outside that scope aren’t eligible. If you’re actively building and supporting the Base ecosystem and fit these criteria, request your discount via the link below.



https://docs.google.com/forms/d/e/1FAIpQLScdJLiTU-RWsNMwsenpmRYWihlVNoTh5weSoB1cXqjwKDpGhg/viewform | +| [Onchain](https://onchain.org) | Research | Onchain's Research-as-a-Service delivers onchain insights via custom reports, ecosystem analysis & dashboards, guiding protocols, builders, VCs & startups in the Base Ecosystem to informed decisions. | 10% off total services - $1-$10,000 15% off total services - $10,001-$25,000 20% off total services - $25,001+ | Discounts apply only to research services that center on your core company or BASE-bound contract. Projects outside that scope aren’t eligible. If you’re actively building and supporting the Base ecosystem and fit these criteria, request your discount via the link below.



https://docs.google.com/forms/d/e/1FAIpQLScdJLiTU-RWsNMwsenpmRYWihlVNoTh5weSoB1cXqjwKDpGhg/viewform | | [OpenCover](https://opencover.com) | Insurance | OpenCover is the #1 onchain cover provider on L2 (crypto-native insurance) used by wallets, platforms and protocol teams to cover their users against protocol and transaction risk programmatically. | Waived protocol or transaction insurance/cover setup fees, including listing, underwriting capital provision and API access (typically $5,000). | Apply here [link to https://opencover.com/base-builders]



or contact Jeremiah [link to https://t.me/itsjeremiahs] | -| [Privy](https://privy.io) | Wallets | Privy powers user onboarding and wallet infrastructure for many of the most popular products built onchain. | 25% off of Privy's listed pricing tiers for your first three months. | Reach out to base@privy.io with your Privy appID and a brief description of what you're building to redeem offer. | -| [Runtime Verification](https://runtimeverification.com) | Security | Runtime Verification secures smart contracts with open-source formal verification and quality assurance tools. Trusted by Lido, Optimism, Uniswap, Solana and more. | FREE Audit Readiness assessment and consultation; 10% off all formal verification and security services; 20% on KaaS - our cloud formal verification platform subscriptions | Choose "Base" on the contact form under ecosystem dropdown menu (https://amp.runtimeverification.com/)

OR



Reach out to https://t.me/gregorymakodzeba on Telegram or Email: gregory.makodzeba@runtimeverification.com and mention you are building on Base to activate a discount | -| [Slash](https://slash.com) | Banking | Slash provides an all in one banking platform that includes business checking, high yield treasury, high cashback cards, and more. We also support native on/off ramp for USDC on chains like Base. | Founders in the Base ecosystem can bank with Slash for free, and receive up to 2.3% cash back on most categories, up to 3.9% treasury yield, and low off ramp fees. | Make an account at https://app.slash.com/onboarding?invite_code=BASE to claim the offer. | +| [Privy](privy.io) | Wallets | Privy powers user onboarding and wallet infrastructure for many of the most popular products built onchain. | 25% off of Privy's listed pricing tiers for your first three months. | Reach out to base@privy.io with your Privy appID and a brief description of what you're building to redeem offer. | +| [Ratio1](https://ratio1.ai) | AI & Infrastructure | Ratio1 is a decentralized AI compute network delivering trustless, secure and scalable AI execution, fair node-provider rewards, and an efficient, privacy-focused, accessible protocol. | 20% off tech consultancy to port & upgrade custom apps to Ratio1 containerized docker mode + 50 USDC rebate for each ND node-deed license purchased. | Email us to contact@ratio1.ai with subject “Base Services Ratio1 Discount”, state your interest (tech services or ND license), and ensure your https://app.ratio1.ai account is active with KYC passed. | +| [Runtime Verification](runtimeverification.com) | Security | Runtime Verification secures smart contracts with open-source formal verification and quality assurance tools. Trusted by Lido, Optimism, Uniswap, Solana and more. | FREE Audit Readiness assessment and consultation; 10% off all formal verification and security services; 20% on KaaS - our cloud formal verification platform subscriptions | Choose "Base" on the contact form under ecosystem dropdown menu (https://amp.runtimeverification.com/)

OR



Reach out to https://t.me/gregorymakodzeba on Telegram or Email: gregory.makodzeba@runtimeverification.com and mention you are building on Base to activate a discount | +| [Slash](slash.com) | Banking | Slash provides an all in one banking platform that includes business checking, high yield treasury, high cashback cards, and more. We also support native on/off ramp for USDC on chains like Base. | Founders in the Base ecosystem can bank with Slash for free, and receive up to 2.3% cash back on most categories, up to 3.9% treasury yield, and low off ramp fees. | Make an account at https://app.slash.com/onboarding?invite_code=BASE to claim the offer. | | [Team Finance ](https://www.team.finance/) | Token Management | The leading token management platform on Base. We offer a full suite of tools, including Liquidity Locks, Team Token Locks, Token Vesting, Token Generation, Staking Pool Creation, and a Multisender. | 20% discount for Team Finance services on Base. | Your discount will be automatically applied when using Team Finance on Base. | | [Token Terminal](https://tokenterminal.com/) | Financial & Protocol Analytics | Token Terminal is a full-stack onchain data platform focused on standardizing financial and alternative data for the most widely used blockchains and decentralized applications. | Token Terminal will offer Base builders a -40% discount on its Data Partnership subscription product. | Apply [here](https://tokenterminal.com/explorer/listings).

All Base builders will need to submit a "proof of deployment on Base". | | [Tokka Labs](https://tokkalabs.com/) | Token Management | Tokka Labs is a DeFi-native prop trading firm offering custom onchain market making across 70+ venues, helping projects grow TVL, volume, and token utility through tailored liquidity strategies. | Priority access to liquidity partnership scoping. Base-native projects are fast-tracked for initial conversations with our team to explore potential liquidity partnerships tailored to their needs | Submit your project to https://form.typeform.com/to/hz4Nf5zV. | | [Zapper](https://protocol.zapper.xyz/) | Data API | Access portfolio data, token prices, NFTs, and transaction history on Base with a single API. | Free API credits (5,000) and a 15% discount on credit purchases for the Zapper API | Create an account on https://protocol.zapper.xyz/ and use the discount code "BASE15" when purchasing credits. | - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/get-started/base.mdx b/docs/get-started/base.mdx index 6471ce2c1..7b7dced9e 100644 --- a/docs/get-started/base.mdx +++ b/docs/get-started/base.mdx @@ -81,7 +81,7 @@ Explore [all products](/get-started/products) and [use cases](/get-started/use-c Mini Apps run directly inside the social feed: Make your existing app a Mini App or build a new one. @@ -94,7 +94,3 @@ Explore [all products](/get-started/products) and [use cases](/get-started/use-c Showcase your project to the Base community and get discovered.
- -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/get-started/build-app.mdx b/docs/get-started/build-app.mdx index a39d265fb..4a561e319 100644 --- a/docs/get-started/build-app.mdx +++ b/docs/get-started/build-app.mdx @@ -248,7 +248,3 @@ This is just the beginning. There are many ways we can improve upon this app. Fo - Make the `increment` transaction gasless by integrating with [Paymaster](/onchainkit/transaction/transaction#sponsor-with-paymaster-capabilities) - Improve the wallet connection and sign up flow with the [WalletModal](/onchainkit/wallet/wallet-modal) component - Add onchain [Identity](/onchainkit/identity/identity) so we know who added the most recent tally - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/get-started/concepts.mdx b/docs/get-started/concepts.mdx index d3c65f868..7e2c3d9eb 100644 --- a/docs/get-started/concepts.mdx +++ b/docs/get-started/concepts.mdx @@ -11,7 +11,3 @@ Key concept overview: - Wallets - Nodes ... - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/get-started/country-leads-and-ambassadors.mdx b/docs/get-started/country-leads-and-ambassadors.mdx index 7f96b597b..4cfd82c8b 100644 --- a/docs/get-started/country-leads-and-ambassadors.mdx +++ b/docs/get-started/country-leads-and-ambassadors.mdx @@ -21,12 +21,16 @@ To get in contact with a Country Lead you can fill in this [form](https://docs.g | Brazil | [Gui](https://x.com/gui_bettanin) | | Central America | [Carlos](https://x.com/carlosjmelgar) | | East Africa | [Eddie](https://x.com/BasedKago) | +| Germany + Switzerland | [Axel](https://x.com/AxelMtbr) | | India | [Saumya](https://x.com/Saxenasaheb) | +| Indonesia | [Angeline Vivian](https://x.com/angelinevivian_) | | Ireland | [Evan](https://x.com/EvSlatts) | | Philippines | [Eli](https://x.com/0xmoonlight_) | | Southern Africa | [Derrick](https://x.com/deriq_eth) | | Singapore | [Nick](https://x.com/Nibel_eth) | | South Korea | [David](https://x.com/davidandpassion) | +| Türkiye | [Berkay](https://x.com/berkay_secil) | +| UAE | [Asal](https://x.com/asal_alizade) | | UK + Western Europe | [Clemens](https://x.com/_clemens__) | | West Africa | [Dami](https://x.com/Sir_Damilare) | @@ -52,7 +56,3 @@ We expect each Base Ambassador to maintain a friendly, safe, supportive and hara We may immediately prohibit or suspend an individual's participation in the Base Ambassador Program at any time if there is a failure to comply with these guidelines, or for any or no reason, without notice or liability of any kind. - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/get-started/deploy-chain.mdx b/docs/get-started/deploy-chain.mdx index 821428169..04c221a85 100644 --- a/docs/get-started/deploy-chain.mdx +++ b/docs/get-started/deploy-chain.mdx @@ -284,7 +284,3 @@ Discover the full ecosystem of Base developer tools and integrations **Next Steps**: After joining the waitlist, explore Base's developer documentation to understand how Appchains integrate with Smart Wallet, Paymaster, OnchainKit, and other ecosystem tools. - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/get-started/deploy-smart-contracts.mdx b/docs/get-started/deploy-smart-contracts.mdx index b5963b3ad..7554b28ad 100644 --- a/docs/get-started/deploy-smart-contracts.mdx +++ b/docs/get-started/deploy-smart-contracts.mdx @@ -141,7 +141,3 @@ This will return the initial value of the Counter contract's `number` storage va - Use [Onchainkit](https://onchainkit.com) to connect your frontend to your contracts! Onchainkit is a library of ready-to-use React components and Typescript utilities. - Learn more about interacting with your contracts in the command line using Foundry from our [Foundry tutorial](/learn/foundry/deploy-with-foundry). - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/get-started/get-funded.mdx b/docs/get-started/get-funded.mdx index d7a9b803e..8b1cd7e46 100644 --- a/docs/get-started/get-funded.mdx +++ b/docs/get-started/get-funded.mdx @@ -203,7 +203,3 @@ Don't wait for the "perfect" project. The Base ecosystem values builders at all Remember: Whether you're tinkering with a weekend project or building the next breakthrough application, there's a funding path designed for your journey. The Base ecosystem has invested millions in builders who started with nothing more than an idea and the determination to build. **Take the first step today.** - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/get-started/launch-token.mdx b/docs/get-started/launch-token.mdx index 27c6865dc..868e4f81b 100644 --- a/docs/get-started/launch-token.mdx +++ b/docs/get-started/launch-token.mdx @@ -421,7 +421,3 @@ Remember to always prioritize security, transparency, and community value when d --- Whether you choose a platform-based approach for speed and convenience, or custom development for maximum control, Base provides a robust foundation for token launches. Start with the approach that best fits your technical expertise and project requirements, and leverage Base's growing ecosystem to build successful token projects. - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/get-started/llms-full.txt b/docs/get-started/llms-full.txt new file mode 100644 index 000000000..7ab4da899 --- /dev/null +++ b/docs/get-started/llms-full.txt @@ -0,0 +1,41 @@ +# https://docs.base.org/get-started/llms-full.txt + +## Get Started — Deep Guide for LLMs + +> Orientation hub for Base: what Base is, how to browse products and use cases, and quickstarts to build apps, tokens, chains, and contracts. + +### What you can do here +- Understand Base’s value prop and ecosystem +- Explore products and use cases +- Follow quickstarts to build app/token/chain/contracts +- Find funding, services, and community support +- Use AI prompting resources and prompt library + +## Navigation (with brief descriptions) + +### Introduction +- [Base](./base) — Why Base, platform overview + +### Browse by +- [Products](./products) — Product index and entry points +- [Use Cases](./use-cases) — Scenario‑based navigation + +### Quickstart +- [Build an App](./build-app) — Ship an app on Base +- [Launch a Token](./launch-token) — Token planning and launch +- [Deploy a Chain](./deploy-chain) — OP Stack chain +- [Deploy Smart Contracts](./deploy-smart-contracts) — Contracts + +### Builder Support +- [Get Funded](./get-funded) — Grants and programs +- [Base Services Hub](./base-services-hub) — Official services/tools +- [Country Leads & Ambassadors](./country-leads-and-ambassadors) — Community + +### Build with AI +- [AI Prompting](./ai-prompting) — AI productivity patterns +- [Prompt Library](./prompt-library) — Reusable prompts + +## Minimal Critical Code +None — this section is orientation and navigation. See product sections for code. + + diff --git a/docs/get-started/llms.txt b/docs/get-started/llms.txt new file mode 100644 index 000000000..853848627 --- /dev/null +++ b/docs/get-started/llms.txt @@ -0,0 +1,31 @@ +# https://docs.base.org/get-started/llms.txt + +## Get Started Documentation + +> Start here to understand Base, browse products and use cases, and follow quickstarts to build apps, tokens, chains, and contracts. + +## Introduction +- [Base](./base) — What Base is and why build here + +## Browse by +- [Products](./products) — Overview of Base products with links +- [Use Cases](./use-cases) — Common scenarios and examples + +## Quickstart +- [Build an App](./build-app) — End‑to‑end guide to ship your first app on Base +- [Launch a Token](./launch-token) — Plan and launch tokens responsibly +- [Deploy a Chain](./deploy-chain) — Spin up an OP Stack chain +- [Deploy Smart Contracts](./deploy-smart-contracts) — Contracts on Base (testnet/mainnet) + +## Builder Support +- [Get Funded](./get-funded) — Grants, RPGF, and funding programs +- [Base Services Hub](./base-services-hub) — Official tools and services +- [Country Leads & Ambassadors](./country-leads-and-ambassadors) — Community contacts + +## Build with AI +- [AI Prompting](./ai-prompting) — Patterns for productive AI workflows +- [Prompt Library](./prompt-library) — Ready‑to‑use prompts + +## Optional +- [Use Cases](./use-cases) — Additional inspiration for builders + diff --git a/docs/get-started/products.mdx b/docs/get-started/products.mdx index 52403c720..8faca3f3b 100644 --- a/docs/get-started/products.mdx +++ b/docs/get-started/products.mdx @@ -1,23 +1,22 @@ --- title: 'Products' -keywords: ['onchainkit', 'minikit', 'agentkit', 'base account', 'appchains', 'paymaster','l3','deploy a chain','smart wallet'] +keywords: ['onchainkit', 'minikit', 'agentkit', 'base account', 'appchains', 'paymaster','l3','deploy a chain','smart wallet', 'onchaintestkit', 'testing'] --- All-in-one toolkit and ready-to-use, full-stack components. - + Feature your mini app on decentralized social platforms with a few lines of code. A passkey-based universal account to connect with the onchain world. - + Launch a chain with dedicated blockspace on Base, in minutes. + + End-to-end testing framework for blockchain applications. + - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/get-started/prompt-library.mdx b/docs/get-started/prompt-library.mdx index c1625a08c..83e6c2bd3 100644 --- a/docs/get-started/prompt-library.mdx +++ b/docs/get-started/prompt-library.mdx @@ -7,7 +7,3 @@ description: Learn practical AI prompting techniques to enhance your coding work import AiPrompt from "/snippets/prompt-library.mdx"; - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/get-started/use-cases.mdx b/docs/get-started/use-cases.mdx index c4829a4e2..80a8cfb09 100644 --- a/docs/get-started/use-cases.mdx +++ b/docs/get-started/use-cases.mdx @@ -22,7 +22,3 @@ title: 'Use Cases' Enable gasless transactions and simplify user onboarding.
- -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/images/base-account/BasePayBlueLogo.png b/docs/images/base-account/BasePayBlueLogo.png new file mode 100644 index 000000000..46eec6847 Binary files /dev/null and b/docs/images/base-account/BasePayBlueLogo.png differ diff --git a/docs/images/base-account/BasePayFinal.gif b/docs/images/base-account/BasePayFinal.gif new file mode 100644 index 000000000..ff6567c1f Binary files /dev/null and b/docs/images/base-account/BasePayFinal.gif differ diff --git a/docs/images/base-account/BasePayWhiteLogo.png b/docs/images/base-account/BasePayWhiteLogo.png new file mode 100644 index 000000000..d01b80fcb Binary files /dev/null and b/docs/images/base-account/BasePayWhiteLogo.png differ diff --git a/docs/images/base-account/CoinbaseSpend.png b/docs/images/base-account/CoinbaseSpend.png new file mode 100644 index 000000000..fd2e1497a Binary files /dev/null and b/docs/images/base-account/CoinbaseSpend.png differ diff --git a/docs/images/chat-agents/tba_example.jpeg b/docs/images/chat-agents/tba_example.jpeg new file mode 100644 index 000000000..a983c602f Binary files /dev/null and b/docs/images/chat-agents/tba_example.jpeg differ diff --git a/docs/images/chat-agents/transaction_chat.jpeg b/docs/images/chat-agents/transaction_chat.jpeg new file mode 100644 index 000000000..33bc1ede2 Binary files /dev/null and b/docs/images/chat-agents/transaction_chat.jpeg differ diff --git a/docs/images/minikit/Diagram.png b/docs/images/minikit/Diagram.png new file mode 100644 index 000000000..1c384e53a Binary files /dev/null and b/docs/images/minikit/Diagram.png differ diff --git a/docs/images/minikit/Publisher.png b/docs/images/minikit/Publisher.png new file mode 100644 index 000000000..ea92433cd Binary files /dev/null and b/docs/images/minikit/Publisher.png differ diff --git a/docs/images/minikit/categories.jpg b/docs/images/minikit/categories.jpg new file mode 100644 index 000000000..deaafa2ae Binary files /dev/null and b/docs/images/minikit/categories.jpg differ diff --git a/docs/images/minikit/category_list.jpg b/docs/images/minikit/category_list.jpg new file mode 100644 index 000000000..c91646d33 Binary files /dev/null and b/docs/images/minikit/category_list.jpg differ diff --git a/docs/images/minikit/distribution.gif b/docs/images/minikit/distribution.gif new file mode 100644 index 000000000..2a6214cc1 Binary files /dev/null and b/docs/images/minikit/distribution.gif differ diff --git a/docs/images/minikit/dynamic_embed.jpeg b/docs/images/minikit/dynamic_embed.jpeg new file mode 100644 index 000000000..0d046dc88 Binary files /dev/null and b/docs/images/minikit/dynamic_embed.jpeg differ diff --git a/docs/images/minikit/feed_mini.jpg b/docs/images/minikit/feed_mini.jpg new file mode 100644 index 000000000..b81c8ba31 Binary files /dev/null and b/docs/images/minikit/feed_mini.jpg differ diff --git a/docs/images/minikit/flywheel.png b/docs/images/minikit/flywheel.png new file mode 100644 index 000000000..a9c3e271b Binary files /dev/null and b/docs/images/minikit/flywheel.png differ diff --git a/docs/images/minikit/friends_in_game.gif b/docs/images/minikit/friends_in_game.gif new file mode 100644 index 000000000..b15fceddc Binary files /dev/null and b/docs/images/minikit/friends_in_game.gif differ diff --git a/docs/images/minikit/metadata-guidelines.png b/docs/images/minikit/metadata-guidelines.png new file mode 100644 index 000000000..214e89d1b Binary files /dev/null and b/docs/images/minikit/metadata-guidelines.png differ diff --git a/docs/images/minikit/mobile-first.png b/docs/images/minikit/mobile-first.png new file mode 100644 index 000000000..252f59569 Binary files /dev/null and b/docs/images/minikit/mobile-first.png differ diff --git a/docs/images/minikit/my-apps.jpg b/docs/images/minikit/my-apps.jpg new file mode 100644 index 000000000..7090bdda5 Binary files /dev/null and b/docs/images/minikit/my-apps.jpg differ diff --git a/docs/images/minikit/ranking.jpeg b/docs/images/minikit/ranking.jpeg new file mode 100644 index 000000000..40337c6e7 Binary files /dev/null and b/docs/images/minikit/ranking.jpeg differ diff --git a/docs/images/minikit/search.jpg b/docs/images/minikit/search.jpg new file mode 100644 index 000000000..6fb9d440e Binary files /dev/null and b/docs/images/minikit/search.jpg differ diff --git a/docs/images/minikit/share-button-ui.jpg b/docs/images/minikit/share-button-ui.jpg new file mode 100644 index 000000000..720e9e82e Binary files /dev/null and b/docs/images/minikit/share-button-ui.jpg differ diff --git a/docs/images/minikit/share_cta.jpeg b/docs/images/minikit/share_cta.jpeg new file mode 100644 index 000000000..c299a795e Binary files /dev/null and b/docs/images/minikit/share_cta.jpeg differ diff --git a/docs/images/minikit/social_finding.gif b/docs/images/minikit/social_finding.gif new file mode 100644 index 000000000..882840b71 Binary files /dev/null and b/docs/images/minikit/social_finding.gif differ diff --git a/docs/images/minikit/static_embed.jpeg b/docs/images/minikit/static_embed.jpeg new file mode 100644 index 000000000..6b3455cdf Binary files /dev/null and b/docs/images/minikit/static_embed.jpeg differ diff --git a/docs/images/minikit/trending_today.jpg b/docs/images/minikit/trending_today.jpg new file mode 100644 index 000000000..54015db1f Binary files /dev/null and b/docs/images/minikit/trending_today.jpg differ diff --git a/docs/images/tba_example.jpeg b/docs/images/tba_example.jpeg new file mode 100644 index 000000000..a983c602f Binary files /dev/null and b/docs/images/tba_example.jpeg differ diff --git a/docs/images/transaction_chat.jpeg b/docs/images/transaction_chat.jpeg new file mode 100644 index 000000000..33bc1ede2 Binary files /dev/null and b/docs/images/transaction_chat.jpeg differ diff --git a/docs/images/transaction_tray.jpeg b/docs/images/transaction_tray.jpeg new file mode 100644 index 000000000..aaf12be5e Binary files /dev/null and b/docs/images/transaction_tray.jpeg differ diff --git a/docs/instructions.md b/docs/instructions.md new file mode 100644 index 000000000..3605cf772 --- /dev/null +++ b/docs/instructions.md @@ -0,0 +1,30 @@ + +### Language and style requirements + +- Use clear, direct language appropriate for technical audiences +- Write in second person ("you") for instructions and procedures +- Use active voice over passive voice +- Employ present tense for current states, future tense for outcomes +- Avoid jargon unless necessary and define terms when first used +- Maintain consistent terminology throughout all documentation +- Keep sentences concise while providing necessary context +- Use parallel structure in lists, headings, and procedures + +### Content organization standards + +- Lead with the most important information (inverted pyramid structure) +- Use progressive disclosure: basic concepts before advanced ones +- Break complex procedures into numbered steps +- Include prerequisites and context before instructions +- Provide expected outcomes for each major step +- Use descriptive, keyword-rich headings for navigation and SEO +- Group related information logically with clear section breaks + +### User-centered approach + +- Focus on user goals and outcomes rather than system features +- Anticipate common questions and address them proactively +- Include troubleshooting for likely failure points +- Write for scannability with clear headings, lists, and white space +- Include verification steps to confirm success + diff --git a/docs/learn/address-and-payable/address-and-payable.mdx b/docs/learn/address-and-payable/address-and-payable.mdx index 37a2abc5f..1878e6acf 100644 --- a/docs/learn/address-and-payable/address-and-payable.mdx +++ b/docs/learn/address-and-payable/address-and-payable.mdx @@ -89,7 +89,3 @@ contract PaymentReceiver { Appropriately using address and address payable types is essential for secure and efficient Solidity contract development. By understanding their distinctions and applying them correctly, you can effectively manage Ether transfers and interactions within your contracts. [Address]: https://docs.soliditylang.org/en/latest/types.html#address - -import PolicyBanner from "/snippets/PolicyBanner.mdx"; - - \ No newline at end of file diff --git a/docs/learn/advanced-functions/function-modifiers-vid.mdx b/docs/learn/advanced-functions/function-modifiers-vid.mdx index 6ea8e025b..21a9fa35f 100644 --- a/docs/learn/advanced-functions/function-modifiers-vid.mdx +++ b/docs/learn/advanced-functions/function-modifiers-vid.mdx @@ -7,7 +7,3 @@ hide_table_of_contents: false import { Video } from '/snippets/VideoPlayer.mdx';