diff --git a/docs/docs.json b/docs/docs.json
index bc8cac8a..1529da07 100644
--- a/docs/docs.json
+++ b/docs/docs.json
@@ -211,7 +211,8 @@
"smart-wallet/concepts/features/optional/spend-permissions",
"smart-wallet/concepts/features/optional/batch-operations",
"smart-wallet/concepts/features/optional/custom-gas-tokens",
- "smart-wallet/concepts/features/optional/sub-accounts"
+ "smart-wallet/concepts/features/optional/sub-accounts",
+ "smart-wallet/concepts/features/optional/profiles"
]
}
]
@@ -242,7 +243,8 @@
"pages": [
"smart-wallet/guides/sub-accounts",
"smart-wallet/guides/sub-accounts/setup",
- "smart-wallet/guides/sub-accounts/using-sub-accounts"
+ "smart-wallet/guides/sub-accounts/using-sub-accounts",
+ "smart-wallet/guides/sub-accounts/sub-accounts-with-privy"
]
},
"smart-wallet/guides/spend-permissions"
@@ -251,7 +253,8 @@
{
"group": "Examples",
"pages": [
- "smart-wallet/examples/coin-a-joke-app"
+ "smart-wallet/examples/coin-a-joke-app",
+ "smart-wallet/examples/onchain-vibes-store-with-profiles"
]
},
{
@@ -305,7 +308,8 @@
"smart-wallet/technical-reference/spend-permissions/coinbase-fetchpermissions"
]
},
- "smart-wallet/technical-reference/sub-account-reference"
+ "smart-wallet/technical-reference/sdk/sub-account-reference",
+ "smart-wallet/technical-reference/profiles-reference"
]
},
{
diff --git a/docs/smart-wallet/concepts/features/optional/profiles.mdx b/docs/smart-wallet/concepts/features/optional/profiles.mdx
new file mode 100644
index 00000000..30e65896
--- /dev/null
+++ b/docs/smart-wallet/concepts/features/optional/profiles.mdx
@@ -0,0 +1,102 @@
+---
+title: 'Profiles'
+---
+
+## Overview
+
+Profiles allow developers to request personal information from Smart Wallet users during a transaction. This is useful for applications that need:
+
+- Email addresses for notifications or account creation
+- Physical addresses for shipping products
+- Phone numbers for verification
+- Names for personalization
+
+Smart Wallet handles the collection of this information and it is left to the developer
+to validate it and store it as they see fit while following the standards and regulations in place.
+Users can choose to use profile info (securely stored with Coinbase) or enter new information when asked for it.
+
+
+
+ Profiles Demo
+
+
+
+**Privacy First**
+
+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.
+
+
+
+## How it works
+
+When you send a request for a transaction to Smart Wallet,
+you can specify the information you need from the user.
+
+```ts
+const response = await provider?.request({
+ method: "wallet_sendCalls",
+ params: [
+ {
+ version: "1.0",
+ chainId: numberToHex(84532), // Base Sepolia testnet
+ calls: [
+ {
+ to: "0x036CbD53842c5426634e7929541eC2318f3dCF7e", // USDC contract address on Base Sepolia
+ data: encodeFunctionData({
+ abi: erc20Abi,
+ functionName: "transfer",
+ args: [
+ "0xd8da6bf26964af9d7eed9e03e53415d37aa96045",
+ parseUnits("0.01", 6),
+ ],
+ }),
+ },
+ ], // Simple transfer of 0.01 USDC to the contract
+ capabilities: {
+ dataCallback: {
+ requests: [
+ {
+ type: "email",
+ optional: false,
+ },
+ {
+ type: "physicalAddress",
+ optional: false,
+ },
+ ],
+ callbackURL: YOUR_CALLBACK_URL, // eg. https://your-url.com/api/data-validation
+ },
+ },
+ },
+ ],
+ });
+```
+
+This adds an additional step to the transaction where the user is prompted to share their email address and their physical address like so:
+
+
+
+ Profiles Screenshot
+
+
+Once the user has shared their information, the callbackURL is called with the data.
+The callbackURL is a route on your server that you define and is used to validate the data.
+
+You are also able to update the request based on the response from the user.
+
+## Start using Profiles
+
+Profiles is currently in alpha and only available in a dev environment on all networks.
+
+To start using Profiles, you can follow the [Profiles guide](/identity/smart-wallet/guides/profiles).
diff --git a/docs/smart-wallet/examples/coin-a-joke-app.mdx b/docs/smart-wallet/examples/coin-a-joke-app.mdx
index b1093a60..6570a404 100644
--- a/docs/smart-wallet/examples/coin-a-joke-app.mdx
+++ b/docs/smart-wallet/examples/coin-a-joke-app.mdx
@@ -1,6 +1,6 @@
---
title: "Coin Your Bangers: Turn Your Jokes into Coins"
-sidebarTitle: "Coin Your Bangers"
+sidebarTitle: "Coin Your Bangers (Sub Accounts)"
---
import { GithubRepoCard } from "/snippets/GithubRepoCard.mdx"
diff --git a/docs/smart-wallet/examples/onchain-vibes-store-with-profiles.mdx b/docs/smart-wallet/examples/onchain-vibes-store-with-profiles.mdx
new file mode 100644
index 00000000..154ea479
--- /dev/null
+++ b/docs/smart-wallet/examples/onchain-vibes-store-with-profiles.mdx
@@ -0,0 +1,182 @@
+---
+title: 'Onchain Vibes Store (Profiles)'
+description: 'How Onchain Vibes Store uses Coinbase Smart Wallet Profiles for secure user data collection during onchain checkout.'
+---
+
+import {GithubRepoCard} from "/snippets/GithubRepoCard.mdx";
+
+## Overview
+
+Onchain Vibes Store demonstrates how to use [**Smart Wallet Profiles**](/smart-wallet/concepts/features/optional/profiles)
+to securely collect user information (like email and physical address)
+during an onchain transaction. This feature enables seamless, privacy-first
+data collection for e-commerce and other onchain apps.
+
+## What Are Smart Wallet Profiles?
+
+Smart Wallet Profiles allow your app to request personal information from users as part of a transaction. Supported data types include:
+
+- Email address
+- Phone number
+- Physical address
+- Name
+- Onchain wallet address
+
+Users are always in control: they see exactly what you request and can choose to share or withhold any information.
+
+## How It Works in This App
+
+1. **User clicks Buy**: The checkout UI lets users select what info to share (email, address).
+2. **App requests data**: The app sends a transaction with a `dataCallback` capability, specifying the requested data and a callback URL for validation.
+3. **Smart Wallet prompts the user**: The wallet UI collects the requested info.
+4. **Validation**: The wallet POSTs the data to your callback API for validation.
+5. **Transaction proceeds**: If validation passes, the transaction completes and the app displays the collected info.
+
+
+
+
+
+ Onchain Vibes Store Quick Demo
+
+
+## Skip ahead
+
+If you want to skip ahead and just get the final code, you can find it here:
+
+
+
+
+## UI Walkthrough: CheckoutButton
+
+The main logic lives in `src/components/CheckoutButton.tsx`:
+
+```tsx src/components/CheckoutButton.tsx
+const requests = [];
+if (dataToRequest.email) requests.push({ type: "email", optional: false });
+if (dataToRequest.address) requests.push({ type: "physicalAddress", optional: false });
+
+const response: any = await provider?.request({
+ method: "wallet_sendCalls",
+ params: [{
+ version: "1.0",
+ chainId: numberToHex(84532), // Base Sepolia
+ calls: [
+ {
+ to: "0x036CbD53842c5426634e7929541eC2318f3dCF7e", // USDC contract
+ data: encodeFunctionData({
+ abi: erc20Abi,
+ functionName: "transfer",
+ args: [
+ "0xd8da6bf26964af9d7eed9e03e53415d37aa96045",
+ parseUnits("0.01", 6),
+ ],
+ }),
+ },
+ ],
+ capabilities: {
+ dataCallback: {
+ requests,
+ callbackURL: getCallbackURL(),
+ },
+ },
+ }],
+});
+```
+
+- The user selects which data to share (checkboxes for email/address).
+- The app sends a transaction with a `dataCallback` capability.
+- The callback URL is set to your API endpoint (must be HTTPS, e.g. via ngrok in dev).
+
+---
+
+## API Walkthrough: Data Validation
+
+The callback API is implemented in `src/api/data-validation/route.ts`:
+
+```ts src/api/data-validation/route.ts
+export async function POST(request: NextRequest) {
+ const requestData = await request.json();
+ try {
+ const email = requestData.requestedInfo.email;
+ const physicalAddress = requestData.requestedInfo.physicalAddress;
+ const errors: any = {};
+ if (email && email.endsWith("@example.com")) {
+ errors.email = "Example.com emails are not allowed";
+ }
+ if (physicalAddress) {
+ if (physicalAddress.postalCode && physicalAddress.postalCode.length < 5) {
+ if (!errors.physicalAddress) errors.physicalAddress = {};
+ errors.physicalAddress.postalCode = "Invalid postal code";
+ }
+ if (physicalAddress.countryCode === "XY") {
+ if (!errors.physicalAddress) errors.physicalAddress = {};
+ errors.physicalAddress.countryCode = "We don't ship to this country";
+ }
+ }
+ if (Object.keys(errors).length > 0) {
+ return NextResponse.json({ errors });
+ }
+ return NextResponse.json({
+ calls: requestData.calls,
+ chainId: requestData.chainId,
+ capabilities: requestData.capabilities
+ });
+ } catch (error) {
+ return NextResponse.json({ errors: { server: "Server error validating data" } });
+ }
+}
+```
+
+- 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 errors are returned, the wallet prompts the user to correct their info.
+
+---
+
+## Wagmi & Wallet Setup
+
+The app uses Wagmi and the Coinbase Wallet connector, configured for Smart Wallet and profiles:
+
+```ts
+import { coinbaseWallet } from "wagmi/connectors";
+const cbWalletConnector = coinbaseWallet({
+ appName: "Vibes Store",
+ preference: {
+ keysUrl: "https://keys.coinbase.com/connect",
+ options: "smartWalletOnly",
+ },
+});
+```
+
+---
+
+## Testing Locally
+
+1. Start your dev server: `npm run dev`
+2. Start ngrok: `ngrok http 3000`
+3. Set `VITE_NGROK_URL` in your `.env` to your ngrok HTTPS URL
+4. Try a purchase and share profile data
+
+---
+
+## Best Practices
+
+- **Only request what you need**: Ask for the minimum info required.
+- **Explain why**: Tell users why you need each field.
+- **Validate thoroughly**: Implement robust server-side validation.
+- **Handle errors gracefully**: Show clear error messages.
+- **Use HTTPS**: Your callback URL must be HTTPS (ngrok for local dev).
+
+---
+
+## Resources
+
+- [Profiles Guide](/smart-wallet/guides/profiles)
+- [Profiles Reference](/smart-wallet/technical-reference/profiles-reference)
+- [Smart Wallet Docs](/smart-wallet/quickstart)
+- [Wagmi Docs](https://wagmi.sh/)
diff --git a/docs/smart-wallet/guides/profiles.mdx b/docs/smart-wallet/guides/profiles.mdx
new file mode 100644
index 00000000..6efc4322
--- /dev/null
+++ b/docs/smart-wallet/guides/profiles.mdx
@@ -0,0 +1,582 @@
+---
+title: 'Profiles'
+---
+
+import { GithubRepoCard } from "/snippets/GithubRepoCard.mdx"
+
+## Overview
+
+In this guide, we'll explore how to use the Profiles feature in Smart Wallet to request and validate user information
+like email addresses and physical addresses for a simple checkout flow.
+
+
+
+ Profiles Demo
+
+
+## What you'll achieve
+
+By the end of this guide, you will:
+
+- Set up a NextJS+Wagmi project to work with Smart Wallet Profiles
+- Create a simple interface to request user profile data
+- Implement a validation API to verify the data
+- Handle successful responses and errors
+
+### Skip ahead
+
+If you want to skip ahead and just get the final code, you can find it here:
+
+
+
+## Understanding Profiles
+
+The Profiles feature allows developers to request personal information from Smart Wallet users during a transaction. This is useful for applications that need:
+
+- Email addresses
+- Physical addresses
+- Phone numbers
+- Names
+
+Smart Wallet handles the collection of this information and it is left to you to validate it and store it as you see fit
+while following the standards and regulations in place.
+
+
+**Privacy First**
+
+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.
+
+
+
+## Project Setup
+
+Let's create a simple app that demonstrates the Profiles feature. We'll be using NextJS with [Wagmi](https://wagmi.sh/) hooks to integrate with Smart Wallet.
+
+
+**Get started with Wagmi**
+
+If you don't have Wagmi set up yet,
+you can use `npm create wagmi@latest` to get started.
+
+For more information on how to use Wagmi,
+check out the [Wagmi documentation](https://wagmi.sh/react/getting-started).
+
+
+
+### Configure Wagmi
+
+First, ensure your Wagmi configuration is set up correctly:
+
+```ts [wagmi.ts]
+import { http, cookieStorage, createConfig, createStorage } from "wagmi";
+import { baseSepolia } from "wagmi/chains";
+import { coinbaseWallet } from "wagmi/connectors";
+
+const cbWalletConnector = coinbaseWallet({
+ appName: "Profiles Demo",
+ preference: {
+ options: "smartWalletOnly",
+ },
+});
+
+export function getConfig() {
+ return createConfig({
+ chains: [baseSepolia],
+ connectors: [cbWalletConnector],
+ storage: createStorage({
+ storage: cookieStorage,
+ }),
+ ssr: true,
+ transports: {
+ [baseSepolia.id]: http(),
+ },
+ });
+}
+
+declare module "wagmi" {
+ interface Register {
+ config: ReturnType;
+ }
+}
+```
+
+### Creating the User Interface
+
+Now, let's create a simple UI to request profile data from users along with a transaction.
+We'll create a page with checkboxes to select which data to request and a button to submit the request
+along with a transfer of 0.01 USDC to an address on Base Sepolia.
+
+```tsx [app/page.tsx]
+"use client";
+
+import { useEffect, useState } from "react";
+import { encodeFunctionData, erc20Abi, numberToHex, parseUnits } from "viem";
+import { useConnect, useSendCalls } from "wagmi";
+
+interface DataRequest {
+ email: boolean;
+ address: boolean;
+}
+
+interface ProfileResult {
+ success: boolean;
+ email?: string;
+ address?: string;
+ error?: string;
+}
+
+export default function Home() {
+ const [dataToRequest, setDataToRequest] = useState({
+ email: true,
+ address: true
+ });
+ const [result, setResult] = useState(null);
+
+ const { sendCalls, data, error, isPending } = useSendCalls();
+ const { connect, connectors } = useConnect()
+
+
+ // Function to get callback URL - replace in production
+ function getCallbackURL() {
+ return "https://your-ngrok-url.ngrok-free.app/api/data-validation";
+ }
+
+ // Handle response data when sendCalls completes
+ useEffect(() => {
+ if (data?.capabilities?.dataCallback) {
+ const callbackData = data.capabilities.dataCallback;
+ const newResult: ProfileResult = { success: true };
+
+ // Extract email if provided
+ if (callbackData.email) newResult.email = callbackData.email;
+
+ // Extract address if provided
+ if (callbackData.physicalAddress) {
+ const addr = callbackData.physicalAddress.physicalAddress;
+ newResult.address = [
+ addr.address1,
+ addr.address2,
+ addr.city,
+ addr.state,
+ addr.postalCode,
+ addr.countryCode
+ ].filter(Boolean).join(", ");
+ }
+
+ setResult(newResult);
+ } else if (data && !data.capabilities?.dataCallback) {
+ setResult({ success: false, error: "Invalid response - no data callback" });
+ }
+ }, [data]);
+
+ // Handle errors
+ useEffect(() => {
+ if (error) {
+ setResult({
+ success: false,
+ error: error.message || "Transaction failed"
+ });
+ }
+ }, [error]);
+
+ // Handle form submission
+ async function handleSubmit() {
+ try {
+ setResult(null);
+
+ // Build requests array based on checkboxes
+ const requests = [];
+ if (dataToRequest.email) requests.push({ type: "email", optional: false });
+ if (dataToRequest.address) requests.push({ type: "physicalAddress", optional: false });
+
+ if (requests.length === 0) {
+ setResult({ success: false, error: "Select at least one data type" });
+ return;
+ }
+
+ // Send calls using wagmi hook
+ sendCalls({
+ connector: connectors[0],
+ account: null,
+ calls: [
+ {
+ to: "0x036CbD53842c5426634e7929541eC2318f3dCF7e", // USDC contract address on Base Sepolia
+ data: encodeFunctionData({
+ abi: erc20Abi,
+ functionName: "transfer",
+ args: [
+ "0xd8da6bf26964af9d7eed9e03e53415d37aa96045",
+ parseUnits("0.01", 6),
+ ],
+ }),
+ },
+ ],
+ chainId: 84532, // Base Sepolia
+ capabilities: {
+ dataCallback: {
+ requests: requests,
+ callbackURL: getCallbackURL(),
+ },
+ },
+ });
+ } catch (err) {
+ setResult({
+ success: false,
+ error: err instanceof Error ? err.message : "Unknown error occurred"
+ });
+ }
+ }
+
+ return (
+
+
Profiles Demo
+
+ {/* Data Request Form */}
+
+
Checkout
+
+
+
+
+
+
+
+
+
+
+
+
+ {/* Results Display */}
+ {result && (
+
+ {result.success ? (
+ <>
+
Data Received
+ {result.email &&
Email: {result.email}
}
+ {result.address &&
Address: {result.address}
}
+ >
+ ) : (
+ <>
+
Error
+
{result.error}
+ >
+ )}
+
+ )}
+
+ );
+}
+```
+
+
+**HTTPS Required**
+
+Smart Wallet requires callback URLs to use HTTPS, even for local development.
+For local testing, use a secure tunnel service like ngrok and update
+the `ngrokUrl` variable with your tunnel URL.
+
+To start ngrok, run the following command:
+
+```bash [Terminal]
+ngrok http 3000
+```
+
+If you don't have ngrok installed, you can install it with the following command:
+
+```bash [Terminal]
+npm install -g ngrok
+```
+
+You can learn more about ngrok [here](https://ngrok.com/docs).
+
+
+
+
+**Important caveats about the validation API**
+
+If the API validation check is successful, you MUST return the original calls in the response of the API endpoint as it's shown in the [validation API section](#implementing-the-validation-api).
+If you don't, the wallet will return an error.
+
+You can also return a new set of calls to be made after the data is validated.
+
+
+
+### Implementing the Validation API
+
+Now, let's create an API endpoint to validate the data received from Smart Wallet.
+This endpoint will check for valid formats and return errors if needed.
+
+
+**Wallet Validation**
+
+Smart Wallet will make some basic validation of the data before sending it to your callback URL.
+This includes checking that the email is a valid email address and the physical address is a valid address.
+
+If the data is invalid, Smart Wallet will return an error to the user and not make the callback.
+
+
+
+```ts [app/api/data-validation/route.ts]
+// app/api/data-validation/route.ts
+export async function POST(request: Request) {
+ const requestData = await request.json();
+
+ try {
+ // Extract data from request
+ const email = requestData.requestedInfo.email;
+ const physicalAddress = requestData.requestedInfo.physicalAddress;
+
+ const errors = {};
+
+ // Example: Reject example.com emails
+ if (email && email.endsWith("@example.com")) {
+ errors.email = "Example.com emails are not allowed";
+ }
+
+ // Example: Validate physical address
+ if (physicalAddress) {
+ if (physicalAddress.postalCode && physicalAddress.postalCode.length < 5) {
+ if (!errors.physicalAddress) errors.physicalAddress = {};
+ errors.physicalAddress.postalCode = "Invalid postal code";
+ }
+
+ if (physicalAddress.countryCode === "XY") {
+ if (!errors.physicalAddress) errors.physicalAddress = {};
+ errors.physicalAddress.countryCode = "We don't ship to this country";
+ }
+ }
+
+ // Return errors if any found
+ if (Object.keys(errors).length > 0) {
+ return Response.json({
+ errors,
+ /*request: {
+ calls: [], // Replace the old calls with new ones
+ chainId: numberToHex(84532), // Base Sepolia
+ version: "1.0",
+ },*/
+ });
+ }
+
+ // Success - no validation errors - you HAVE to return the original calls
+ return Response.json({
+ request: {
+ calls: requestData.calls,
+ chainId: requestData.chainId,
+ version: requestData.version,
+ },
+ });
+
+ } catch (error) {
+ console.error("Error processing data:", error);
+ return Response.json({
+ errors: { server: "Server error validating data" }
+ });
+ }
+}
+```
+
+
+**Returning the original calls**
+
+You MUST return the original calls in the response if the check is successful.
+
+If you don't, the wallet will return an error.
+
+
+
+## How It Works
+
+When a user visits your application and connects their Smart Wallet, the following process occurs:
+
+1. **User connects their Smart Wallet**: The user clicks "Sign in with Smart Wallet" and authorizes the connection.
+
+2. **App requests profile data**: Your app calls `wallet_sendCalls` with the `dataCallback` capability, a list of calls to be made, a list of requested data types, and a callback URL to call with the data.
+
+3. **Smart Wallet prompts the user**: The wallet shows a UI asking the user to share the requested information.
+
+4. **User provides data**: The user enters/confirms their information in the Smart Wallet interface.
+
+5. **Data is validated**: Smart Wallet sends the data to your callback URL for validation.
+
+6. **Validation response**: Your API validates the data and returns success or errors as well as any new calls to be made.
+
+7. **Transaction completion**: If validation passes, the wallet returns the data to your application.
+
+
+**Replacing calls after data validation**
+
+If you need to replace the calls made in the initial request with new ones, you can do so by returning a `request` object in the response.
+
+This is useful if you need to make a second call to get a new address based on the first one, for example.
+
+```ts [app/api/data-validation/route.ts]
+{
+ request: {
+ calls: [], // Replace the old calls with new ones
+ chainId: numberToHex(84532), // Base Sepolia
+ version: "1.0",
+ },
+}
+```
+
+
+
+## Understanding the Response Format
+
+When a user provides profile information, Smart Wallet returns a response object with the following structure:
+
+```ts
+{
+ callsId: string,
+ requestedInfo: {
+ email?: string,
+ physicalAddress?: {
+ physicalAddress: {
+ address1: string,
+ address2?: string,
+ city: string,
+ state: string,
+ postalCode: string,
+ countryCode: string,
+ name?: {
+ firstName: string,
+ familyName: string,
+ }
+ },
+ isPrimary: boolean,
+ },
+ walletAddress?: string,
+ phoneNumber?: {
+ number: string,
+ country: string,
+ isPrimary: boolean,
+ },
+ name?: {
+ firstName: string,
+ familyName: string,
+ }
+ }
+ }
+}
+```
+
+Each field will only be present if it was requested and provided by the user.
+
+## Validation Error Format
+
+If your validation API finds issues with the data, return an errors object with this structure:
+
+```ts
+{
+ errors: {
+ email?: string,
+ name?: {
+ firstName?: string,
+ lastName?: string,
+ },
+ phoneNumber?: {
+ countryCode?: string,
+ number?: string,
+ },
+ physicalAddress?: {
+ address1?: string,
+ address2?: string,
+ city?: string,
+ state?: string,
+ postalCode?: string,
+ country?: string,
+ },
+ walletAddress?: string,
+ }
+}
+```
+
+Smart Wallet will show these error messages to the user and prompt them to correct the information.
+
+## Testing Your Integration
+
+To test your integration:
+
+1. Start your development server:
+
+```bash [Terminal]
+npm run dev
+```
+
+2. Start an ngrok tunnel to your local server:
+
+```bash [Terminal]
+ngrok http 3000
+```
+
+3. Update the `ngrokUrl` variable in your code with the HTTPS URL from ngrok.
+
+4. Open your browser and navigate to your localhost URL.
+
+5. Connect your Smart Wallet and test requesting and validating user data.
+
+## Making Fields Optional
+
+You can make any requested field optional by setting the `optional` parameter:
+
+```ts
+requests: [
+ { type: "email", optional: false }, // Required
+ { type: "physicalAddress", optional: true }, // Optional
+]
+```
+
+Optional fields will be marked as such in the Smart Wallet interface, allowing users to skip them.
+
+## Best Practices
+
+1. **Only request what you need**: Ask for the minimum information necessary for your application.
+
+2. **Explain why**: In your UI, clearly explain why you need each piece of information.
+
+3. **Be transparent**: Inform users how their data will be used and stored.
+
+4. **Thorough validation**: Validate all fields properly and provide helpful error messages.
+
+5. **Handle errors gracefully**: Always account for cases where users decline to provide information.
+
+## Conclusion
+
+The Profiles feature in Smart Wallet provides a secure and user-friendly way to collect necessary user information for your onchain app. By following this guide, you've learned how to:
+
+- Request profile data from Smart Wallet users
+- Validate that data through a callback API
+- Process and display the information in your application
+
+This integration simplifies user onboarding and data collection while maintaining user privacy and control.
diff --git a/docs/smart-wallet/guides/sub-accounts/add-sub-accounts-to-onchainkit-minikit.mdx b/docs/smart-wallet/guides/sub-accounts/add-sub-accounts-to-onchainkit.mdx
similarity index 66%
rename from docs/smart-wallet/guides/sub-accounts/add-sub-accounts-to-onchainkit-minikit.mdx
rename to docs/smart-wallet/guides/sub-accounts/add-sub-accounts-to-onchainkit.mdx
index 7286d89b..ab531836 100644
--- a/docs/smart-wallet/guides/sub-accounts/add-sub-accounts-to-onchainkit-minikit.mdx
+++ b/docs/smart-wallet/guides/sub-accounts/add-sub-accounts-to-onchainkit.mdx
@@ -1,5 +1,5 @@
---
-title: "Add Sub Accounts to OnchainKit/MiniKit"
+title: "Add Sub Accounts to OnchainKit"
---
import {GithubRepoCard} from "/snippets/GithubRepoCard.mdx";
@@ -11,36 +11,18 @@ you can read more about it in the [Sub Accounts Intro Page](/identity/smart-wall
Before you start, make sure you have checked the following guides:
-- [OnchainKit Quickstart](/builderkits/onchainkit/getting-started)
-- [MiniKit Quickstart](/builderkits/minikit/quickstart)
-- [OnchainKit Custom Providers](/builderkits/onchainkit/config/supplemental-providers)
-- [Sub Accounts Intro Page](/identity/smart-wallet/guides/sub-accounts)
-
-
-**Smart Wallet compatibility with Mini Apps**
-
-The Coinbase Wallet team is working on adding Smart Wallet support to Mini Apps.
-
-For now, you can only use Sub Accounts with OnchainKit/MiniKit outside of the social feed.
-
-
+- [OnchainKit Quickstart](/onchainkit/getting-started)
+- [OnchainKit Custom Providers](/onchainkit/config/supplemental-providers)
+- [Sub Accounts Intro Page](/smart-wallet/guides/sub-accounts)
## 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
+## Add Sub Accounts to your OnchainKit project
### Override the default Coinbase Wallet SDK version
@@ -49,7 +31,7 @@ Currently, Sub Accounts are only available in the Smart Wallet development envir
To override the default Coinbase Wallet SDK version, you can run the following command:
```bash
-npm @coinbase/wallet-sdk@canary install
+npm pkg set overrides.@coinbase/wallet-sdk=canary
```
@@ -148,46 +130,6 @@ To use this environment, you need to set the `keysUrl` to `https://keys-dev.coin
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";
@@ -220,22 +162,12 @@ export function Providers(props: {
);
}
```
-
-
-
-**For OnchainKit**
-
-For OnchainKit, use `OnchainKitProvider` instead of `MiniKitProvider` as it's detailed in the
-[OnchainKit Custom Providers page](/builderkits/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](/builderkits/onchainkit/getting-started)or
-[MiniKit Quickstart](/builderkits/minikit/quickstart).
+the [OnchainKit Quickstart](/builderkits/onchainkit/getting-started)
Then, you can run the app with the following command:
@@ -243,6 +175,6 @@ Then, you can run the app with the following command:
npm run dev
```
-Congratulations! You've successfully added Sub Accounts to your OnchainKit/MiniKit project.
+Congratulations! You've successfully added Sub Accounts to your OnchainKit project.
If you have any questions, join the _#smart-wallet_ channel on [Discord](https://discord.gg/cdp).
diff --git a/docs/smart-wallet/guides/sub-accounts/setup.mdx b/docs/smart-wallet/guides/sub-accounts/setup.mdx
index 027140c0..9e1c16a3 100644
--- a/docs/smart-wallet/guides/sub-accounts/setup.mdx
+++ b/docs/smart-wallet/guides/sub-accounts/setup.mdx
@@ -1,5 +1,5 @@
---
-title: 'Project Setup'
+title: 'Project Setup (Wagmi)'
description: 'Set up a NextJS project with Smart Wallet integration'
---
diff --git a/docs/smart-wallet/guides/sub-accounts/sub-accounts-with-privy.mdx b/docs/smart-wallet/guides/sub-accounts/sub-accounts-with-privy.mdx
index adf1261c..d3c6f9ab 100644
--- a/docs/smart-wallet/guides/sub-accounts/sub-accounts-with-privy.mdx
+++ b/docs/smart-wallet/guides/sub-accounts/sub-accounts-with-privy.mdx
@@ -5,7 +5,8 @@ description: 'How to enable Smart Wallet Sub Accounts in a Privy project'
import {GithubRepoCard} from "/snippets/GithubRepoCard.mdx";
-# Using Sub Accounts with Privy
+## Overview
+
In this guide, you'll learn how to enable [Smart Wallet Sub Accounts](/identity/smart-wallet/concepts/features/optional/sub-accounts) in a [Privy](https://www.privy.io/) project.
This allows your users to automatically create a Sub Account with Spend Permissions when connecting their wallet via Privy.
@@ -29,7 +30,7 @@ By the end of this guide, you will:
## Requirements
- A [Privy project](https://docs.privy.io/guide/quickstart)
-- [Smart Wallet Sub Accounts](/identity/smart-wallet/concepts/features/optional/sub-accounts) are **only available on the canary version** of the Coinbase Wallet SDK and on **Base Sepolia**
+- [Smart Wallet Sub Accounts](/smart-wallet/concepts/features/optional/sub-accounts) are **only available on the canary version** of the Coinbase Wallet SDK and on **Base Sepolia**
- For now, you must also use a specific beta version of `@privy-io/react-auth` (see below)
## 1. Override Required Package Versions
@@ -40,9 +41,9 @@ Sub Accounts require the latest canary version of `@coinbase/wallet-sdk` and a s
# If you have an existing node_modules or lock file, delete them first
rm -rf node_modules package-lock.json yarn.lock pnpm-lock.yaml
-# Set the required overrides in your package.json
+# Set the required overrides and dependencies in your package.json
npm pkg set overrides.@coinbase/wallet-sdk=canary
-npm pkg set overrides.@privy-io/react-auth=2.13.1-beta-20250512172154
+npm pkg set dependencies.@privy-io/react-auth=2.13.1-beta-20250512172154
# Now install dependencies
npm install
@@ -136,7 +137,7 @@ npm run dev
## Additional Resources
- [Sub Accounts Privy Demo](https://github.com/base/demos/tree/master/smart-wallet/sub-accounts-privy)
-- [Sub Accounts Setup Guide](/identity/smart-wallet/guides/sub-accounts/setup)
-- [Smart Wallet Sub Accounts Feature](/identity/smart-wallet/concepts/features/optional/sub-accounts)
+- [Sub Accounts Setup Guide](/smart-wallet/guides/sub-accounts/setup)
+- [Smart Wallet Sub Accounts Feature](/smart-wallet/concepts/features/optional/sub-accounts)
- [Privy Quickstart](https://docs.privy.io/guide/quickstart)
- [Discord #smart-wallet](https://discord.com/invite/cdp) for questions or feedback
diff --git a/docs/smart-wallet/guides/sub-accounts/using-sub-accounts.mdx b/docs/smart-wallet/guides/sub-accounts/using-sub-accounts.mdx
index 21355d6b..cfbba172 100644
--- a/docs/smart-wallet/guides/sub-accounts/using-sub-accounts.mdx
+++ b/docs/smart-wallet/guides/sub-accounts/using-sub-accounts.mdx
@@ -1,5 +1,5 @@
---
-title: 'Using Sub Accounts'
+title: 'Using Sub Accounts (Wagmi)'
description: 'Learn how to sign messages and send transactions with your Sub Account'
---
@@ -7,7 +7,7 @@ import {GithubRepoCard} from "/snippets/GithubRepoCard.mdx"
# Using Sub Accounts
-Once you've [set up your Wagmi config and created your Smart Wallet connection](/identity/smart-wallet/guides/sub-accounts/setup), you can use it to perform various operations like signing messages and sending transactions.
+Once you've [set up your Wagmi config and created your Smart Wallet connection](/smart-wallet/guides/sub-accounts/setup), you can use it to perform various operations like signing messages and sending transactions.
In this section, we'll cover how to send a simple transaction using your Sub Account.
## What you'll achieve
@@ -269,6 +269,7 @@ This example provides a complete interface for users to:
5. Disconnect their wallet
+
All transactions will be automatically limited by the Spend Permissions configured in your [Wagmi setup](/identity/smart-wallet/guides/sub-accounts/setup).
diff --git a/docs/smart-wallet/technical-reference/profiles-reference.mdx b/docs/smart-wallet/technical-reference/profiles-reference.mdx
new file mode 100644
index 00000000..f3d19760
--- /dev/null
+++ b/docs/smart-wallet/technical-reference/profiles-reference.mdx
@@ -0,0 +1,231 @@
+---
+title: 'Profiles Reference'
+---
+
+## Overview
+
+The [Profiles feature](/identity/smart-wallet/concepts/features/optional/profiles) allows developers to request personal information from Smart Wallet users during a
+transaction. This is useful for applications that need to collect user data like email addresses,
+physical addresses, phone numbers, and names.
+
+This is not a guide on how to implement Profiles, but rather a reference for the API endpoints and data formats.
+If you are looking for a guide on how to implement Profiles, please see the [Profiles guide](/identity/smart-wallet/guides/profiles).
+
+## Supported Data Types
+
+The following data types are supported for profile requests:
+
+```typescript
+type DataCallbackType =
+ | 'email' // Email address
+ | '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
+
+type DataCallbackRequestType = {
+ optional?: boolean;
+ type: DataCallbackType;
+}
+
+type DataCallbackCapability = {
+ requests: DataCallbackRequestType[];
+ callbackURL?: string;
+}
+```
+
+## API Reference
+
+### Request Format
+
+To request profile data, include the `dataCallback` capability in your [`wallet_sendCalls`](/identity/smart-wallet/technical-reference/sdk/CoinbaseWalletProvider/wallet_sendCalls) request:
+
+```typescript
+const response = await provider.request({
+ method: "wallet_sendCalls",
+ params: [{
+ version: "1.0",
+ chainId: numberToHex(84532), // Base Sepolia
+ calls: [
+ // Your transaction calls here
+ ],
+ capabilities: {
+ dataCallback: {
+ requests: [
+ {
+ type: "email",
+ optional: false, // Whether this field is optional
+ },
+ {
+ type: "physicalAddress",
+ optional: true,
+ },
+ // Add more requests as needed
+ ],
+ callbackURL: "https://your-api.com/validate", // Your validation endpoint
+ },
+ },
+ }],
+});
+```
+
+### Callback API
+
+Your callback API will receive a POST request with the following structure:
+
+```typescript
+// Request body structure
+{
+ calls: {
+ to: string;
+ data: string;
+ }[];
+ chainId: string;
+ version: string;
+ capabilities: {
+ dataCallback: {
+ requestedInfo: {
+ email?: string;
+ phoneNumber?: {
+ number: string;
+ country: string;
+ isPrimary: boolean;
+ };
+ physicalAddress?: {
+ physicalAddress: {
+ address1: string;
+ address2?: string;
+ city: string;
+ state: string;
+ postalCode: string;
+ countryCode: string;
+ name?: {
+ firstName: string;
+ familyName: string;
+ };
+ };
+ isPrimary: boolean;
+ };
+ name?: {
+ firstName: string;
+ familyName: string;
+ };
+ onchainAddress?: string;
+ };
+ };
+ };
+}
+```
+
+### Response Format
+
+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.
+
+```typescript
+{
+ calls: {
+ to: string;
+ data: string;
+ }[];
+ chainId: string;
+ version: string;
+ capabilities: {
+ dataCallback: {
+ // Original or updated dataCallback capability
+ };
+ // Other capabilities can be changed as needed
+ };
+}
+```
+
+2. **Error Response** - Return validation errors to prompt the user to correct their information:
+
+```typescript
+{
+ errors: {
+ email?: string;
+ phoneNumber?: {
+ number?: string;
+ country?: string;
+ };
+ physicalAddress?: {
+ address1?: string;
+ address2?: string;
+ city?: string;
+ state?: string;
+ postalCode?: string;
+ countryCode?: string;
+ };
+ name?: {
+ firstName?: string;
+ familyName?: string;
+ };
+ onchainAddress?: string;
+ };
+}
+```
+
+## Example Implementation
+
+Here's a complete example of a validation API endpoint:
+
+```typescript
+export async function POST(request: Request) {
+ const requestData = await request.json();
+
+ try {
+ const { requestedInfo } = requestData.capabilities.dataCallback;
+ const errors = {};
+
+ // Validate email
+ if (requestedInfo.email && requestedInfo.email.endsWith("@example.com")) {
+ errors.email = "Example.com emails are not allowed";
+ }
+
+ // Validate physical address
+ if (requestedInfo.physicalAddress) {
+ const addr = requestedInfo.physicalAddress.physicalAddress;
+ if (addr.postalCode && addr.postalCode.length < 5) {
+ if (!errors.physicalAddress) errors.physicalAddress = {};
+ errors.physicalAddress.postalCode = "Invalid postal code";
+ }
+ }
+
+ // Return errors if any found
+ if (Object.keys(errors).length > 0) {
+ return Response.json({ errors });
+ }
+
+ // Success - return original calls
+ return Response.json({
+ calls: requestData.calls,
+ chainId: requestData.chainId,
+ version: requestData.version,
+ capabilities: requestData.capabilities
+ });
+
+ } catch (error) {
+ return Response.json({
+ errors: { server: "Server error validating data" }
+ });
+ }
+}
+```
+
+## Important Notes
+
+1. **HTTPS Required**: Your callback URL must use HTTPS, even for local development. Use a service like ngrok for testing.
+
+2. **Return Original or New Calls**: You MUST return the original calls or new calls in your success response. If you don't, the wallet will return an error.
+
+3. **Optional Fields**: You can make any requested field optional by setting `optional: true` in the request. Optional fields will be marked as such in the Smart Wallet interface.
+
+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**: Smart Wallet performs basic validation before sending data to your callback URL. This includes checking that emails are valid and addresses are properly formatted.