Skip to content
This repository was archived by the owner on Mar 17, 2026. It is now read-only.

Commit 640ba7d

Browse files
committed
Merge branch 'master' into docs/cbw-beta-link-update
2 parents 4a16a1a + a7afdc7 commit 640ba7d

9 files changed

Lines changed: 179 additions & 168 deletions

File tree

apps/base-docs/docs/pages/identity/smart-wallet/guides/profiles.mdx

Lines changed: 127 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -138,44 +138,79 @@ along with a transfer of 0.01 USDC to an address on Base Sepolia.
138138
```tsx [app/page.tsx]
139139
"use client";
140140

141-
import { ProviderInterface } from "@coinbase/wallet-sdk";
142141
import { useEffect, useState } from "react";
143142
import { encodeFunctionData, erc20Abi, numberToHex, parseUnits } from "viem";
144-
import { useAccount, useConnect, useDisconnect } from "wagmi";
143+
import { useConnect, useSendCalls } from "wagmi";
144+
145+
interface DataRequest {
146+
email: boolean;
147+
address: boolean;
148+
}
149+
150+
interface ProfileResult {
151+
success: boolean;
152+
email?: string;
153+
address?: string;
154+
error?: string;
155+
}
145156

146157
export default function Home() {
147-
const [provider, setProvider] = useState(undefined);
148-
const [dataToRequest, setDataToRequest] = useState({
158+
const [dataToRequest, setDataToRequest] = useState<DataRequest>({
149159
email: true,
150160
address: true
151161
});
152-
const [isLoading, setIsLoading] = useState(false);
153-
const [result, setResult] = useState(null);
162+
const [result, setResult] = useState<ProfileResult | null>(null);
154163

155-
const account = useAccount();
156-
const { connectors, connect, status, error } = useConnect();
157-
const { disconnect } = useDisconnect();
164+
const { sendCalls, data, error, isPending } = useSendCalls();
165+
const { connect, connectors } = useConnect()
158166

159-
// Initialize provider when account connected
160-
useEffect(() => {
161-
async function getProvider() {
162-
if (account.status === 'connected' && account.connector) {
163-
const provider = await account.connector.getProvider();
164-
setProvider(provider);
165-
}
166-
}
167-
getProvider();
168-
}, [account]);
169167

170168
// Function to get callback URL - replace in production
171169
function getCallbackURL() {
172170
return "https://your-ngrok-url.ngrok-free.app/api/data-validation";
173171
}
174172

173+
// Handle response data when sendCalls completes
174+
useEffect(() => {
175+
if (data?.capabilities?.dataCallback) {
176+
const callbackData = data.capabilities.dataCallback;
177+
const newResult: ProfileResult = { success: true };
178+
179+
// Extract email if provided
180+
if (callbackData.email) newResult.email = callbackData.email;
181+
182+
// Extract address if provided
183+
if (callbackData.physicalAddress) {
184+
const addr = callbackData.physicalAddress.physicalAddress;
185+
newResult.address = [
186+
addr.address1,
187+
addr.address2,
188+
addr.city,
189+
addr.state,
190+
addr.postalCode,
191+
addr.countryCode
192+
].filter(Boolean).join(", ");
193+
}
194+
195+
setResult(newResult);
196+
} else if (data && !data.capabilities?.dataCallback) {
197+
setResult({ success: false, error: "Invalid response - no data callback" });
198+
}
199+
}, [data]);
200+
201+
// Handle errors
202+
useEffect(() => {
203+
if (error) {
204+
setResult({
205+
success: false,
206+
error: error.message || "Transaction failed"
207+
});
208+
}
209+
}, [error]);
210+
175211
// Handle form submission
176212
async function handleSubmit() {
177213
try {
178-
setIsLoading(true);
179214
setResult(null);
180215

181216
// Build requests array based on checkboxes
@@ -185,125 +220,79 @@ export default function Home() {
185220

186221
if (requests.length === 0) {
187222
setResult({ success: false, error: "Select at least one data type" });
188-
setIsLoading(false);
189223
return;
190224
}
191225

192-
// Request data from wallet
193-
const response = await provider?.request({
194-
method: "wallet_sendCalls",
195-
params: [{
196-
version: "1.0",
197-
chainId: numberToHex(84532), // Base Sepolia
198-
calls: [
199-
{
200-
to: "0x036CbD53842c5426634e7929541eC2318f3dCF7e", // USDC contract address on Base Sepolia
201-
data: encodeFunctionData({
202-
abi: erc20Abi,
203-
functionName: "transfer",
204-
args: [
205-
"0xd8da6bf26964af9d7eed9e03e53415d37aa96045",
206-
parseUnits("0.01", 6),
207-
],
208-
}),
209-
},
210-
], // Simple transfer of 0.01 USDC to the contract
211-
capabilities: {
212-
dataCallback: {
213-
requests: requests,
214-
callbackURL: getCallbackURL(),
215-
},
226+
// Send calls using wagmi hook
227+
sendCalls({
228+
connector: connectors[0],
229+
account: null,
230+
calls: [
231+
{
232+
to: "0x036CbD53842c5426634e7929541eC2318f3dCF7e", // USDC contract address on Base Sepolia
233+
data: encodeFunctionData({
234+
abi: erc20Abi,
235+
functionName: "transfer",
236+
args: [
237+
"0xd8da6bf26964af9d7eed9e03e53415d37aa96045",
238+
parseUnits("0.01", 6),
239+
],
240+
}),
241+
},
242+
],
243+
chainId: 84532, // Base Sepolia
244+
capabilities: {
245+
dataCallback: {
246+
requests: requests,
247+
callbackURL: getCallbackURL(),
216248
},
217-
}],
249+
},
250+
});
251+
} catch (err) {
252+
setResult({
253+
success: false,
254+
error: err instanceof Error ? err.message : "Unknown error occurred"
218255
});
219-
220-
// Process response
221-
if (response?.capabilities?.dataCallback) {
222-
const data = response.capabilities.dataCallback;
223-
const result = { success: true };
224-
225-
// Extract email if provided
226-
if (data.email) result.email = data.email;
227-
228-
// Extract address if provided
229-
if (data.physicalAddress) {
230-
const addr = data.physicalAddress.physicalAddress;
231-
result.address = [
232-
addr.address1,
233-
addr.address2,
234-
addr.city,
235-
addr.state,
236-
addr.postalCode,
237-
addr.countryCode
238-
].filter(Boolean).join(", ");
239-
}
240-
241-
setResult(result);
242-
} else {
243-
setResult({ success: false, error: "Invalid response" });
244-
}
245-
} catch (error) {
246-
setResult({ success: false, error: error.message || "Transaction failed" });
247-
} finally {
248-
setIsLoading(false);
249256
}
250257
}
251258

252259
return (
253260
<div style={{ maxWidth: "600px", margin: "0 auto", padding: "20px" }}>
254261
<h1>Profiles Demo</h1>
255262

256-
{/* Wallet Status */}
257-
<div style={{ marginBottom: "20px" }}>
258-
<p>Status: {account.status}</p>
259-
{account.status === 'connected' ? (
260-
<button onClick={disconnect}>Disconnect</button>
261-
) : (
262-
connectors
263-
.filter(c => c.name === 'Coinbase Wallet')
264-
.map(connector => (
265-
<button key={connector.uid} onClick={() => connect({ connector })}>
266-
Connect Smart Wallet
267-
</button>
268-
))
269-
)}
270-
</div>
271-
272263
{/* Data Request Form */}
273-
{account.status === 'connected' && (
274-
<div style={{ marginTop: "20px" }}>
275-
<h2>Checkout</h2>
276-
277-
<div>
278-
<label>
279-
<input
280-
type="checkbox"
281-
checked={dataToRequest.email}
282-
onChange={() => setDataToRequest(prev => ({ ...prev, email: !prev.email }))}
283-
/>
284-
Email Address
285-
</label>
286-
</div>
287-
288-
<div>
289-
<label>
290-
<input
291-
type="checkbox"
292-
checked={dataToRequest.address}
293-
onChange={() => setDataToRequest(prev => ({ ...prev, address: !prev.address }))}
294-
/>
295-
Physical Address
296-
</label>
297-
</div>
298-
299-
<button
300-
onClick={handleSubmit}
301-
disabled={isLoading || !provider}
302-
>
303-
{isLoading ? "Processing..." : "Checkout"}
304-
</button>
264+
<div style={{ marginTop: "20px" }}>
265+
<h2>Checkout</h2>
266+
267+
<div>
268+
<label>
269+
<input
270+
type="checkbox"
271+
checked={dataToRequest.email}
272+
onChange={() => setDataToRequest(prev => ({ ...prev, email: !prev.email }))}
273+
/>
274+
Email Address
275+
</label>
305276
</div>
306-
)}
277+
278+
<div>
279+
<label>
280+
<input
281+
type="checkbox"
282+
checked={dataToRequest.address}
283+
onChange={() => setDataToRequest(prev => ({ ...prev, address: !prev.address }))}
284+
/>
285+
Physical Address
286+
</label>
287+
</div>
288+
289+
<button
290+
onClick={handleSubmit}
291+
disabled={isPending}
292+
>
293+
{isPending ? "Processing..." : "Checkout"}
294+
</button>
295+
</div>
307296

308297
{/* Results Display */}
309298
{result && (
@@ -355,6 +344,16 @@ You can learn more about ngrok [here](https://ngrok.com/docs).
355344

356345
</Callout>
357346

347+
<Callout type="warning">
348+
**Important caveats about the validation API**
349+
350+
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).
351+
If you don't, the wallet will return an error.
352+
353+
You can also return a new set of calls to be made after the data is validated.
354+
355+
</Callout>
356+
358357
### Implementing the Validation API
359358

360359
Now, let's create an API endpoint to validate the data received from Smart Wallet.
@@ -414,9 +413,11 @@ export async function POST(request: Request) {
414413

415414
// Success - no validation errors - you HAVE to return the original calls
416415
return Response.json({
416+
request: {
417417
calls: requestData.calls,
418418
chainId: requestData.chainId,
419-
capabilities: requestData.capabilities
419+
version: requestData.version,
420+
},
420421
});
421422

422423
} catch (error) {

apps/base-docs/sidebar.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1086,6 +1086,10 @@ export const sidebar: Sidebar = [
10861086
text: 'Security Council',
10871087
link: '/chain/security-council',
10881088
},
1089+
{
1090+
text: 'Block Building',
1091+
link: '/chain/block-building',
1092+
},
10891093
{
10901094
text: 'Tools',
10911095
collapsed: true,

apps/web/app/api/registry/entries/route.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { NextRequest, NextResponse } from 'next/server';
2-
import { getDb } from 'apps/web/src/utils/datastores/rds';
2+
import { getDb } from 'apps/web/src/utils/datastores/postgres';
33
import { getKv } from 'apps/web/src/utils/datastores/kv';
44
import { logger } from 'apps/web/src/utils/logger';
55
import { withTimeout } from 'apps/web/app/api/decorators';

apps/web/app/api/registry/featured/route.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { NextResponse } from 'next/server';
2-
import { getDb } from 'apps/web/src/utils/datastores/rds';
2+
import { getDb } from 'apps/web/src/utils/datastores/postgres';
33
import { getKv } from 'apps/web/src/utils/datastores/kv';
44
import { logger } from 'apps/web/src/utils/logger';
55
import { withTimeout } from 'apps/web/app/api/decorators';
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { Kysely, PostgresDialect } from 'kysely';
2+
import { Pool } from 'pg';
3+
import { isDevelopment } from 'apps/web/src/constants';
4+
import { Database } from './types';
5+
import { logger } from 'apps/web/src/utils/logger';
6+
7+
function createDefaultPostgresManager() {
8+
const user = isDevelopment ? process.env.POSTGRES_USER_DEVELOPMENT : process.env.POSTGRES_USER;
9+
const password = isDevelopment ? process.env.POSTGRES_PASSWORD_DEVELOPMENT : process.env.POSTGRES_PASSWORD;
10+
const host = isDevelopment ? process.env.POSTGRES_HOST_DEVELOPMENT : process.env.POSTGRES_HOST;
11+
const dbName = isDevelopment ? process.env.POSTGRES_DB_NAME_DEVELOPMENT : process.env.POSTGRES_DB_NAME;
12+
const connectionString = `postgresql://${user}:${password}@${host}:5432/${dbName}`;
13+
const poolConfig = isDevelopment
14+
? {
15+
connectionString,
16+
}
17+
: {
18+
connectionString,
19+
ssl: {
20+
rejectUnauthorized: false,
21+
},
22+
};
23+
24+
try {
25+
const pool = new Pool(poolConfig);
26+
const dialect = new PostgresDialect({ pool });
27+
return new Kysely<Database>({ dialect });
28+
} catch (error) {
29+
if (isDevelopment) {
30+
console.error('Failed to connect to postgres', error);
31+
} else {
32+
logger.error('Failed to connect to postgres', error);
33+
}
34+
throw new Error(`Failed to connect to postgres: ${error}`);
35+
}
36+
}
37+
38+
let db: Kysely<Database> | undefined = undefined;
39+
export function getDb() {
40+
if (!db) {
41+
db = createDefaultPostgresManager();
42+
}
43+
return db;
44+
}
File renamed without changes.

0 commit comments

Comments
 (0)