Skip to content

Commit fdf8775

Browse files
Profiles + Sub Accounts PR (#9)
* adding changes * add profiles reference and other changes * add modifs --------- Co-authored-by: eric-brown <eric.brown@coinbase.com>
1 parent d50a5fc commit fdf8775

File tree

10 files changed

+1126
-91
lines changed

10 files changed

+1126
-91
lines changed

docs/docs.json

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,8 @@
211211
"smart-wallet/concepts/features/optional/spend-permissions",
212212
"smart-wallet/concepts/features/optional/batch-operations",
213213
"smart-wallet/concepts/features/optional/custom-gas-tokens",
214-
"smart-wallet/concepts/features/optional/sub-accounts"
214+
"smart-wallet/concepts/features/optional/sub-accounts",
215+
"smart-wallet/concepts/features/optional/profiles"
215216
]
216217
}
217218
]
@@ -242,7 +243,8 @@
242243
"pages": [
243244
"smart-wallet/guides/sub-accounts",
244245
"smart-wallet/guides/sub-accounts/setup",
245-
"smart-wallet/guides/sub-accounts/using-sub-accounts"
246+
"smart-wallet/guides/sub-accounts/using-sub-accounts",
247+
"smart-wallet/guides/sub-accounts/sub-accounts-with-privy"
246248
]
247249
},
248250
"smart-wallet/guides/spend-permissions"
@@ -251,7 +253,8 @@
251253
{
252254
"group": "Examples",
253255
"pages": [
254-
"smart-wallet/examples/coin-a-joke-app"
256+
"smart-wallet/examples/coin-a-joke-app",
257+
"smart-wallet/examples/onchain-vibes-store-with-profiles"
255258
]
256259
},
257260
{
@@ -305,7 +308,8 @@
305308
"smart-wallet/technical-reference/spend-permissions/coinbase-fetchpermissions"
306309
]
307310
},
308-
"smart-wallet/technical-reference/sub-account-reference"
311+
"smart-wallet/technical-reference/sdk/sub-account-reference",
312+
"smart-wallet/technical-reference/profiles-reference"
309313
]
310314
},
311315
{
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
---
2+
title: 'Profiles'
3+
---
4+
5+
## Overview
6+
7+
Profiles allow developers to request personal information from Smart Wallet users during a transaction. This is useful for applications that need:
8+
9+
- Email addresses for notifications or account creation
10+
- Physical addresses for shipping products
11+
- Phone numbers for verification
12+
- Names for personalization
13+
14+
Smart Wallet handles the collection of this information and it is left to the developer
15+
to validate it and store it as they see fit while following the standards and regulations in place.
16+
Users can choose to use profile info (securely stored with Coinbase) or enter new information when asked for it.
17+
18+
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
19+
<img
20+
alt="Profiles Demo"
21+
src="https://i.imgur.com/rC674pl.gif"
22+
width="1000"
23+
loading="lazy"
24+
style={{ borderRadius: "16px" }}
25+
/>
26+
<em>Profiles Demo</em>
27+
</div>
28+
29+
<Info>
30+
**Privacy First**
31+
32+
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.
33+
34+
</Info>
35+
36+
## How it works
37+
38+
When you send a request for a transaction to Smart Wallet,
39+
you can specify the information you need from the user.
40+
41+
```ts
42+
const response = await provider?.request({
43+
method: "wallet_sendCalls",
44+
params: [
45+
{
46+
version: "1.0",
47+
chainId: numberToHex(84532), // Base Sepolia testnet
48+
calls: [
49+
{
50+
to: "0x036CbD53842c5426634e7929541eC2318f3dCF7e", // USDC contract address on Base Sepolia
51+
data: encodeFunctionData({
52+
abi: erc20Abi,
53+
functionName: "transfer",
54+
args: [
55+
"0xd8da6bf26964af9d7eed9e03e53415d37aa96045",
56+
parseUnits("0.01", 6),
57+
],
58+
}),
59+
},
60+
], // Simple transfer of 0.01 USDC to the contract
61+
capabilities: {
62+
dataCallback: {
63+
requests: [
64+
{
65+
type: "email",
66+
optional: false,
67+
},
68+
{
69+
type: "physicalAddress",
70+
optional: false,
71+
},
72+
],
73+
callbackURL: YOUR_CALLBACK_URL, // eg. https://your-url.com/api/data-validation
74+
},
75+
},
76+
},
77+
],
78+
});
79+
```
80+
81+
This adds an additional step to the transaction where the user is prompted to share their email address and their physical address like so:
82+
83+
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
84+
<img
85+
alt="Profiles Demo"
86+
src="/images/smart-wallet/ProfilesScreenshot.png"
87+
width="300"
88+
loading="lazy"
89+
/>
90+
<em>Profiles Screenshot</em>
91+
</div>
92+
93+
Once the user has shared their information, the callbackURL is called with the data.
94+
The callbackURL is a route on your server that you define and is used to validate the data.
95+
96+
You are also able to update the request based on the response from the user.
97+
98+
## Start using Profiles
99+
100+
Profiles is currently in alpha and only available in a dev environment on all networks.
101+
102+
To start using Profiles, you can follow the [Profiles guide](/identity/smart-wallet/guides/profiles).

docs/smart-wallet/examples/coin-a-joke-app.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
title: "Coin Your Bangers: Turn Your Jokes into Coins"
3-
sidebarTitle: "Coin Your Bangers"
3+
sidebarTitle: "Coin Your Bangers (Sub Accounts)"
44
---
55

66
import { GithubRepoCard } from "/snippets/GithubRepoCard.mdx"
Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
---
2+
title: 'Onchain Vibes Store (Profiles)'
3+
description: 'How Onchain Vibes Store uses Coinbase Smart Wallet Profiles for secure user data collection during onchain checkout.'
4+
---
5+
6+
import {GithubRepoCard} from "/snippets/GithubRepoCard.mdx";
7+
8+
## Overview
9+
10+
Onchain Vibes Store demonstrates how to use [**Smart Wallet Profiles**](/smart-wallet/concepts/features/optional/profiles)
11+
to securely collect user information (like email and physical address)
12+
during an onchain transaction. This feature enables seamless, privacy-first
13+
data collection for e-commerce and other onchain apps.
14+
15+
## What Are Smart Wallet Profiles?
16+
17+
Smart Wallet Profiles allow your app to request personal information from users as part of a transaction. Supported data types include:
18+
19+
- Email address
20+
- Phone number
21+
- Physical address
22+
- Name
23+
- Onchain wallet address
24+
25+
Users are always in control: they see exactly what you request and can choose to share or withhold any information.
26+
27+
## How It Works in This App
28+
29+
1. **User clicks Buy**: The checkout UI lets users select what info to share (email, address).
30+
2. **App requests data**: The app sends a transaction with a `dataCallback` capability, specifying the requested data and a callback URL for validation.
31+
3. **Smart Wallet prompts the user**: The wallet UI collects the requested info.
32+
4. **Validation**: The wallet POSTs the data to your callback API for validation.
33+
5. **Transaction proceeds**: If validation passes, the transaction completes and the app displays the collected info.
34+
35+
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
36+
<img
37+
alt="Onchain Vibes Store"
38+
src="https://i.imgur.com/rC674pl.gif"
39+
width="1000"
40+
loading="lazy"
41+
/>
42+
</div>
43+
<div style={{ textAlign: 'center', marginTop: '0.5rem', fontStyle: 'italic' }}>
44+
Onchain Vibes Store Quick Demo
45+
</div>
46+
47+
## Skip ahead
48+
49+
If you want to skip ahead and just get the final code, you can find it here:
50+
51+
<GithubRepoCard title="Onchain Vibes Store" githubUrl="https://github.com/base/demos/tree/master/smart-wallet/onchain-vibes-store" />
52+
53+
54+
## UI Walkthrough: CheckoutButton
55+
56+
The main logic lives in `src/components/CheckoutButton.tsx`:
57+
58+
```tsx src/components/CheckoutButton.tsx
59+
const requests = [];
60+
if (dataToRequest.email) requests.push({ type: "email", optional: false });
61+
if (dataToRequest.address) requests.push({ type: "physicalAddress", optional: false });
62+
63+
const response: any = await provider?.request({
64+
method: "wallet_sendCalls",
65+
params: [{
66+
version: "1.0",
67+
chainId: numberToHex(84532), // Base Sepolia
68+
calls: [
69+
{
70+
to: "0x036CbD53842c5426634e7929541eC2318f3dCF7e", // USDC contract
71+
data: encodeFunctionData({
72+
abi: erc20Abi,
73+
functionName: "transfer",
74+
args: [
75+
"0xd8da6bf26964af9d7eed9e03e53415d37aa96045",
76+
parseUnits("0.01", 6),
77+
],
78+
}),
79+
},
80+
],
81+
capabilities: {
82+
dataCallback: {
83+
requests,
84+
callbackURL: getCallbackURL(),
85+
},
86+
},
87+
}],
88+
});
89+
```
90+
91+
- The user selects which data to share (checkboxes for email/address).
92+
- The app sends a transaction with a `dataCallback` capability.
93+
- The callback URL is set to your API endpoint (must be HTTPS, e.g. via ngrok in dev).
94+
95+
---
96+
97+
## API Walkthrough: Data Validation
98+
99+
The callback API is implemented in `src/api/data-validation/route.ts`:
100+
101+
```ts src/api/data-validation/route.ts
102+
export async function POST(request: NextRequest) {
103+
const requestData = await request.json();
104+
try {
105+
const email = requestData.requestedInfo.email;
106+
const physicalAddress = requestData.requestedInfo.physicalAddress;
107+
const errors: any = {};
108+
if (email && email.endsWith("@example.com")) {
109+
errors.email = "Example.com emails are not allowed";
110+
}
111+
if (physicalAddress) {
112+
if (physicalAddress.postalCode && physicalAddress.postalCode.length < 5) {
113+
if (!errors.physicalAddress) errors.physicalAddress = {};
114+
errors.physicalAddress.postalCode = "Invalid postal code";
115+
}
116+
if (physicalAddress.countryCode === "XY") {
117+
if (!errors.physicalAddress) errors.physicalAddress = {};
118+
errors.physicalAddress.countryCode = "We don't ship to this country";
119+
}
120+
}
121+
if (Object.keys(errors).length > 0) {
122+
return NextResponse.json({ errors });
123+
}
124+
return NextResponse.json({
125+
calls: requestData.calls,
126+
chainId: requestData.chainId,
127+
capabilities: requestData.capabilities
128+
});
129+
} catch (error) {
130+
return NextResponse.json({ errors: { server: "Server error validating data" } });
131+
}
132+
}
133+
```
134+
135+
- The API receives the user's data, validates it, and returns errors if needed.
136+
- If validation passes, it must return the original `calls`, `chainId`, and `capabilities`.
137+
- If errors are returned, the wallet prompts the user to correct their info.
138+
139+
---
140+
141+
## Wagmi & Wallet Setup
142+
143+
The app uses Wagmi and the Coinbase Wallet connector, configured for Smart Wallet and profiles:
144+
145+
```ts
146+
import { coinbaseWallet } from "wagmi/connectors";
147+
const cbWalletConnector = coinbaseWallet({
148+
appName: "Vibes Store",
149+
preference: {
150+
keysUrl: "https://keys.coinbase.com/connect",
151+
options: "smartWalletOnly",
152+
},
153+
});
154+
```
155+
156+
---
157+
158+
## Testing Locally
159+
160+
1. Start your dev server: `npm run dev`
161+
2. Start ngrok: `ngrok http 3000`
162+
3. Set `VITE_NGROK_URL` in your `.env` to your ngrok HTTPS URL
163+
4. Try a purchase and share profile data
164+
165+
---
166+
167+
## Best Practices
168+
169+
- **Only request what you need**: Ask for the minimum info required.
170+
- **Explain why**: Tell users why you need each field.
171+
- **Validate thoroughly**: Implement robust server-side validation.
172+
- **Handle errors gracefully**: Show clear error messages.
173+
- **Use HTTPS**: Your callback URL must be HTTPS (ngrok for local dev).
174+
175+
---
176+
177+
## Resources
178+
179+
- [Profiles Guide](/smart-wallet/guides/profiles)
180+
- [Profiles Reference](/smart-wallet/technical-reference/profiles-reference)
181+
- [Smart Wallet Docs](/smart-wallet/quickstart)
182+
- [Wagmi Docs](https://wagmi.sh/)

0 commit comments

Comments
 (0)