Skip to content

Commit

Permalink
🚧 Privy embeded provider + switch provider depending on the route
Browse files Browse the repository at this point in the history
  • Loading branch information
KONFeature committed Dec 31, 2024
1 parent 6965ea0 commit 323d67c
Show file tree
Hide file tree
Showing 25 changed files with 316 additions and 36 deletions.
Binary file modified bun.lockb
Binary file not shown.
4 changes: 4 additions & 0 deletions example/news-interactions/sst-env.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ declare module "sst" {
"type": "sst.sst.Secret"
"value": string
}
"PRIVY_APP_ID": {
"type": "sst.sst.Secret"
"value": string
}
"SESSION_ENCRYPTION_KEY": {
"type": "sst.sst.Secret"
"value": string
Expand Down
4 changes: 4 additions & 0 deletions example/vanilla-js/sst-env.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ declare module "sst" {
"type": "sst.sst.Secret"
"value": string
}
"PRIVY_APP_ID": {
"type": "sst.sst.Secret"
"value": string
}
"SESSION_ENCRYPTION_KEY": {
"type": "sst.sst.Secret"
"value": string
Expand Down
4 changes: 4 additions & 0 deletions example/wallet-ethcc/sst-env.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ declare module "sst" {
"type": "sst.sst.Secret"
"value": string
}
"PRIVY_APP_ID": {
"type": "sst.sst.Secret"
"value": string
}
"SESSION_ENCRYPTION_KEY": {
"type": "sst.sst.Secret"
"value": string
Expand Down
1 change: 1 addition & 0 deletions infra/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ export const vapidPublicKey = new sst.Secret("VAPID_PUBLIC_KEY");
export const umamiWalletWebsiteId = new sst.Secret("UMAMI_WALLET_WEBSITE_ID");
export const sessionEncryptionKy = new sst.Secret("SESSION_ENCRYPTION_KEY");
export const mongoBusinessDb = new sst.Secret("MONGODB_BUSINESS_URI");
export const privyAppId = new sst.Secret("PRIVY_APP_ID");

export const postgresHost = new sst.Secret("POSTGRES_HOST");
export const postgresPassword = new sst.Secret("POSTGRES_PASSWORD");
3 changes: 2 additions & 1 deletion infra/wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
indexerUrl,
isProd,
nexusRpcSecret,
pimlicoApiKey,
pimlicoApiKey, privyAppId,
umamiWalletWebsiteId,
vapidPublicKey,
} from "./config";
Expand Down Expand Up @@ -37,5 +37,6 @@ new sst.aws.StaticSite("Wallet", {
NEXUS_RPC_SECRET: nexusRpcSecret.value,
VAPID_PUBLIC_KEY: vapidPublicKey.value,
UMAMI_WALLET_WEBSITE_ID: umamiWalletWebsiteId.value,
PRIVY_APP_ID: privyAppId.value,
},
});
4 changes: 4 additions & 0 deletions packages/app-essentials/sst-env.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ declare module "sst" {
"type": "sst.sst.Secret"
"value": string
}
"PRIVY_APP_ID": {
"type": "sst.sst.Secret"
"value": string
}
"SESSION_ENCRYPTION_KEY": {
"type": "sst.sst.Secret"
"value": string
Expand Down
4 changes: 4 additions & 0 deletions packages/backend-elysia/sst-env.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ declare module "sst" {
"type": "sst.sst.Secret"
"value": string
}
"PRIVY_APP_ID": {
"type": "sst.sst.Secret"
"value": string
}
"SESSION_ENCRYPTION_KEY": {
"type": "sst.sst.Secret"
"value": string
Expand Down
4 changes: 4 additions & 0 deletions packages/dashboard/sst-env.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ declare module "sst" {
"type": "sst.sst.Secret"
"value": string
}
"PRIVY_APP_ID": {
"type": "sst.sst.Secret"
"value": string
}
"SESSION_ENCRYPTION_KEY": {
"type": "sst.sst.Secret"
"value": string
Expand Down
4 changes: 4 additions & 0 deletions packages/shared/sst-env.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ declare module "sst" {
"type": "sst.sst.Secret"
"value": string
}
"PRIVY_APP_ID": {
"type": "sst.sst.Secret"
"value": string
}
"SESSION_ENCRYPTION_KEY": {
"type": "sst.sst.Secret"
"value": string
Expand Down
6 changes: 6 additions & 0 deletions packages/wallet/app/module/common/provider/PrivyProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { useSyncEcdsaSession } from "@/module/common/hook/useSyncEcdsaSession";
import { createContext, useContext } from "react";
import type { Address, Hex } from "viem";

Expand Down Expand Up @@ -43,3 +44,8 @@ export const usePrivyContext = () => {
}
return context;
};

export function PrivySessionSyncer() {
useSyncEcdsaSession();
return null;
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import { currentChain } from "@/context/blockchain/provider";
import { createPrivyCrossAppClient } from "@privy-io/cross-app-connect";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { type ReactNode, useMemo } from "react";
import { type PropsWithChildren, useMemo } from "react";
import type { Address, Hex } from "viem";
import { PrivyContext } from "./PrivyProvider";
import { PrivyContext, PrivySessionSyncer } from "./PrivyProvider";

/**
* Export the privy SDK provider
* @constructor
*/
export function PrivySdkProvider({ children }: { children: ReactNode }) {
export function PrivySdkProvider({ children }: PropsWithChildren) {
const queryClient = useQueryClient();

/**
Expand Down Expand Up @@ -112,6 +112,7 @@ export function PrivySdkProvider({ children }: { children: ReactNode }) {

return (
<PrivyContext.Provider value={context}>
<PrivySessionSyncer />
{children}
</PrivyContext.Provider>
);
Expand Down
187 changes: 187 additions & 0 deletions packages/wallet/app/module/common/provider/PrivyWalletProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
import { currentChain } from "@/context/blockchain/provider";
import {
PrivyProvider,
type User,
useLogin,
usePrivy,
} from "@privy-io/react-auth";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import {
type PropsWithChildren,
type ReactNode,
useMemo,
useState,
} from "react";
import type { Address, Hex } from "viem";
import { PrivyContext, PrivySessionSyncer } from "./PrivyProvider";

/**
* Export the privy Wallet provider
* @constructor
*/
export function PrivyWalletProvider({ children }: { children: ReactNode }) {
return (
<PrivyProviderWithConfig>
<InnerPrivyWalletProvider>
<PrivySessionSyncer />
{children}
</InnerPrivyWalletProvider>
</PrivyProviderWithConfig>
);
}

function PrivyProviderWithConfig({ children }: PropsWithChildren) {
return (
<PrivyProvider
appId={process.env.PRIVY_APP_ID ?? ""}
config={{
// Customize Privy's appearance in your app
appearance: {
theme: "light",
accentColor: "#676FFF",
logo: "https://wallet.frak.id/icon-192.png",
walletChainType: "ethereum-only",
},
embeddedWallets: {
// Create wallet on login for user who don't have one
createOnLogin: "users-without-wallets",
},
supportedChains: [currentChain],
defaultChain: currentChain,
}}
>
{children}
</PrivyProvider>
);
}

function InnerPrivyWalletProvider({ children }: PropsWithChildren) {
const queryClient = useQueryClient();
const { ready, logout, user, signMessage: baseSignMessage } = usePrivy();

// Small promise wrapper to map the login callback stuff to a mutation
const [loginPromise, setLoginPromise] = useState<{
resolve?: (value: User) => void;
reject?: (reason?: Error) => void;
}>({});

// Privy login, using the promise wrapper to send back the results to the mutation
const { login: baseLogin } = useLogin({
onComplete(user) {
console.log("Privy login complete", user);
// Invalidate privy related queries
queryClient.invalidateQueries({
queryKey: ["privy"],
exact: false,
});
// Resolve the promise
loginPromise.resolve?.(user);
},
onError() {
console.log("Privy login failed", user);
// Invalidate privy related queries
queryClient.invalidateQueries({
queryKey: ["privy"],
exact: false,
});
// Reject the login promise
loginPromise.reject?.(new Error("Login failed"));
},
});
/**
* Launch a login process
*/
const { mutateAsync: login } = useMutation({
mutationKey: ["privy", "login"],
async mutationFn() {
// Check if we got a user already
if (wallet) {
return wallet;
}

// Set the new login promise
const loginPromise = new Promise<User>((resolve, reject) => {
setLoginPromise({
resolve,
reject,
});
});

// Trigger the login
baseLogin();

// Wait for the login to complete
console.log("Waiting for login to complete");
const user = await loginPromise;
console.log("Login complete", user);

// Ensure the user got a wallet
if (!user.wallet) {
throw new Error("No wallet found");
}

return user.wallet.address as Address;
},
});

/**
* Get the current wallet address
*/
const { data: wallet } = useQuery({
queryKey: [
"privy",
"wallet",
user?.id ?? "no-user",
user?.wallet?.address ?? "no-wallet",
],
enabled: ready,
queryFn() {
if (!ready) return null;
if (!user?.wallet?.address) return null;

return user.wallet.address as Address;
},
refetchOnMount: "always",
refetchOnWindowFocus: "always",
});

/**
* Launch a sign message process
*/
const { mutateAsync: signMessage } = useMutation({
mutationKey: ["privy", "sign-message", wallet],
async mutationFn({ hash, address }: { hash: Hex; address: Address }) {
const signature = await baseSignMessage(
hash,
{
showWalletUIs: true,
title: "Validate the action",
description:
"Sign the following message to validate the current Frak action",
},
address
);
if (!signature) {
throw new Error("No signature returned");
}
return signature as Hex;
},
});

const context = useMemo(
() => ({
ready,
wallet: wallet ?? undefined,
login,
signMessage,
logout,
}),
[ready, wallet, login, logout, signMessage]
);

return (
<PrivyContext.Provider value={context}>
{children}
</PrivyContext.Provider>
);
}
21 changes: 8 additions & 13 deletions packages/wallet/app/module/common/provider/RootProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ import { currentChain } from "@/context/blockchain/provider";
import { authenticatedBackendApi } from "@/context/common/backendClient";
import { smartAccountConnector } from "@/context/wallet/smartWallet/connector";
import { useEnforceWagmiConnection } from "@/module/common/hook/useEnforceWagmiConnection";
import { useSyncEcdsaSession } from "@/module/common/hook/useSyncEcdsaSession";
import { PrivySdkProvider } from "@/module/common/provider/PrivySdkProvider";
import { subscriptionAtom } from "@/module/notification/atom/subscriptionAtom";
import { getTransport } from "@frak-labs/app-essentials/blockchain";
import { jotaiStore } from "@module/atoms/store";
Expand Down Expand Up @@ -59,16 +57,14 @@ export function RootProvider({ children }: PropsWithChildren) {
client={queryClient}
persistOptions={persistOptions}
>
<PrivySdkProvider>
<SetupServiceWorker />
<WagmiProviderWithDynamicConfig>
{children}
</WagmiProviderWithDynamicConfig>
<ReactQueryDevtools
initialIsOpen={false}
buttonPosition={"bottom-left"}
/>
</PrivySdkProvider>
<SetupServiceWorker />
<WagmiProviderWithDynamicConfig>
{children}
</WagmiProviderWithDynamicConfig>
<ReactQueryDevtools
initialIsOpen={false}
buttonPosition={"bottom-left"}
/>
</PersistQueryClientProvider>
</Provider>
</>
Expand Down Expand Up @@ -164,6 +160,5 @@ function WagmiProviderWithDynamicConfig({ children }: PropsWithChildren) {

function EnforceWagmiConnection() {
useEnforceWagmiConnection();
useSyncEcdsaSession();
return null;
}
Loading

0 comments on commit 323d67c

Please sign in to comment.