@coinbase/create-cdp-app 0.0.43 → 0.0.44

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/dist/index.js +119 -41
  2. package/dist/index.js.map +1 -1
  3. package/package.json +1 -1
  4. package/template-nextjs/src/components/{Header.tsx → evm-eoa/Header.tsx} +0 -4
  5. package/template-nextjs/src/components/evm-eoa/SignedInScreen.tsx +68 -0
  6. package/template-nextjs/src/components/evm-eoa/SignedInScreenWithOnramp.tsx +117 -0
  7. package/template-nextjs/src/components/{UserBalance.tsx → evm-eoa/UserBalance.tsx} +3 -4
  8. package/template-nextjs/src/components/evm-smart/Header.tsx +64 -0
  9. package/template-nextjs/src/components/evm-smart/SignedInScreen.tsx +68 -0
  10. package/template-nextjs/src/components/evm-smart/SignedInScreenWithOnramp.tsx +120 -0
  11. package/template-nextjs/src/components/evm-smart/UserBalance.tsx +43 -0
  12. package/template-nextjs/src/components/solana/Header.tsx +63 -0
  13. package/template-nextjs/src/components/solana/SignedInScreen.tsx +74 -0
  14. package/template-nextjs/src/components/solana/SignedInScreenWithOnramp.tsx +121 -0
  15. package/template-nextjs/src/components/solana/UserBalance.tsx +43 -0
  16. package/template-react/src/evm-eoa/Header.tsx +67 -0
  17. package/template-react/src/evm-eoa/SignedInScreen.tsx +64 -0
  18. package/template-react/src/{UserBalance.tsx → evm-eoa/UserBalance.tsx} +10 -28
  19. package/template-react/src/evm-smart/Header.tsx +68 -0
  20. package/template-react/src/evm-smart/SignedInScreen.tsx +64 -0
  21. package/template-react/src/evm-smart/UserBalance.tsx +44 -0
  22. package/template-react/src/{Header.tsx → solana/Header.tsx} +9 -21
  23. package/template-react/src/solana/SignedInScreen.tsx +70 -0
  24. package/template-react/src/solana/UserBalance.tsx +44 -0
  25. package/template-react-native/{components → evm-eoa}/WalletHeader.tsx +10 -21
  26. package/template-react-native/evm-smart/WalletHeader.tsx +115 -0
  27. package/template-react-native/solana/WalletHeader.tsx +111 -0
  28. package/template-nextjs/src/components/SignedInScreen.tsx +0 -116
  29. package/template-nextjs/src/components/SignedInScreenWithOnramp.tsx +0 -239
  30. package/template-react/src/SignedInScreen.tsx +0 -116
  31. package/template-react-native/Transaction.tsx +0 -101
  32. /package/template-nextjs/src/components/{EOATransaction.tsx → evm-eoa/EOATransaction.tsx} +0 -0
  33. /package/template-nextjs/src/components/{SmartAccountTransaction.tsx → evm-smart/SmartAccountTransaction.tsx} +0 -0
  34. /package/template-nextjs/src/components/{SolanaTransaction.tsx → solana/SolanaTransaction.tsx} +0 -0
  35. /package/template-react/src/{EOATransaction.tsx → evm-eoa/EOATransaction.tsx} +0 -0
  36. /package/template-react/src/{SmartAccountTransaction.tsx → evm-smart/SmartAccountTransaction.tsx} +0 -0
  37. /package/template-react/src/{SolanaTransaction.tsx → solana/SolanaTransaction.tsx} +0 -0
  38. /package/template-react-native/{EOATransaction.tsx → evm-eoa/EOATransaction.tsx} +0 -0
  39. /package/template-react-native/{SmartAccountTransaction.tsx → evm-smart/SmartAccountTransaction.tsx} +0 -0
  40. /package/template-react-native/{SolanaTransaction.tsx → solana/SolanaTransaction.tsx} +0 -0
@@ -0,0 +1,44 @@
1
+ import { LoadingSkeleton } from "@coinbase/cdp-react/components/ui/LoadingSkeleton";
2
+
3
+ interface Props {
4
+ balance?: string;
5
+ }
6
+
7
+ /**
8
+ * A component that displays the user's balance.
9
+ *
10
+ * @param {Props} props - The props for the UserBalance component.
11
+ * @param {string} [props.balance] - The user's balance.
12
+ * @returns A component that displays the user's balance.
13
+ */
14
+ function UserBalance(props: Props) {
15
+ const { balance } = props;
16
+
17
+ return (
18
+ <>
19
+ <h2 className="card-title">Available balance</h2>
20
+ <p className="user-balance flex-col-container flex-grow">
21
+ {balance === undefined && <LoadingSkeleton as="span" className="loading--balance" />}
22
+ {balance !== undefined && (
23
+ <span className="flex-row-container">
24
+ <img src="/sol.svg" alt="" className="balance-icon" />
25
+ <span>{balance}</span>
26
+ <span className="sr-only">Solana</span>
27
+ </span>
28
+ )}
29
+ </p>
30
+ <p>
31
+ Get testnet SOL from{" "}
32
+ <a
33
+ href="https://portal.cdp.coinbase.com/products/faucet?network=solana-devnet"
34
+ target="_blank"
35
+ rel="noopener noreferrer"
36
+ >
37
+ Solana Devnet Faucet
38
+ </a>
39
+ </p>
40
+ </>
41
+ );
42
+ }
43
+
44
+ export default UserBalance;
@@ -1,9 +1,9 @@
1
1
  import React from "react";
2
2
  import { View, Text, TouchableOpacity, StyleSheet, Alert } from "react-native";
3
3
  import * as Clipboard from "expo-clipboard";
4
- import { useEvmAddress, useCurrentUser, useSolanaAddress } from "@coinbase/cdp-hooks";
4
+ import { useEvmAddress, useCurrentUser } from "@coinbase/cdp-hooks";
5
5
  import { useTheme } from "../theme/ThemeContext";
6
- import { UserIcon } from "./UserIcon";
6
+ import { UserIcon } from "../components/UserIcon";
7
7
 
8
8
  interface WalletHeaderProps {
9
9
  onSignOut: () => void;
@@ -12,26 +12,24 @@ interface WalletHeaderProps {
12
12
  export const WalletHeader: React.FC<WalletHeaderProps> = ({ onSignOut }) => {
13
13
  const { colors } = useTheme();
14
14
  const { evmAddress } = useEvmAddress();
15
- const { solanaAddress } = useSolanaAddress();
16
15
  const { currentUser } = useCurrentUser();
17
16
 
18
17
  // Use EVM address if available, otherwise fall back to smart account
19
18
  const evmWalletAddress = evmAddress || currentUser?.evmSmartAccounts?.[0];
20
- const solanaWalletAddress = solanaAddress;
21
19
 
22
20
  const formatAddress = (address: string) => {
23
21
  if (!address) return "";
24
22
  return `${address.slice(0, 6)}...${address.slice(-4)}`;
25
23
  };
26
24
 
27
- const copyWalletAddress = async (address: string, type: "EVM" | "Solana") => {
28
- if (!address) return;
25
+ const copyWalletAddress = async () => {
26
+ if (!evmWalletAddress) return;
29
27
 
30
28
  try {
31
- await Clipboard.setStringAsync(address);
32
- Alert.alert("Copied!", `${type} address copied to clipboard.`);
29
+ await Clipboard.setStringAsync(evmWalletAddress);
30
+ Alert.alert("Copied!", "EVM address copied to clipboard.");
33
31
  } catch (error) {
34
- Alert.alert("Error", `Failed to copy ${type} address.`);
32
+ Alert.alert("Error", "Failed to copy EVM address.");
35
33
  }
36
34
  };
37
35
 
@@ -97,23 +95,14 @@ export const WalletHeader: React.FC<WalletHeaderProps> = ({ onSignOut }) => {
97
95
  <UserIcon size={18} color={colors.cardBackground} />
98
96
  </View>
99
97
  <View style={styles.addressContainer}>
100
- {evmWalletAddress && (
98
+ {evmWalletAddress ? (
101
99
  <>
102
100
  <Text style={styles.addressLabel}>EVM</Text>
103
- <TouchableOpacity onPress={() => copyWalletAddress(evmWalletAddress, "EVM")}>
101
+ <TouchableOpacity onPress={copyWalletAddress}>
104
102
  <Text style={styles.address}>{formatAddress(evmWalletAddress)}</Text>
105
103
  </TouchableOpacity>
106
104
  </>
107
- )}
108
- {solanaWalletAddress && (
109
- <>
110
- <Text style={styles.addressLabel}>Solana</Text>
111
- <TouchableOpacity onPress={() => copyWalletAddress(solanaWalletAddress, "Solana")}>
112
- <Text style={styles.address}>{formatAddress(solanaWalletAddress)}</Text>
113
- </TouchableOpacity>
114
- </>
115
- )}
116
- {!evmWalletAddress && !solanaWalletAddress && (
105
+ ) : (
117
106
  <Text style={styles.address}>Loading...</Text>
118
107
  )}
119
108
  </View>
@@ -0,0 +1,115 @@
1
+ import React from "react";
2
+ import { View, Text, TouchableOpacity, StyleSheet, Alert } from "react-native";
3
+ import * as Clipboard from "expo-clipboard";
4
+ import { useEvmAddress, useCurrentUser } from "@coinbase/cdp-hooks";
5
+ import { useTheme } from "../theme/ThemeContext";
6
+ import { UserIcon } from "../components/UserIcon";
7
+
8
+ interface WalletHeaderProps {
9
+ onSignOut: () => void;
10
+ }
11
+
12
+ export const WalletHeader: React.FC<WalletHeaderProps> = ({ onSignOut }) => {
13
+ const { colors } = useTheme();
14
+ const { evmAddress } = useEvmAddress();
15
+ const { currentUser } = useCurrentUser();
16
+
17
+ // Use EVM address if available, otherwise fall back to smart account
18
+ const evmWalletAddress = evmAddress || currentUser?.evmSmartAccounts?.[0];
19
+
20
+ const formatAddress = (address: string) => {
21
+ if (!address) return "";
22
+ return `${address.slice(0, 6)}...${address.slice(-4)}`;
23
+ };
24
+
25
+ const copyWalletAddress = async () => {
26
+ if (!evmWalletAddress) return;
27
+
28
+ try {
29
+ await Clipboard.setStringAsync(evmWalletAddress);
30
+ Alert.alert("Copied!", "EVM address copied to clipboard.");
31
+ } catch (error) {
32
+ Alert.alert("Error", "Failed to copy EVM address.");
33
+ }
34
+ };
35
+
36
+ const createStyles = () =>
37
+ StyleSheet.create({
38
+ container: {
39
+ backgroundColor: colors.cardBackground,
40
+ paddingHorizontal: 20,
41
+ paddingVertical: 16,
42
+ borderBottomWidth: 1,
43
+ borderBottomColor: colors.border,
44
+ flexDirection: "row",
45
+ alignItems: "center",
46
+ justifyContent: "space-between",
47
+ },
48
+ leftContent: {
49
+ flexDirection: "row",
50
+ alignItems: "center",
51
+ flex: 1,
52
+ },
53
+ avatar: {
54
+ width: 32,
55
+ height: 32,
56
+ borderRadius: 16,
57
+ backgroundColor: colors.textSecondary,
58
+ marginRight: 12,
59
+ justifyContent: "center",
60
+ alignItems: "center",
61
+ },
62
+ addressContainer: {
63
+ flex: 1,
64
+ },
65
+ address: {
66
+ fontSize: 14,
67
+ fontWeight: "500",
68
+ color: colors.text,
69
+ marginBottom: 2,
70
+ },
71
+ addressLabel: {
72
+ fontSize: 12,
73
+ color: colors.textSecondary,
74
+ marginBottom: 1,
75
+ },
76
+ signOutButton: {
77
+ backgroundColor: colors.accent,
78
+ paddingHorizontal: 16,
79
+ paddingVertical: 8,
80
+ borderRadius: 20,
81
+ },
82
+ signOutButtonText: {
83
+ color: "#ffffff",
84
+ fontSize: 14,
85
+ fontWeight: "600",
86
+ },
87
+ });
88
+
89
+ const styles = createStyles();
90
+
91
+ return (
92
+ <View style={styles.container}>
93
+ <View style={styles.leftContent}>
94
+ <View style={styles.avatar}>
95
+ <UserIcon size={18} color={colors.cardBackground} />
96
+ </View>
97
+ <View style={styles.addressContainer}>
98
+ {evmWalletAddress ? (
99
+ <>
100
+ <Text style={styles.addressLabel}>EVM</Text>
101
+ <TouchableOpacity onPress={copyWalletAddress}>
102
+ <Text style={styles.address}>{formatAddress(evmWalletAddress)}</Text>
103
+ </TouchableOpacity>
104
+ </>
105
+ ) : (
106
+ <Text style={styles.address}>Loading...</Text>
107
+ )}
108
+ </View>
109
+ </View>
110
+ <TouchableOpacity style={styles.signOutButton} onPress={onSignOut}>
111
+ <Text style={styles.signOutButtonText}>Sign out</Text>
112
+ </TouchableOpacity>
113
+ </View>
114
+ );
115
+ };
@@ -0,0 +1,111 @@
1
+ import React from "react";
2
+ import { View, Text, TouchableOpacity, StyleSheet, Alert } from "react-native";
3
+ import * as Clipboard from "expo-clipboard";
4
+ import { useSolanaAddress } from "@coinbase/cdp-hooks";
5
+ import { useTheme } from "../theme/ThemeContext";
6
+ import { UserIcon } from "../components/UserIcon";
7
+
8
+ interface WalletHeaderProps {
9
+ onSignOut: () => void;
10
+ }
11
+
12
+ export const WalletHeader: React.FC<WalletHeaderProps> = ({ onSignOut }) => {
13
+ const { colors } = useTheme();
14
+ const { solanaAddress } = useSolanaAddress();
15
+
16
+ const formatAddress = (address: string) => {
17
+ if (!address) return "";
18
+ return `${address.slice(0, 6)}...${address.slice(-4)}`;
19
+ };
20
+
21
+ const copyWalletAddress = async () => {
22
+ if (!solanaAddress) return;
23
+
24
+ try {
25
+ await Clipboard.setStringAsync(solanaAddress);
26
+ Alert.alert("Copied!", "Solana address copied to clipboard.");
27
+ } catch (error) {
28
+ Alert.alert("Error", "Failed to copy Solana address.");
29
+ }
30
+ };
31
+
32
+ const createStyles = () =>
33
+ StyleSheet.create({
34
+ container: {
35
+ backgroundColor: colors.cardBackground,
36
+ paddingHorizontal: 20,
37
+ paddingVertical: 16,
38
+ borderBottomWidth: 1,
39
+ borderBottomColor: colors.border,
40
+ flexDirection: "row",
41
+ alignItems: "center",
42
+ justifyContent: "space-between",
43
+ },
44
+ leftContent: {
45
+ flexDirection: "row",
46
+ alignItems: "center",
47
+ flex: 1,
48
+ },
49
+ avatar: {
50
+ width: 32,
51
+ height: 32,
52
+ borderRadius: 16,
53
+ backgroundColor: colors.textSecondary,
54
+ marginRight: 12,
55
+ justifyContent: "center",
56
+ alignItems: "center",
57
+ },
58
+ addressContainer: {
59
+ flex: 1,
60
+ },
61
+ address: {
62
+ fontSize: 14,
63
+ fontWeight: "500",
64
+ color: colors.text,
65
+ marginBottom: 2,
66
+ },
67
+ addressLabel: {
68
+ fontSize: 12,
69
+ color: colors.textSecondary,
70
+ marginBottom: 1,
71
+ },
72
+ signOutButton: {
73
+ backgroundColor: colors.accent,
74
+ paddingHorizontal: 16,
75
+ paddingVertical: 8,
76
+ borderRadius: 20,
77
+ },
78
+ signOutButtonText: {
79
+ color: "#ffffff",
80
+ fontSize: 14,
81
+ fontWeight: "600",
82
+ },
83
+ });
84
+
85
+ const styles = createStyles();
86
+
87
+ return (
88
+ <View style={styles.container}>
89
+ <View style={styles.leftContent}>
90
+ <View style={styles.avatar}>
91
+ <UserIcon size={18} color={colors.cardBackground} />
92
+ </View>
93
+ <View style={styles.addressContainer}>
94
+ {solanaAddress ? (
95
+ <>
96
+ <Text style={styles.addressLabel}>Solana</Text>
97
+ <TouchableOpacity onPress={copyWalletAddress}>
98
+ <Text style={styles.address}>{formatAddress(solanaAddress)}</Text>
99
+ </TouchableOpacity>
100
+ </>
101
+ ) : (
102
+ <Text style={styles.address}>Loading...</Text>
103
+ )}
104
+ </View>
105
+ </View>
106
+ <TouchableOpacity style={styles.signOutButton} onPress={onSignOut}>
107
+ <Text style={styles.signOutButtonText}>Sign out</Text>
108
+ </TouchableOpacity>
109
+ </View>
110
+ );
111
+ };
@@ -1,116 +0,0 @@
1
- "use client";
2
-
3
- import { useEvmAddress, useSolanaAddress, useIsSignedIn } from "@coinbase/cdp-hooks";
4
- import { Connection, clusterApiUrl, LAMPORTS_PER_SOL, PublicKey } from "@solana/web3.js";
5
- import { useCallback, useEffect, useMemo, useState, lazy, Suspense } from "react";
6
- import { createPublicClient, http, formatEther } from "viem";
7
- import { baseSepolia } from "viem/chains";
8
-
9
- import Header from "@/components/Header";
10
- import UserBalance from "@/components/UserBalance";
11
-
12
- const isSolana = process.env.NEXT_PUBLIC_CDP_CREATE_SOLANA_ACCOUNT === "true";
13
- const isSmartAccount = process.env.NEXT_PUBLIC_CDP_CREATE_ETHEREUM_ACCOUNT_TYPE === "smart";
14
-
15
- // Dynamically import components based on configuration
16
- const TransactionComponent = lazy(() => {
17
- if (isSolana) {
18
- return import("@/components/SolanaTransaction");
19
- } else if (isSmartAccount) {
20
- return import("@/components/SmartAccountTransaction");
21
- } else {
22
- return import("@/components/EOATransaction");
23
- }
24
- });
25
-
26
- /**
27
- * Create a viem client to access user's balance on the Base Sepolia network
28
- */
29
- const client = createPublicClient({
30
- chain: baseSepolia,
31
- transport: http(),
32
- });
33
-
34
- /**
35
- * Create a Solana connection to access user's balance on Solana Devnet
36
- */
37
- const solanaConnection = new Connection(clusterApiUrl("devnet"));
38
-
39
- /**
40
- * The Signed In screen
41
- */
42
- export default function SignedInScreen() {
43
- const { isSignedIn } = useIsSignedIn();
44
- const { evmAddress } = useEvmAddress();
45
- const { solanaAddress } = useSolanaAddress();
46
- const [balance, setBalance] = useState<bigint | undefined>(undefined);
47
-
48
- const address = isSolana ? solanaAddress : evmAddress;
49
-
50
- const formattedBalance = useMemo(() => {
51
- if (balance === undefined) return undefined;
52
- if (isSolana) {
53
- // Convert lamports to SOL
54
- return formatSol(Number(balance));
55
- } else {
56
- // Convert wei to ETH
57
- return formatEther(balance);
58
- }
59
- }, [balance]);
60
-
61
- const getBalance = useCallback(async () => {
62
- if (isSolana && solanaAddress) {
63
- // Get Solana balance in lamports
64
- const lamports = await solanaConnection.getBalance(new PublicKey(solanaAddress));
65
- setBalance(BigInt(lamports));
66
- } else if (!isSolana && evmAddress) {
67
- // Get EVM balance in wei
68
- const weiBalance = await client.getBalance({
69
- address: evmAddress,
70
- });
71
- setBalance(weiBalance);
72
- }
73
- }, [evmAddress, solanaAddress]);
74
-
75
- useEffect(() => {
76
- getBalance();
77
- const interval = setInterval(getBalance, 500);
78
- return () => clearInterval(interval);
79
- }, [getBalance]);
80
-
81
- return (
82
- <>
83
- <Header />
84
- <main className="main flex-col-container flex-grow">
85
- <div className="main-inner flex-col-container">
86
- <div className="card card--user-balance">
87
- <UserBalance
88
- balance={formattedBalance}
89
- faucetName={isSolana ? "Solana Faucet" : "Base Sepolia Faucet"}
90
- faucetUrl={`https://portal.cdp.coinbase.com/products/faucet${isSolana ? "?network=solana-devnet" : ""}`}
91
- />
92
- </div>
93
- <div className="card card--transaction">
94
- {isSignedIn && address && (
95
- <Suspense fallback={<div>Loading transaction component...</div>}>
96
- <TransactionComponent balance={formattedBalance} onSuccess={getBalance} />
97
- </Suspense>
98
- )}
99
- </div>
100
- </div>
101
- </main>
102
- </>
103
- );
104
- }
105
-
106
- /**
107
- * Format a Solana balance.
108
- *
109
- * @param lamports - The balance in lamports.
110
- * @returns The formatted balance.
111
- */
112
- function formatSol(lamports: number) {
113
- const maxDecimalPlaces = 9;
114
- const roundedStr = (lamports / LAMPORTS_PER_SOL).toFixed(maxDecimalPlaces);
115
- return roundedStr.replace(/0+$/, "").replace(/\.$/, "");
116
- }
@@ -1,239 +0,0 @@
1
- "use client";
2
-
3
- import { useEvmAddress, useIsSignedIn, useSolanaAddress } from "@coinbase/cdp-hooks";
4
- import { Connection, clusterApiUrl, LAMPORTS_PER_SOL, PublicKey } from "@solana/web3.js";
5
- import { useCallback, useEffect, useMemo, useState, lazy, Suspense } from "react";
6
- import {
7
- createPublicClient,
8
- http,
9
- formatEther,
10
- type PublicClient,
11
- type Transport,
12
- type Address,
13
- } from "viem";
14
- import { baseSepolia, base } from "viem/chains";
15
-
16
- import FundWallet from "@/components/FundWallet";
17
- import Header from "@/components/Header";
18
- import UserBalance from "@/components/UserBalance";
19
-
20
- // Dynamically determine component path for EVM transactions
21
- const getEVMComponentPath = () => {
22
- const isSmartAccount = process.env.NEXT_PUBLIC_CDP_CREATE_ETHEREUM_ACCOUNT_TYPE === "smart";
23
-
24
- if (isSmartAccount) {
25
- return "@/components/SmartAccountTransaction";
26
- } else {
27
- return "@/components/EOATransaction";
28
- }
29
- };
30
-
31
- const EVMTransactionComponent = lazy(() => import(/* @vite-ignore */ getEVMComponentPath()));
32
- const SolanaTransactionComponent = lazy(() => import("@/components/SolanaTransaction"));
33
-
34
- /**
35
- * Create a viem client to access user's balance on the Base network
36
- */
37
- const client = createPublicClient({
38
- chain: base,
39
- transport: http(),
40
- });
41
-
42
- /**
43
- * Create a viem client to access user's balance on the Base Sepolia network
44
- */
45
- const sepoliaClient = createPublicClient({
46
- chain: baseSepolia,
47
- transport: http(),
48
- });
49
-
50
- /**
51
- * Create a Solana connection to access user's balance on Solana Mainnet
52
- */
53
- const solanaMainnetConnection = new Connection(clusterApiUrl("mainnet-beta"));
54
-
55
- /**
56
- * Create a Solana connection to access user's balance on Solana Devnet
57
- */
58
- const solanaDevnetConnection = new Connection(clusterApiUrl("devnet"));
59
-
60
- const useEvmBalance = (
61
- address: Address | null,
62
- client: PublicClient<Transport, typeof base | typeof baseSepolia, undefined, undefined>,
63
- poll = false,
64
- ) => {
65
- const [balance, setBalance] = useState<bigint | undefined>(undefined);
66
-
67
- const formattedBalance = useMemo(() => {
68
- if (balance === undefined) return undefined;
69
- return formatEther(balance);
70
- }, [balance]);
71
-
72
- const getBalance = useCallback(async () => {
73
- if (!address) return;
74
- const balance = await client.getBalance({ address });
75
- setBalance(balance);
76
- }, [address, client]);
77
-
78
- useEffect(() => {
79
- if (!poll) {
80
- return;
81
- }
82
- getBalance();
83
- const interval = setInterval(getBalance, 500);
84
- return () => clearInterval(interval);
85
- }, [getBalance, poll]);
86
-
87
- return { balance, formattedBalance, getBalance };
88
- };
89
-
90
- const useSolanaBalance = (address: string | null, connection: Connection, poll = false) => {
91
- const [balance, setBalance] = useState<bigint | undefined>(undefined);
92
-
93
- const formattedBalance = useMemo(() => {
94
- if (balance === undefined) return undefined;
95
- // Convert lamports to SOL
96
- return formatSol(Number(balance));
97
- }, [balance]);
98
-
99
- const getBalance = useCallback(async () => {
100
- if (!address) return;
101
- try {
102
- const lamports = await connection.getBalance(new PublicKey(address));
103
- setBalance(BigInt(lamports));
104
- } catch (error) {
105
- console.error("Error fetching Solana balance:", error);
106
- setBalance(BigInt(0));
107
- }
108
- }, [address, connection]);
109
-
110
- useEffect(() => {
111
- if (!poll) {
112
- return;
113
- }
114
- getBalance();
115
- const interval = setInterval(getBalance, 500);
116
- return () => clearInterval(interval);
117
- }, [getBalance, poll]);
118
-
119
- return { balance, formattedBalance, getBalance };
120
- };
121
-
122
- /**
123
- * Format a Solana balance.
124
- *
125
- * @param lamports - The balance in lamports.
126
- * @returns The formatted balance.
127
- */
128
- function formatSol(lamports: number) {
129
- const maxDecimalPlaces = 9;
130
- const roundedStr = (lamports / LAMPORTS_PER_SOL).toFixed(maxDecimalPlaces);
131
- return roundedStr.replace(/0+$/, "").replace(/\.$/, "");
132
- }
133
-
134
- /**
135
- * The Signed In screen with onramp support for both EVM and Solana
136
- */
137
- export default function SignedInScreen() {
138
- const { isSignedIn } = useIsSignedIn();
139
- const { evmAddress } = useEvmAddress();
140
- const { solanaAddress } = useSolanaAddress();
141
-
142
- const { formattedBalance, getBalance } = useEvmBalance(evmAddress, client, true);
143
- const { formattedBalance: formattedBalanceSepolia, getBalance: getBalanceSepolia } =
144
- useEvmBalance(evmAddress, sepoliaClient, true);
145
-
146
- const { formattedBalance: formattedBalanceSolana, getBalance: getBalanceSolana } =
147
- useSolanaBalance(solanaAddress, solanaMainnetConnection);
148
- const { formattedBalance: formattedBalanceSolanaDevnet, getBalance: getBalanceSolanaDevnet } =
149
- useSolanaBalance(solanaAddress, solanaDevnetConnection, true);
150
-
151
- return (
152
- <>
153
- <Header />
154
- <main className="main flex-col-container flex-grow">
155
- {evmAddress && (
156
- <>
157
- <p className="page-heading">Fund your EVM wallet on Base</p>
158
- <div className="main-inner flex-col-container">
159
- <div className="card card--user-balance">
160
- <UserBalance balance={formattedBalance} />
161
- </div>
162
- <div className="card card--transaction">
163
- {isSignedIn && (
164
- <FundWallet
165
- onSuccess={getBalance}
166
- network="base"
167
- cryptoCurrency="eth"
168
- destinationAddress={evmAddress}
169
- />
170
- )}
171
- </div>
172
- </div>
173
- <hr className="page-divider" />
174
- <p className="page-heading">Send an EVM transaction on Base Sepolia</p>
175
- <div className="main-inner flex-col-container">
176
- <div className="card card--user-balance">
177
- <UserBalance
178
- balance={formattedBalanceSepolia}
179
- faucetName="Base Sepolia Faucet"
180
- faucetUrl="https://portal.cdp.coinbase.com/products/faucet"
181
- />
182
- </div>
183
- <div className="card card--transaction">
184
- {isSignedIn && (
185
- <Suspense fallback={<div>Loading transaction component...</div>}>
186
- <EVMTransactionComponent
187
- balance={formattedBalanceSepolia}
188
- onSuccess={getBalanceSepolia}
189
- />
190
- </Suspense>
191
- )}
192
- </div>
193
- </div>
194
- </>
195
- )}
196
-
197
- {solanaAddress && (
198
- <>
199
- {evmAddress && <hr className="page-divider" />}
200
- <p className="page-heading">Fund your Solana wallet on Mainnet</p>
201
- <div className="main-inner flex-col-container">
202
- <div className="card card--user-balance">
203
- <UserBalance balance={formattedBalanceSolana} />
204
- </div>
205
- <div className="card card--transaction">
206
- {isSignedIn && (
207
- <FundWallet
208
- onSuccess={getBalanceSolana}
209
- network="solana"
210
- cryptoCurrency="sol"
211
- destinationAddress={solanaAddress}
212
- />
213
- )}
214
- </div>
215
- </div>
216
- <hr className="page-divider" />
217
- <p className="page-heading">Send a Solana transaction on Devnet</p>
218
- <div className="main-inner flex-col-container">
219
- <div className="card card--user-balance">
220
- <UserBalance
221
- balance={formattedBalanceSolanaDevnet}
222
- faucetName="Solana Devnet Faucet"
223
- faucetUrl="https://portal.cdp.coinbase.com/products/faucet?network=solana-devnet"
224
- />
225
- </div>
226
- <div className="card card--transaction">
227
- {isSignedIn && (
228
- <Suspense fallback={<div>Loading transaction component...</div>}>
229
- <SolanaTransactionComponent onSuccess={getBalanceSolanaDevnet} />
230
- </Suspense>
231
- )}
232
- </div>
233
- </div>
234
- </>
235
- )}
236
- </main>
237
- </>
238
- );
239
- }