Skip to content

Commit 9e11fb3

Browse files
committed
update wagmi template
1 parent bda596e commit 9e11fb3

File tree

5 files changed

+240
-91
lines changed

5 files changed

+240
-91
lines changed

base-account/base-account-wagmi-template/src/app/api/auth/verify/route.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,6 @@ export async function POST(request: NextRequest) {
3232
const body = await request.json();
3333
const { address, message, signature } = body;
3434

35-
console.log("address", address);
36-
console.log("message", message);
37-
console.log("signature", signature);
38-
3935
// Validate required fields
4036
if (!address || !message || !signature) {
4137
return NextResponse.json(
@@ -89,7 +85,6 @@ export async function POST(request: NextRequest) {
8985
message,
9086
signature: signature as `0x${string}`,
9187
});
92-
console.log("isValid", isValid);
9388

9489
if (isValid) {
9590
// Mark nonce as used (expire after 1 hour)

base-account/base-account-wagmi-template/src/app/page.tsx

Lines changed: 6 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -1,85 +1,13 @@
11
"use client";
22

3-
import { Connector, http, useAccount, useConnect, useDisconnect } from "wagmi";
4-
import { SignInWithBaseButton } from "@base-org/account-ui/react";
5-
import { useState } from "react";
6-
import { base } from "viem/chains";
7-
import { createPublicClient } from "viem";
3+
import { useAccount, useConnect, useDisconnect } from "wagmi";
4+
import { SignInWithBase } from "../components/SignInWithBase";
5+
import { BatchTransactions } from "../components/BatchTransactions";
86

97
function App() {
108
const account = useAccount();
119
const { connectors, connect, status, error } = useConnect();
1210
const { disconnect } = useDisconnect();
13-
const [verificationResult, setVerificationResult] = useState<string>("");
14-
15-
async function handleBaseAccountConnect(connector: Connector) {
16-
const provider = await connector.getProvider();
17-
if (provider) {
18-
try {
19-
// Generate a fresh nonce (this will be overwritten with the backend nonce)
20-
const clientNonce = Math.random().toString(36).substring(2, 15) +
21-
Math.random().toString(36).substring(2, 15);
22-
console.log("clientNonce", clientNonce);
23-
// Connect with SIWE to get signature, message, and address
24-
const accounts = await (provider as any).request({
25-
method: "wallet_connect",
26-
params: [
27-
{
28-
version: "1",
29-
capabilities: {
30-
signInWithEthereum: {
31-
nonce: clientNonce,
32-
chainId: "0x2105", // Base Mainnet - 8453
33-
},
34-
},
35-
},
36-
],
37-
});
38-
39-
const walletAddress = accounts.accounts[0].address;
40-
console.log("walletAddress", walletAddress);
41-
const signature = accounts.accounts[0].capabilities.signInWithEthereum.signature;
42-
const message = accounts.accounts[0].capabilities.signInWithEthereum.message;
43-
console.log("message", message);
44-
console.log("signature", signature);
45-
46-
const publicClient = createPublicClient({
47-
chain: base,
48-
transport: http(),
49-
});
50-
51-
const isValid = await publicClient.verifyMessage({
52-
address: walletAddress as `0x${string}`,
53-
message,
54-
signature: signature as `0x${string}`,
55-
});
56-
console.log("isValid", isValid);
57-
// Verify the signature on the backend
58-
const verifyResponse = await fetch("/api/auth/verify", {
59-
method: "POST",
60-
headers: { "Content-Type": "application/json" },
61-
body: JSON.stringify({
62-
address: walletAddress,
63-
message,
64-
signature,
65-
}),
66-
});
67-
68-
const result = await verifyResponse.json();
69-
70-
if (result.success) {
71-
setVerificationResult(`Verified! Address: ${result.address}`);
72-
} else {
73-
setVerificationResult(`Verification failed: ${result.error}`);
74-
}
75-
} catch (err) {
76-
console.error("Error:", err);
77-
setVerificationResult(`Error: ${err instanceof Error ? err.message : "Unknown error"}`);
78-
}
79-
} else {
80-
console.error("No provider");
81-
}
82-
}
8311

8412
return (
8513
<>
@@ -106,15 +34,7 @@ function App() {
10634
{connectors.map((connector) => {
10735
if (connector.name === "Base Account") {
10836
return (
109-
<div style={{ width: "300px" }}>
110-
<SignInWithBaseButton
111-
key={connector.uid}
112-
onClick={() => handleBaseAccountConnect(connector)}
113-
variant="solid"
114-
colorScheme="system"
115-
align="center"
116-
/>
117-
</div>
37+
<SignInWithBase key={connector.uid} connector={connector} />
11838
);
11939
} else {
12040
return (
@@ -130,8 +50,9 @@ function App() {
13050
})}
13151
<div>{status}</div>
13252
<div>{error?.message}</div>
133-
{verificationResult && <div>{verificationResult}</div>}
13453
</div>
54+
55+
{account.status === "connected" && <BatchTransactions />}
13556
</>
13657
);
13758
}
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
"use client";
2+
3+
import { useState } from "react";
4+
import { useSendCalls } from "wagmi";
5+
import { encodeFunctionData, parseUnits } from "viem";
6+
import { baseSepolia } from "wagmi/chains";
7+
8+
// USDC contract address on Base Sepolia
9+
const USDC_ADDRESS = "0x036CbD53842c5426634e7929541eC2318f3dCF7e";
10+
11+
// ERC20 ABI for the transfer function
12+
const erc20Abi = [
13+
{
14+
inputs: [
15+
{ name: "to", type: "address" },
16+
{ name: "amount", type: "uint256" },
17+
],
18+
name: "transfer",
19+
outputs: [{ name: "", type: "bool" }],
20+
stateMutability: "nonpayable",
21+
type: "function",
22+
},
23+
] as const;
24+
25+
export function BatchTransactions() {
26+
const { sendCalls, data, isPending, isSuccess, error } = useSendCalls();
27+
const [amount1, setAmount1] = useState("1");
28+
const [amount2, setAmount2] = useState("1");
29+
const [usePaymaster, setUsePaymaster] = useState(false);
30+
31+
async function handleBatchTransfer() {
32+
try {
33+
// Encode the first transfer call
34+
const call1Data = encodeFunctionData({
35+
abi: erc20Abi,
36+
functionName: "transfer",
37+
args: [
38+
"0x2211d1D0020DAEA8039E46Cf1367962070d77DA9",
39+
parseUnits(amount1, 6), // USDC has 6 decimals
40+
],
41+
});
42+
43+
// Encode the second transfer call
44+
const call2Data = encodeFunctionData({
45+
abi: erc20Abi,
46+
functionName: "transfer",
47+
args: [
48+
"0xd8da6bf26964af9d7eed9e03e53415d37aa96045",
49+
parseUnits(amount2, 6), // USDC has 6 decimals
50+
],
51+
});
52+
53+
// Prepare capabilities object if paymaster is enabled
54+
const capabilities = usePaymaster
55+
? {
56+
paymasterService: {
57+
url: process.env.NEXT_PUBLIC_PAYMASTER_URL || "https://api.developer.coinbase.com/rpc/v1/base-sepolia",
58+
},
59+
}
60+
: undefined;
61+
62+
// Send the batch of calls
63+
sendCalls({
64+
calls: [
65+
{
66+
to: USDC_ADDRESS,
67+
data: call1Data,
68+
},
69+
{
70+
to: USDC_ADDRESS,
71+
data: call2Data,
72+
},
73+
],
74+
chainId: baseSepolia.id,
75+
capabilities,
76+
});
77+
} catch (err) {
78+
console.error("Error batching transactions:", err);
79+
}
80+
}
81+
82+
return (
83+
<div>
84+
<h2>Batch USDC Transfers</h2>
85+
86+
<div>
87+
<div>
88+
<label>Amount 1 (USDC):</label>
89+
<input
90+
type="number"
91+
value={amount1}
92+
onChange={(e) => setAmount1(e.target.value)}
93+
placeholder="1"
94+
step="0.000001"
95+
min="0"
96+
/>
97+
</div>
98+
99+
<div>
100+
<label>Amount 2 (USDC):</label>
101+
<input
102+
type="number"
103+
value={amount2}
104+
onChange={(e) => setAmount2(e.target.value)}
105+
placeholder="1"
106+
step="0.000001"
107+
min="0"
108+
/>
109+
</div>
110+
111+
<div>
112+
<label>
113+
<input
114+
type="checkbox"
115+
checked={usePaymaster}
116+
onChange={(e) => setUsePaymaster(e.target.checked)}
117+
/>
118+
Use Paymaster (sponsor gas fees)
119+
</label>
120+
</div>
121+
122+
<button onClick={handleBatchTransfer} disabled={isPending}>
123+
{isPending ? "Sending..." : "Send Batch Transfer"}
124+
</button>
125+
</div>
126+
127+
{isPending && <div>Transaction pending...</div>}
128+
129+
{isSuccess && data && (
130+
<div>
131+
<p>Batch sent successfully!</p>
132+
<p>Batch ID: {data.id}</p>
133+
</div>
134+
)}
135+
136+
{error && <div>Error: {error.message}</div>}
137+
</div>
138+
);
139+
}
140+
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
"use client";
2+
3+
import { Connector } from "wagmi";
4+
import { SignInWithBaseButton } from "@base-org/account-ui/react";
5+
import { useState } from "react";
6+
7+
interface SignInWithBaseProps {
8+
connector: Connector;
9+
}
10+
11+
export function SignInWithBase({ connector }: SignInWithBaseProps) {
12+
const [verificationResult, setVerificationResult] = useState<string>("");
13+
14+
async function handleBaseAccountConnect() {
15+
const provider = await connector.getProvider();
16+
if (provider) {
17+
try {
18+
// Generate a fresh nonce (this will be overwritten with the backend nonce)
19+
const clientNonce =
20+
Math.random().toString(36).substring(2, 15) +
21+
Math.random().toString(36).substring(2, 15);
22+
console.log("clientNonce", clientNonce);
23+
// Connect with SIWE to get signature, message, and address
24+
const accounts = await (provider as any).request({
25+
method: "wallet_connect",
26+
params: [
27+
{
28+
version: "1",
29+
capabilities: {
30+
signInWithEthereum: {
31+
nonce: clientNonce,
32+
chainId: "0x2105", // Base Mainnet - 8453
33+
},
34+
},
35+
},
36+
],
37+
});
38+
39+
const walletAddress = accounts.accounts[0].address;
40+
console.log("walletAddress", walletAddress);
41+
const signature =
42+
accounts.accounts[0].capabilities.signInWithEthereum.signature;
43+
const message =
44+
accounts.accounts[0].capabilities.signInWithEthereum.message;
45+
console.log("message", message);
46+
console.log("signature", signature);
47+
// Verify the signature on the backend
48+
const verifyResponse = await fetch("/api/auth/verify", {
49+
method: "POST",
50+
headers: { "Content-Type": "application/json" },
51+
body: JSON.stringify({
52+
address: walletAddress,
53+
message,
54+
signature,
55+
}),
56+
});
57+
58+
const result = await verifyResponse.json();
59+
60+
if (result.success) {
61+
setVerificationResult(`Verified! Address: ${result.address}`);
62+
} else {
63+
setVerificationResult(`Verification failed: ${result.error}`);
64+
}
65+
} catch (err) {
66+
console.error("Error:", err);
67+
setVerificationResult(
68+
`Error: ${err instanceof Error ? err.message : "Unknown error"}`
69+
);
70+
}
71+
} else {
72+
console.error("No provider");
73+
}
74+
}
75+
76+
return (
77+
<div>
78+
<div style={{ width: "300px" }}>
79+
<SignInWithBaseButton
80+
onClick={handleBaseAccountConnect}
81+
variant="solid"
82+
colorScheme="system"
83+
align="center"
84+
/>
85+
</div>
86+
{verificationResult && (
87+
<div style={{ marginTop: "10px" }}>{verificationResult}</div>
88+
)}
89+
</div>
90+
);
91+
}
92+

base-account/base-account-wagmi-template/src/wagmi.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,12 @@ import { baseAccount, injected, walletConnect } from "wagmi/connectors";
55
export function getConfig() {
66
return createConfig({
77
chains: [base, baseSepolia],
8+
multiInjectedProviderDiscovery: false,
89
connectors: [
910
baseAccount({
1011
appName: "My Wagmi App",
1112
}),
12-
walletConnect({ projectId: process.env.NEXT_PUBLIC_WC_PROJECT_ID! }),
13+
//walletConnect({ projectId: process.env.NEXT_PUBLIC_WC_PROJECT_ID! }),
1314
],
1415
storage: createStorage({
1516
storage: cookieStorage,

0 commit comments

Comments
 (0)