Skip to content

Commit

Permalink
Merge pull request #7 from privy-io/ankush/add-linking-to-demo
Browse files Browse the repository at this point in the history
Add example of linking smart account to user
  • Loading branch information
ankushswar1 authored Aug 12, 2024
2 parents 5758363 + 9de3131 commit 6f66cf3
Show file tree
Hide file tree
Showing 5 changed files with 72 additions and 35 deletions.
39 changes: 25 additions & 14 deletions hooks/SmartAccountContext.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,17 @@
import React, { useState, useEffect, useContext } from "react";
import { ConnectedWallet, useWallets } from "@privy-io/react-auth";
import { ConnectedWallet, usePrivy, useWallets } from "@privy-io/react-auth";
import { createPublicClient, createWalletClient, custom, http } from "viem";
import { baseSepolia } from "viem/chains";
import { Address, Chain, Transport } from "viem";
import { SmartAccount } from "permissionless/accounts";
import { Chain, Transport } from "viem";
import { signerToSafeSmartAccount, SmartAccount } from "permissionless/accounts";
import {
walletClientToSmartAccountSigner,
type SmartAccountClient,
createSmartAccountClient,
ENTRYPOINT_ADDRESS_V06,
ENTRYPOINT_ADDRESS_V07,
} from "permissionless";
import { EntryPoint } from "permissionless/types";
import { signerToSimpleSmartAccount } from "permissionless/accounts";
import { createPimlicoPaymasterClient } from "permissionless/clients/pimlico";
import { SMART_ACCOUNT_FACTORY_ADDRESS } from "../lib/constants";
import { createPimlicoBundlerClient, createPimlicoPaymasterClient } from "permissionless/clients/pimlico";

/** Interface returned by custom `useSmartAccount` hook */
interface SmartAccountInterface {
Expand Down Expand Up @@ -50,11 +48,14 @@ export const SmartAccountProvider = ({
}) => {
// Get a list of all of the wallets (EOAs) the user has connected to your site
const { wallets } = useWallets();
const {ready} = usePrivy();
// Find the embedded wallet by finding the entry in the list with a `walletClientType` of 'privy'
const embeddedWallet = wallets.find(
(wallet) => wallet.walletClientType === "privy"
);

console.log({wallets});

// States to store the smart account and its status
const [eoa, setEoa] = useState<ConnectedWallet | undefined>();
const [smartAccountClient, setSmartAccountClient] = useState<
Expand All @@ -69,6 +70,10 @@ export const SmartAccountProvider = ({
>();
const [smartAccountReady, setSmartAccountReady] = useState(false);

useEffect(() => {
if (!ready) return;
}, [ready, embeddedWallet]);

useEffect(() => {
// Creates a smart account given a Privy `ConnectedWallet` object representing
// the user's EOA.
Expand All @@ -89,28 +94,34 @@ export const SmartAccountProvider = ({
transport: http(),
});

const simpleSmartAccount = await signerToSimpleSmartAccount(
const safeAccount = await signerToSafeSmartAccount(
publicClient,
{
entryPoint: ENTRYPOINT_ADDRESS_V06,
signer: customSigner,
factoryAddress: SMART_ACCOUNT_FACTORY_ADDRESS! as Address,
safeVersion: '1.4.1',
entryPoint: ENTRYPOINT_ADDRESS_V07
}
);

const pimlicoPaymaster = createPimlicoPaymasterClient({
chain: baseSepolia,
transport: http(process.env.NEXT_PUBLIC_PIMLICO_PAYMASTER_URL),
entryPoint: ENTRYPOINT_ADDRESS_V06,
entryPoint: ENTRYPOINT_ADDRESS_V07,
});

const pimlicoBundler = createPimlicoBundlerClient({
transport: http(process.env.NEXT_PUBLIC_PIMLICO_PAYMASTER_URL),
entryPoint: ENTRYPOINT_ADDRESS_V07,
})

const smartAccountClient = createSmartAccountClient({
account: simpleSmartAccount,
entryPoint: ENTRYPOINT_ADDRESS_V06,
account: safeAccount,
entryPoint: ENTRYPOINT_ADDRESS_V07,
chain: baseSepolia, // Replace this with the chain for your app
bundlerTransport: http(process.env.NEXT_PUBLIC_PIMLICO_BUNDLER_URL),
middleware: {
sponsorUserOperation: pimlicoPaymaster.sponsorUserOperation, // If your app uses a paymaster for gas sponsorship
sponsorUserOperation: pimlicoPaymaster.sponsorUserOperation,
gasPrice: async () => (await pimlicoBundler.getUserOperationGasPrice()).fast,
},
});

Expand Down
30 changes: 15 additions & 15 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"dependencies": {
"@headlessui/react": "^1.7.19",
"@heroicons/react": "^2.1.3",
"@privy-io/react-auth": "^1.77.0",
"@privy-io/react-auth": "^1.78.1",
"@tailwindcss/forms": "^0.5.7",
"abitype": "^1.0.2",
"next": "latest",
Expand Down
5 changes: 1 addition & 4 deletions pages/_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,12 @@ import "../styles/globals.css";
import type { AppProps } from "next/app";
import Head from "next/head";
import { PrivyProvider } from "@privy-io/react-auth";
import { useRouter } from "next/router";
import { baseSepolia } from "viem/chains";
import { SmartAccountProvider } from "../hooks/SmartAccountContext";
import { ToastContainer } from "react-toastify";
import "react-toastify/dist/ReactToastify.css";

function MyApp({ Component, pageProps }: AppProps) {
const router = useRouter();

return (
<>
<Head>
Expand Down Expand Up @@ -49,6 +46,7 @@ function MyApp({ Component, pageProps }: AppProps) {
</Head>
<PrivyProvider
appId={process.env.NEXT_PUBLIC_PRIVY_APP_ID || ""}

config={{
loginMethods: ["email", "google"],
appearance: {
Expand All @@ -62,7 +60,6 @@ function MyApp({ Component, pageProps }: AppProps) {
// @ts-ignore
defaultChain: baseSepolia,
}}
onSuccess={() => router.push("/dashboard")}
>
<SmartAccountProvider>
<ToastContainer position="top-right" />
Expand Down
31 changes: 30 additions & 1 deletion pages/dashboard.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useRouter } from "next/router";
import React, { useEffect, useState } from "react";
import { usePrivy } from "@privy-io/react-auth";
import { useLinkWithSiwe, usePrivy } from "@privy-io/react-auth";
import Head from "next/head";
import { useSmartAccount } from "../hooks/SmartAccountContext";
import { BASE_SEPOLIA_SCAN_URL, NFT_ADDRESS } from "../lib/constants";
Expand All @@ -13,6 +13,7 @@ import { baseSepolia } from "viem/chains";
export default function DashboardPage() {
const router = useRouter();
const { ready, authenticated, user, logout } = usePrivy();
const {generateSiweMessage, linkWithSiwe} = useLinkWithSiwe();
const { smartAccountAddress, smartAccountClient, eoa } = useSmartAccount();

// If the user is not authenticated, redirect them back to the landing page
Expand Down Expand Up @@ -79,6 +80,27 @@ export default function DashboardPage() {
setIsMinting(false);
};

const onLink = async () => {
// The link button is disabled if either of these are undefined
if (!smartAccountClient || !smartAccountAddress) return;
const chainId = `eip155:${baseSepolia.id}`;

const message = await generateSiweMessage({
address: smartAccountAddress,
chainId
});

const signature = await smartAccountClient.signMessage({message});

await linkWithSiwe({
signature,
message,
chainId,
walletClientType: 'privy_smart_account',
connectorType: 'safe'
});
}

return (
<>
<Head>
Expand Down Expand Up @@ -108,6 +130,13 @@ export default function DashboardPage() {
>
Mint NFT
</button>
<button
onClick={onLink}
className="text-sm bg-violet-600 hover:bg-violet-700 disabled:bg-violet-400 py-2 px-4 rounded-md text-white"
disabled={isLoading}
>
Link smart account to user
</button>
</div>
<p className="mt-6 font-bold uppercase text-sm text-gray-600">
Your Smart Wallet Address
Expand Down

0 comments on commit 6f66cf3

Please sign in to comment.