From 76cbb31f97014ae3dcaf1d431e586a3e76b90094 Mon Sep 17 00:00:00 2001 From: youssefea Date: Tue, 17 Jun 2025 16:49:40 +0100 Subject: [PATCH 1/3] adding changes --- docs/docs.json | 7 +- .../concepts/features/optional/profiles.mdx | 102 +++ .../onchain-vibes-store-with-profiles.mdx | 170 +++++ docs/smart-wallet/guides/profiles.mdx | 582 ++++++++++++++++++ ...mdx => add-sub-accounts-to-onchainkit.mdx} | 82 +-- .../guides/sub-accounts/setup.mdx | 2 +- .../sub-accounts/sub-accounts-with-privy.mdx | 6 +- .../sub-accounts/using-sub-accounts.mdx | 2 +- 8 files changed, 870 insertions(+), 83 deletions(-) create mode 100644 docs/smart-wallet/concepts/features/optional/profiles.mdx create mode 100644 docs/smart-wallet/examples/onchain-vibes-store-with-profiles.mdx create mode 100644 docs/smart-wallet/guides/profiles.mdx rename docs/smart-wallet/guides/sub-accounts/{add-sub-accounts-to-onchainkit-minikit.mdx => add-sub-accounts-to-onchainkit.mdx} (69%) diff --git a/docs/docs.json b/docs/docs.json index 9fba3077..d1af10b4 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -211,7 +211,8 @@ "smart-wallet/concepts/features/optional/spend-limits", "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,9 +243,11 @@ "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/profiles", "smart-wallet/guides/spend-limits" ] }, 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..f6dd505e --- /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 + 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 Demo + 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/onchain-vibes-store-with-profiles.mdx b/docs/smart-wallet/examples/onchain-vibes-store-with-profiles.mdx new file mode 100644 index 00000000..0915c9e4 --- /dev/null +++ b/docs/smart-wallet/examples/onchain-vibes-store-with-profiles.mdx @@ -0,0 +1,170 @@ +--- +title: 'Smart Wallet Profiles Integration in Onchain Vibes Store' +description: 'How Onchain Vibes Store uses Coinbase Smart Wallet Profiles for secure user data collection during onchain checkout.' +--- + +## Overview + +Onchain Vibes Store demonstrates how to use **Smart Wallet 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. + +--- + +## UI Walkthrough: CheckoutButton + +The main logic lives in [`src/components/CheckoutButton.tsx`](./src/components/CheckoutButton.tsx): + +```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`](./src/api/data-validation/route.ts): + +```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](https://docs.base.org/identity/smart-wallet/guides/profiles) +- [Profiles Reference](https://docs.base.org/identity/smart-wallet/concepts/features/optional/profiles) +- [Coinbase Smart Wallet Docs](https://docs.base.org/identity/smart-wallet/) +- [Wagmi Docs](https://wagmi.sh/) + +--- + +This integration makes onchain commerce seamless and privacy-first, with user-controlled data sharing and validation built in. + 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 + 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 69% 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 90fa93c8..bf513cb8 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"; @@ -12,35 +12,17 @@ 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. - - - ## 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,47 +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: -:::code-group - -```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"; @@ -222,22 +163,11 @@ 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: @@ -245,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 538e74de..aec83029 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 ebe99dc7..0d785c43 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,7 @@ 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 Limits when connecting their wallet via Privy. @@ -40,9 +40,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 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 65773f17..5c50dd94 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' --- From dc557991313d4b400b5fcaeae1529d70e4a29488 Mon Sep 17 00:00:00 2001 From: youssefea Date: Tue, 17 Jun 2025 16:55:58 +0100 Subject: [PATCH 2/3] add profiles reference and other changes --- docs/docs.json | 3 +- .../add-sub-accounts-to-onchainkit.mdx | 6 +- .../sub-accounts/sub-accounts-with-privy.mdx | 8 +- .../sub-accounts/using-sub-accounts.mdx | 4 +- .../profiles-reference.mdx | 231 ++++++++++++++++++ 5 files changed, 242 insertions(+), 10 deletions(-) create mode 100644 docs/smart-wallet/technical-reference/profiles-reference.mdx diff --git a/docs/docs.json b/docs/docs.json index d1af10b4..395acbc8 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -307,7 +307,8 @@ "smart-wallet/technical-reference/spend-permissions/coinbase-fetchpermissions" ] }, - "smart-wallet/technical-reference/sdk/sub-account-reference" + "smart-wallet/technical-reference/sdk/sub-account-reference", + "smart-wallet/technical-reference/profiles-reference" ] }, { diff --git a/docs/smart-wallet/guides/sub-accounts/add-sub-accounts-to-onchainkit.mdx b/docs/smart-wallet/guides/sub-accounts/add-sub-accounts-to-onchainkit.mdx index bf513cb8..b1f3bd6e 100644 --- a/docs/smart-wallet/guides/sub-accounts/add-sub-accounts-to-onchainkit.mdx +++ b/docs/smart-wallet/guides/sub-accounts/add-sub-accounts-to-onchainkit.mdx @@ -11,9 +11,9 @@ 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) -- [OnchainKit Custom Providers](/builderkits/onchainkit/config/supplemental-providers) -- [Sub Accounts Intro Page](/identity/smart-wallet/guides/sub-accounts) +- [OnchainKit Quickstart](/onchainkit/getting-started) +- [OnchainKit Custom Providers](/onchainkit/config/supplemental-providers) +- [Sub Accounts Intro Page](/smart-wallet/guides/sub-accounts) ## Skip ahead 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 0d785c43..57965432 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 @@ -7,7 +7,7 @@ import {GithubRepoCard} from "/snippets/GithubRepoCard.mdx"; ## 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. +In this guide, you'll learn how to enable [Smart Wallet Sub Accounts](/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 Limits when connecting their wallet via Privy. ## Skip ahead @@ -29,7 +29,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 @@ -136,7 +136,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 5c50dd94..f5bbc941 100644 --- a/docs/smart-wallet/guides/sub-accounts/using-sub-accounts.mdx +++ b/docs/smart-wallet/guides/sub-accounts/using-sub-accounts.mdx @@ -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,7 +269,7 @@ This example provides a complete interface for users to: 5. Disconnect their wallet -All transactions will be automatically limited by the spend limits configured in your [Wagmi setup](/identity/smart-wallet/guides/sub-accounts/setup). +All transactions will be automatically limited by the spend limits configured in your [Wagmi setup](/smart-wallet/guides/sub-accounts/setup). ## Additional Resources 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. From 3562ff7eb437c9e504988c22d38dab78dace302b Mon Sep 17 00:00:00 2001 From: youssefea Date: Tue, 17 Jun 2025 17:06:34 +0100 Subject: [PATCH 3/3] add modifs --- docs/docs.json | 3 +- .../concepts/features/optional/profiles.mdx | 2 +- .../smart-wallet/examples/coin-a-joke-app.mdx | 2 +- .../onchain-vibes-store-with-profiles.mdx | 46 ++++++++++++------- 4 files changed, 33 insertions(+), 20 deletions(-) diff --git a/docs/docs.json b/docs/docs.json index 395acbc8..2c698001 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -254,7 +254,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" ] }, { diff --git a/docs/smart-wallet/concepts/features/optional/profiles.mdx b/docs/smart-wallet/concepts/features/optional/profiles.mdx index f6dd505e..30e65896 100644 --- a/docs/smart-wallet/concepts/features/optional/profiles.mdx +++ b/docs/smart-wallet/concepts/features/optional/profiles.mdx @@ -18,7 +18,7 @@ Users can choose to use profile info (securely stored with Coinbase) or enter ne
Profiles Demo + Onchain Vibes Store +
+
+ 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`](./src/components/CheckoutButton.tsx): +The main logic lives in `src/components/CheckoutButton.tsx`: -```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 }); @@ -79,9 +96,9 @@ const response: any = await provider?.request({ ## API Walkthrough: Data Validation -The callback API is implemented in [`src/api/data-validation/route.ts`](./src/api/data-validation/route.ts): +The callback API is implemented in `src/api/data-validation/route.ts`: -```ts +```ts src/api/data-validation/route.ts export async function POST(request: NextRequest) { const requestData = await request.json(); try { @@ -159,12 +176,7 @@ const cbWalletConnector = coinbaseWallet({ ## Resources -- [Profiles Guide](https://docs.base.org/identity/smart-wallet/guides/profiles) -- [Profiles Reference](https://docs.base.org/identity/smart-wallet/concepts/features/optional/profiles) -- [Coinbase Smart Wallet Docs](https://docs.base.org/identity/smart-wallet/) +- [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/) - ---- - -This integration makes onchain commerce seamless and privacy-first, with user-controlled data sharing and validation built in. -