Skip to content

Commit

Permalink
cool stuff; xmtp client auto created if exists; but still need to add…
Browse files Browse the repository at this point in the history
… auto create when wallet created if doesn't exist

then im going to come up with some clever way for react-query to handle all of this logic and then restore xmtp clients by {address, inboxId?} where if inboxId is undefined we create and if it is defined we build and then also should have a check about the database existing in case something funky happened
  • Loading branch information
technoplato committed Jan 29, 2025
1 parent d7f849a commit a3ddc2c
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 67 deletions.
48 changes: 1 addition & 47 deletions privy-demo-quickstart/LoginScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,52 +50,6 @@ export function LoginScreen() {
<Text style={{ fontSize: 10 }}>
{Constants.expoConfig?.extra?.privyClientId}
</Text>
<Text>
Navigate to your{" "}
<Text
onPress={() =>
Linking.openURL(
`https://dashboard.privy.io/apps/${Constants.expoConfig?.extra?.privyAppId}/settings?setting=clients`
)
}
>
dashboard
</Text>{" "}
and ensure the following Expo Application ID is listed as an `Allowed
app identifier`:
</Text>
<Text style={{ fontSize: 10 }}>{Application.applicationId}</Text>
<Text>
Navigate to your{" "}
<Text
onPress={() =>
Linking.openURL(
`https://dashboard.privy.io/apps/${Constants.expoConfig?.extra?.privyAppId}/settings?setting=clients`
)
}
>
dashboard
</Text>{" "}
and ensure the following value is listed as an `Allowed app URL scheme`:
</Text>
<Text style={{ fontSize: 10 }}>
{Application.applicationId === "host.exp.Exponent"
? "exp"
: Constants.expoConfig?.scheme}
</Text>

{/* <Button
title="Login with Privy UIs"
onPress={() => {
login({ loginMethods: ["email"] })
.then((session) => {
console.log(JSON.stringify(session.user, null, 2));
})
.catch((err) => {
setError(JSON.stringify(err.error) as string);
});
}}
/> */}

<Button
title="Login using Passkey"
Expand All @@ -110,7 +64,7 @@ export function LoginScreen() {
title="Create Passkey"
onPress={() =>
signupWithPasskey({
relyingParty: Constants.expoConfig?.extra?.passkeyAssociatedDomain,
relyingParty: RELYING_PARTY,
})
.then(({ user }) => {
console.log(JSON.stringify(user, null, 2));
Expand Down
125 changes: 105 additions & 20 deletions privy-demo-quickstart/UserScreen.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState, useCallback } from "react";
import React, { useState, useCallback, useEffect } from "react";
import {
Text,
TextInput,
Expand All @@ -24,6 +24,7 @@ import { Client, Signer } from "@xmtp/react-native-sdk";
import { ethers } from "ethers";
import { ClientOptions } from "@xmtp/react-native-sdk/build/lib/Client";
import { Center } from "@/design-system/Center";
import logger from "@/utils/logger";

const toMainIdentifier = (x: PrivyUser["linked_accounts"][number]) => {
if (x.type === "phone") {
Expand All @@ -49,6 +50,8 @@ export const UserScreen = () => {
const [chainId, setChainId] = useState("1");
const [signedMessages, setSignedMessages] = useState<string[]>([]);
const [xmtpClient, setXmtpClient] = useState<Client | null>(null);
const [isLoadingClient, setIsLoadingClient] = useState(false);
const [xmtpClientError, setXmtpClientError] = useState<string | null>(null);

const { logout, user } = usePrivy();
const { linkWithPasskey } = useLinkWithPasskey();
Expand All @@ -59,37 +62,89 @@ export const UserScreen = () => {
const signMessage = useCallback(
async (provider: PrivyEmbeddedWalletProvider) => {
try {
// const plaintext = `0x0${Date.now()}`;
logger.debug(
`[UserScreen] Signing message for address: ${account?.address}`
);
const constantPlaintext = "foobar";
// const message = await provider.request({
// method: "personal_sign",
// params: [plaintext, account?.address],
// });
const signedConstantPlaintext = await provider.request({
method: "personal_sign",
params: [constantPlaintext, account?.address],
});
// if (message) {
// const withPlaintext = `${plaintext}\n${message}`;
// setSignedMessages((prev) => prev.concat(withPlaintext));
// }
if (signedConstantPlaintext) {
const withConstantPlaintext = `${constantPlaintext}\n${signedConstantPlaintext}`;
setSignedMessages((prev) => prev.concat(withConstantPlaintext));
logger.debug(
`[UserScreen] Successfully signed message for address: ${account?.address}`
);
}
} catch (e) {
console.error(e);
logger.error(`[UserScreen] Error signing message: ${e}`);
}
},
[account?.address]
);

useEffect(() => {
const buildExistingClient = async () => {
if (!account?.address || wallet.status !== "connected" || xmtpClient) {
logger.debug(
`[UserScreen] Skipping XMTP client build - address: ${
account?.address
}, wallet status: ${wallet.status}, existing client: ${!!xmtpClient}`
);
return;
}

logger.debug(
`[UserScreen] Building XMTP client for address: ${account.address}`
);
setIsLoadingClient(true);
setXmtpClientError(null);
try {
const encoder = new TextEncoder();
const addressBytes = encoder.encode(account.address);
const keyBytes = new Uint8Array(32);
for (let i = 0; i < 32; i++) {
keyBytes[i] = addressBytes[i % addressBytes.length];
}

const client = await Client.build(account.address, {
env: "dev",
dbEncryptionKey: keyBytes,
});

setXmtpClient(client);
logger.debug(
`[UserScreen] Successfully built XMTP client for address: ${account.address}`
);
} catch (error) {
const errorMsg = `Failed to build XMTP client: ${
error instanceof Error ? error.message : "Unknown error"
}`;
logger.error(`[UserScreen] ${errorMsg}`, error);
setXmtpClientError(errorMsg);
} finally {
setIsLoadingClient(false);
}
};

buildExistingClient();
}, [account?.address, wallet.status, xmtpClient]);

const createXmtpClient = useCallback(async () => {
if (!account?.address || wallet.status !== "connected") {
logger.debug(
`[UserScreen] Cannot create XMTP client - address: ${account?.address}, wallet status: ${wallet.status}`
);
alert("Wallet not connected");
return;
}

logger.debug(
`[UserScreen] Creating new XMTP client for address: ${account.address}`
);
setIsLoadingClient(true);
setXmtpClientError(null);
try {
const signer: Signer = {
getAddress: () => Promise.resolve(account.address),
Expand All @@ -105,35 +160,49 @@ export const UserScreen = () => {
walletType: () => "SCW",
};

// Create a proper 32-byte encryption key
const encryptionKey = new Uint8Array(32);
// Fill with random values
crypto.getRandomValues(encryptionKey);
const encoder = new TextEncoder();
const addressBytes = encoder.encode(account.address);
const keyBytes = new Uint8Array(32);
for (let i = 0; i < 32; i++) {
keyBytes[i] = addressBytes[i % addressBytes.length];
}

const options: ClientOptions = {
env: "dev",
dbEncryptionKey: encryptionKey,
dbEncryptionKey: keyBytes,
};

const client = await Client.create(signer, options);
setXmtpClient(client);
setXmtpClientError(null);
logger.debug(
`[UserScreen] Successfully created XMTP client for address: ${account.address}`
);
alert("XMTP client created successfully!");
} catch (error) {
console.error("Error creating XMTP client:", error);
alert("Failed to create XMTP client");
const errorMsg = `Failed to create XMTP client: ${
error instanceof Error ? error.message : "Unknown error"
}`;
logger.error(`[UserScreen] ${errorMsg}`, error);
setXmtpClientError(errorMsg);
alert(errorMsg);
} finally {
setIsLoadingClient(false);
}
}, [account, wallet]);

const switchChain = useCallback(
async (provider: PrivyEmbeddedWalletProvider, id: string) => {
try {
logger.debug(`[UserScreen] Switching chain to ID: ${id}`);
await provider.request({
method: "wallet_switchEthereumChain",
params: [{ chainId: id }],
});
logger.debug(`[UserScreen] Successfully switched chain to ID: ${id}`);
alert(`Chain switched to ${id} successfully`);
} catch (e) {
console.error(JSON.stringify(e, null, 2));
logger.error(`[UserScreen] Error switching chain: ${e}`);
}
},
[]
Expand Down Expand Up @@ -175,7 +244,14 @@ export const UserScreen = () => {
/>
)}

<Button title="Create XMTP Client" onPress={createXmtpClient} />
{!xmtpClient && !isLoadingClient && (
<Button title="Create XMTP Client" onPress={createXmtpClient} />
)}
{isLoadingClient && (
<Text style={{ textAlign: "center", margin: 10 }}>
Loading XMTP client...
</Text>
)}
{xmtpClient && (
<View
style={{ padding: 10, backgroundColor: "#f0f0f0", margin: 10 }}
Expand All @@ -186,6 +262,15 @@ export const UserScreen = () => {
</View>
)}

{xmtpClientError && (
<View
style={{ padding: 10, backgroundColor: "#ffe6e6", margin: 10 }}
>
<Text style={{ color: "red", fontWeight: "bold" }}>Error:</Text>
<Text style={{ color: "red" }}>{xmtpClientError}</Text>
</View>
)}

<ScrollView
style={{ borderColor: "rgba(0,0,0,0.1)", borderWidth: 1 }}
>
Expand Down

0 comments on commit a3ddc2c

Please sign in to comment.