@coinbase/create-cdp-app 0.0.38 → 0.0.39

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.
@@ -1 +1,101 @@
1
- export { default } from "./EOATransaction";
1
+ import React, { useState } from "react";
2
+ import { View, Text, TouchableOpacity, StyleSheet } from "react-native";
3
+ import { useEvmAddress, useSolanaAddress } from "@coinbase/cdp-hooks";
4
+ import { useTheme } from "./theme/ThemeContext";
5
+ import EOATransaction from "./EOATransaction";
6
+ import SolanaTransaction from "./SolanaTransaction";
7
+
8
+ interface Props {
9
+ onSuccess?: () => void;
10
+ }
11
+
12
+ function Transaction(props: Props) {
13
+ const { onSuccess } = props;
14
+ const { colors } = useTheme();
15
+ const { evmAddress } = useEvmAddress();
16
+ const { solanaAddress } = useSolanaAddress();
17
+ const [activeTab, setActiveTab] = useState<"evm" | "solana">("evm");
18
+
19
+ // Show tabs only if both EVM and Solana addresses are available
20
+ const showTabs = !!evmAddress && !!solanaAddress;
21
+
22
+ const createStyles = () =>
23
+ StyleSheet.create({
24
+ container: {
25
+ flex: 1,
26
+ width: "100%",
27
+ },
28
+ tabContainer: {
29
+ flexDirection: "row",
30
+ marginBottom: 16,
31
+ backgroundColor: colors.cardBackground,
32
+ borderRadius: 8,
33
+ padding: 4,
34
+ borderWidth: 1,
35
+ borderColor: colors.border,
36
+ },
37
+ tab: {
38
+ flex: 1,
39
+ paddingVertical: 12,
40
+ alignItems: "center",
41
+ borderRadius: 6,
42
+ },
43
+ activeTab: {
44
+ backgroundColor: colors.accent,
45
+ },
46
+ tabText: {
47
+ fontSize: 16,
48
+ fontWeight: "600",
49
+ color: colors.textSecondary,
50
+ },
51
+ activeTabText: {
52
+ color: "#ffffff",
53
+ },
54
+ });
55
+
56
+ const styles = createStyles();
57
+
58
+ // If only one address type is available, show that component directly
59
+ if (!showTabs) {
60
+ if (evmAddress) {
61
+ return <EOATransaction onSuccess={onSuccess} />;
62
+ }
63
+ if (solanaAddress) {
64
+ return <SolanaTransaction onSuccess={onSuccess} />;
65
+ }
66
+ return null; // Loading state handled by parent
67
+ }
68
+
69
+ return (
70
+ <View style={styles.container}>
71
+ {/* Tab selector */}
72
+ <View style={styles.tabContainer}>
73
+ <TouchableOpacity
74
+ style={[styles.tab, activeTab === "evm" && styles.activeTab]}
75
+ onPress={() => setActiveTab("evm")}
76
+ >
77
+ <Text style={[styles.tabText, activeTab === "evm" && styles.activeTabText]}>
78
+ Ethereum
79
+ </Text>
80
+ </TouchableOpacity>
81
+ <TouchableOpacity
82
+ style={[styles.tab, activeTab === "solana" && styles.activeTab]}
83
+ onPress={() => setActiveTab("solana")}
84
+ >
85
+ <Text style={[styles.tabText, activeTab === "solana" && styles.activeTabText]}>
86
+ Solana
87
+ </Text>
88
+ </TouchableOpacity>
89
+ </View>
90
+
91
+ {/* Content */}
92
+ {activeTab === "evm" ? (
93
+ <EOATransaction onSuccess={onSuccess} />
94
+ ) : (
95
+ <SolanaTransaction onSuccess={onSuccess} />
96
+ )}
97
+ </View>
98
+ );
99
+ }
100
+
101
+ export default Transaction;
@@ -1,7 +1,7 @@
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 } from "@coinbase/cdp-hooks";
4
+ import { useEvmAddress, useCurrentUser, useSolanaAddress } from "@coinbase/cdp-hooks";
5
5
  import { useTheme } from "../theme/ThemeContext";
6
6
  import { UserIcon } from "./UserIcon";
7
7
 
@@ -12,24 +12,26 @@ 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();
15
16
  const { currentUser } = useCurrentUser();
16
17
 
17
18
  // Use EVM address if available, otherwise fall back to smart account
18
- const walletAddress = evmAddress || currentUser?.evmSmartAccounts?.[0];
19
+ const evmWalletAddress = evmAddress || currentUser?.evmSmartAccounts?.[0];
20
+ const solanaWalletAddress = solanaAddress;
19
21
 
20
22
  const formatAddress = (address: string) => {
21
23
  if (!address) return "";
22
24
  return `${address.slice(0, 6)}...${address.slice(-4)}`;
23
25
  };
24
26
 
25
- const copyWalletAddress = async () => {
26
- if (!walletAddress) return;
27
+ const copyWalletAddress = async (address: string, type: "EVM" | "Solana") => {
28
+ if (!address) return;
27
29
 
28
30
  try {
29
- await Clipboard.setStringAsync(walletAddress);
30
- Alert.alert("Copied!", "Wallet address copied to clipboard.");
31
+ await Clipboard.setStringAsync(address);
32
+ Alert.alert("Copied!", `${type} address copied to clipboard.`);
31
33
  } catch (error) {
32
- Alert.alert("Error", "Failed to copy wallet address.");
34
+ Alert.alert("Error", `Failed to copy ${type} address.`);
33
35
  }
34
36
  };
35
37
 
@@ -63,9 +65,15 @@ export const WalletHeader: React.FC<WalletHeaderProps> = ({ onSignOut }) => {
63
65
  flex: 1,
64
66
  },
65
67
  address: {
66
- fontSize: 16,
68
+ fontSize: 14,
67
69
  fontWeight: "500",
68
70
  color: colors.text,
71
+ marginBottom: 2,
72
+ },
73
+ addressLabel: {
74
+ fontSize: 12,
75
+ color: colors.textSecondary,
76
+ marginBottom: 1,
69
77
  },
70
78
  signOutButton: {
71
79
  backgroundColor: colors.accent,
@@ -84,16 +92,32 @@ export const WalletHeader: React.FC<WalletHeaderProps> = ({ onSignOut }) => {
84
92
 
85
93
  return (
86
94
  <View style={styles.container}>
87
- <TouchableOpacity style={styles.leftContent} onPress={copyWalletAddress}>
95
+ <View style={styles.leftContent}>
88
96
  <View style={styles.avatar}>
89
97
  <UserIcon size={18} color={colors.cardBackground} />
90
98
  </View>
91
99
  <View style={styles.addressContainer}>
92
- <Text style={styles.address}>
93
- {walletAddress ? formatAddress(walletAddress) : "Loading..."}
94
- </Text>
100
+ {evmWalletAddress && (
101
+ <>
102
+ <Text style={styles.addressLabel}>EVM</Text>
103
+ <TouchableOpacity onPress={() => copyWalletAddress(evmWalletAddress, "EVM")}>
104
+ <Text style={styles.address}>{formatAddress(evmWalletAddress)}</Text>
105
+ </TouchableOpacity>
106
+ </>
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 && (
117
+ <Text style={styles.address}>Loading...</Text>
118
+ )}
95
119
  </View>
96
- </TouchableOpacity>
120
+ </View>
97
121
  <TouchableOpacity style={styles.signOutButton} onPress={onSignOut}>
98
122
  <Text style={styles.signOutButtonText}>Sign out</Text>
99
123
  </TouchableOpacity>
@@ -1,5 +1,6 @@
1
1
  # CDP Configuration for the main app
2
2
  EXPO_PUBLIC_CDP_PROJECT_ID=your-project-id-here
3
3
  EXPO_PUBLIC_CDP_CREATE_ETHEREUM_ACCOUNT_TYPE=smart
4
+ EXPO_PUBLIC_CDP_CREATE_SOLANA_ACCOUNT=false
4
5
  EXPO_PUBLIC_CDP_BASE_PATH=https://api.cdp.coinbase.com/platform
5
6
  EXPO_PUBLIC_USE_MOCK=false
@@ -2,12 +2,19 @@ import structuredClone from "@ungap/structured-clone";
2
2
  import { registerRootComponent } from "expo";
3
3
  import { install } from "react-native-quick-crypto";
4
4
  import "react-native-get-random-values";
5
+ import { Buffer } from "buffer";
5
6
 
6
7
  if (!("structuredClone" in globalThis)) {
7
8
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
8
9
  globalThis.structuredClone = structuredClone as any;
9
10
  }
10
11
 
12
+ // Setup Buffer global for Solana Web3.js compatibility
13
+ if (!("Buffer" in globalThis)) {
14
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
15
+ globalThis.Buffer = Buffer as any;
16
+ }
17
+
11
18
  install();
12
19
 
13
20
  import App from "./App";
@@ -0,0 +1,19 @@
1
+ const { getDefaultConfig } = require("expo/metro-config");
2
+
3
+ const config = getDefaultConfig(__dirname);
4
+
5
+ config.resolver.resolveRequest = (context, moduleName, platform) => {
6
+ if (moduleName.includes("zustand")) {
7
+ const result = require.resolve(moduleName);
8
+ return context.resolveRequest(context, result, platform);
9
+ }
10
+
11
+ if (moduleName.includes("rpc-websockets")) {
12
+ const result = require.resolve(moduleName);
13
+ return context.resolveRequest(context, result, platform);
14
+ }
15
+
16
+ return context.resolveRequest(context, moduleName, platform);
17
+ };
18
+
19
+ module.exports = config;
@@ -13,7 +13,9 @@
13
13
  "dependencies": {
14
14
  "@coinbase/cdp-hooks": "latest",
15
15
  "@react-native-async-storage/async-storage": "^2.2.0",
16
+ "@solana/web3.js": "^1.98.4",
16
17
  "@ungap/structured-clone": "^1.3.0",
18
+ "buffer": "^6.0.3",
17
19
  "expo": "~53.0.22",
18
20
  "expo-asset": "~11.1.7",
19
21
  "expo-clipboard": "~7.1.5",
@@ -24,6 +26,7 @@
24
26
  "react-native": "0.79.6",
25
27
  "react-native-get-random-values": "^1.11.0",
26
28
  "react-native-quick-crypto": "^0.7.17",
29
+ "readable-stream": "^4.6.0",
27
30
  "viem": "^2.21.45"
28
31
  },
29
32
  "devDependencies": {