Skip to content

Commit f421c32

Browse files
authored
Merge pull request #3 from dancoombs/danc/jake-changes
feat: modify to use wallet-apis
2 parents c1e61c7 + 575ac61 commit f421c32

File tree

8 files changed

+24089
-6468
lines changed

8 files changed

+24089
-6468
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,3 +103,5 @@ dist
103103

104104
# TernJS port file
105105
.tern-port
106+
107+
.DS_Store

components/smart-wallet-demo.tsx

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
import { ConnectedWallet as PrivyWallet } from "@privy-io/react-auth";
2+
import { useSmartEmbeddedWallet } from "../hooks/use-smart-embedded-wallet";
3+
import { useCallback, useState } from "react";
4+
import type { Address, Hex } from "viem";
5+
6+
export const SmartWalletDemo = ({
7+
embeddedWallet,
8+
}: {
9+
embeddedWallet: PrivyWallet;
10+
}) => {
11+
const { client } = useSmartEmbeddedWallet({ embeddedWallet });
12+
13+
const [status, setStatus] = useState<
14+
| { status: "idle" | "error" | "sending" }
15+
| { status: "success"; txHash: Hex }
16+
>({ status: "idle" });
17+
18+
const delegateAndSend = useCallback(async () => {
19+
if (!client) {
20+
return;
21+
}
22+
23+
setStatus({ status: "sending" });
24+
try {
25+
const {
26+
preparedCallIds: [callId],
27+
} = await client.sendCalls({
28+
capabilities: {
29+
eip7702Auth: true,
30+
},
31+
from: embeddedWallet.address as Address,
32+
calls: [
33+
{
34+
to: "0x0000000000000000000000000000000000000000",
35+
data: "0x",
36+
},
37+
],
38+
});
39+
if (!callId) {
40+
throw new Error("Missing call id");
41+
}
42+
43+
const { receipts } = await client.waitForCallsStatus({ id: callId });
44+
if (!receipts?.length) {
45+
throw new Error("Missing transaction receipts");
46+
}
47+
const [receipt] = receipts;
48+
if (receipt?.status !== "success") {
49+
throw new Error("Transaction failed");
50+
}
51+
setStatus({ status: "success", txHash: receipt.transactionHash });
52+
} catch (err) {
53+
console.error("Transaction failed:", err);
54+
setStatus({ status: "error" });
55+
}
56+
}, [client, embeddedWallet]);
57+
58+
return (
59+
<div className="flex flex-col gap-4">
60+
<div>
61+
<h2 className="text-lg font-semibold text-gray-900">
62+
Embedded EOA Address
63+
</h2>
64+
<p className="text-gray-600 font-mono break-all">
65+
{embeddedWallet.address}
66+
</p>
67+
</div>
68+
<button
69+
onClick={delegateAndSend}
70+
disabled={!client || status.status === "sending"}
71+
className={`w-full py-3 px-4 rounded-lg font-semibold text-white transition-colors ${
72+
status.status === "sending"
73+
? "bg-indigo-400 cursor-not-allowed"
74+
: "bg-indigo-600 hover:bg-indigo-700"
75+
}`}
76+
>
77+
{status.status === "sending"
78+
? "Sending..."
79+
: "Upgrade & Send Sponsored Transaction"}
80+
</button>
81+
{status.status === "success" && (
82+
<section className="bg-green-50 rounded-xl shadow-lg p-6 border border-green-200">
83+
<h2 className="text-lg font-semibold text-green-900 mb-4">
84+
Congrats! Sponsored transaction successful!
85+
</h2>
86+
<p className="text-green-700 mb-4">
87+
You've successfully upgraded your EOA to a smart account and sent
88+
your first sponsored transaction.{" "}
89+
<a
90+
href="https://www.alchemy.com/docs/wallets/react/using-7702"
91+
className="text-indigo-600 hover:underline"
92+
target="_blank"
93+
rel="noopener noreferrer"
94+
>
95+
Keep building
96+
</a>
97+
.
98+
</p>
99+
<p className="text-green-700">
100+
<strong>Transaction Hash:</strong>{" "}
101+
<span className="font-mono break-all">{status.txHash}</span>
102+
</p>
103+
</section>
104+
)}
105+
{status.status === "error" && (
106+
<section className="bg-red-50 rounded-xl shadow-lg p-6 border border-red-200">
107+
<h2 className="text-lg font-semibold text-red-900 mb-4">
108+
Transaction Failed
109+
</h2>
110+
<p className="text-red-700">
111+
There was an error sending your sponsored transaction. Please try
112+
again.
113+
</p>
114+
</section>
115+
)}
116+
</div>
117+
);
118+
};

hooks/use-smart-embedded-wallet.ts

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import { Address, Authorization, createWalletClient, custom } from "viem";
2+
import { useSign7702Authorization } from "@privy-io/react-auth";
3+
import { AuthorizationRequest, WalletClientSigner } from "@aa-sdk/core";
4+
import { sepolia, alchemy } from "@account-kit/infra";
5+
import { useEffect, useState } from "react";
6+
import {
7+
createSmartWalletClient,
8+
SmartWalletClient,
9+
} from "@account-kit/wallet-client";
10+
import { ConnectedWallet as PrivyWallet } from "@privy-io/react-auth";
11+
12+
/** Creates an Alchemy Smart Wallet client for an embedded Privy wallet using EIP-7702. */
13+
export const useSmartEmbeddedWallet = ({
14+
embeddedWallet,
15+
}: {
16+
embeddedWallet: PrivyWallet;
17+
}) => {
18+
const { signAuthorization } = useSign7702Authorization();
19+
const [client, setClient] = useState<SmartWalletClient>();
20+
21+
useEffect(() => {
22+
const apiKey = process.env.NEXT_PUBLIC_ALCHEMY_API_KEY;
23+
if (!apiKey) {
24+
throw new Error("Missing NEXT_PUBLIC_ALCHEMY_API_KEY");
25+
}
26+
(async () => {
27+
const provider = await embeddedWallet.getEthereumProvider();
28+
29+
const baseSigner = new WalletClientSigner(
30+
createWalletClient({
31+
account: embeddedWallet.address as Address,
32+
chain: sepolia,
33+
transport: custom(provider),
34+
}),
35+
"privy"
36+
);
37+
38+
const signer = {
39+
...baseSigner,
40+
signAuthorization: async (
41+
unsignedAuth: AuthorizationRequest<number>
42+
): Promise<Authorization<number, true>> => {
43+
const signature = await signAuthorization({
44+
...unsignedAuth,
45+
contractAddress:
46+
unsignedAuth.address ?? unsignedAuth.contractAddress,
47+
});
48+
49+
return {
50+
...unsignedAuth,
51+
...signature,
52+
};
53+
},
54+
};
55+
56+
const client = createSmartWalletClient({
57+
chain: sepolia,
58+
transport: alchemy({
59+
apiKey,
60+
}),
61+
signer,
62+
policyId: process.env.NEXT_PUBLIC_ALCHEMY_POLICY_ID,
63+
});
64+
65+
setClient(client);
66+
})();
67+
}, [embeddedWallet, signAuthorization]);
68+
69+
return { client };
70+
};

next-env.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
/// <reference types="next" />
22
/// <reference types="next/image-types/global" />
3+
/// <reference path="./.next/types/routes.d.ts" />
34

45
// NOTE: This file should not be edited
56
// see https://nextjs.org/docs/pages/api-reference/config/typescript for more information.

0 commit comments

Comments
 (0)