@getpara/create-para-app 0.5.0 → 2.7.0

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 (142) hide show
  1. package/README.md +71 -0
  2. package/dist/cli.d.ts +2 -0
  3. package/dist/cli.d.ts.map +1 -0
  4. package/dist/cli.js +306 -0
  5. package/dist/cli.js.map +1 -0
  6. package/dist/index.d.ts +3 -0
  7. package/dist/index.d.ts.map +1 -0
  8. package/dist/index.js +7 -48
  9. package/dist/index.js.map +1 -0
  10. package/dist/package-builder.d.ts +13 -0
  11. package/dist/package-builder.d.ts.map +1 -0
  12. package/dist/package-builder.js +89 -0
  13. package/dist/package-builder.js.map +1 -0
  14. package/dist/post-scaffold.d.ts +3 -0
  15. package/dist/post-scaffold.d.ts.map +1 -0
  16. package/dist/post-scaffold.js +64 -0
  17. package/dist/post-scaffold.js.map +1 -0
  18. package/dist/prompt-orchestrator.d.ts +3 -0
  19. package/dist/prompt-orchestrator.d.ts.map +1 -0
  20. package/dist/prompt-orchestrator.js +198 -0
  21. package/dist/prompt-orchestrator.js.map +1 -0
  22. package/dist/scaffolder.d.ts +3 -0
  23. package/dist/scaffolder.d.ts.map +1 -0
  24. package/dist/scaffolder.js +82 -0
  25. package/dist/scaffolder.js.map +1 -0
  26. package/dist/template-registry.d.ts +12 -0
  27. package/dist/template-registry.d.ts.map +1 -0
  28. package/dist/template-registry.js +29 -0
  29. package/dist/template-registry.js.map +1 -0
  30. package/dist/template-renderer.d.ts +11 -0
  31. package/dist/template-renderer.d.ts.map +1 -0
  32. package/dist/template-renderer.js +133 -0
  33. package/dist/template-renderer.js.map +1 -0
  34. package/dist/template-strategies/expo-template.d.ts +18 -0
  35. package/dist/template-strategies/expo-template.d.ts.map +1 -0
  36. package/dist/template-strategies/expo-template.js +173 -0
  37. package/dist/template-strategies/expo-template.js.map +1 -0
  38. package/dist/template-strategies/index.d.ts +4 -0
  39. package/dist/template-strategies/index.d.ts.map +1 -0
  40. package/dist/template-strategies/index.js +3 -0
  41. package/dist/template-strategies/index.js.map +1 -0
  42. package/dist/template-strategies/nextjs-template.d.ts +12 -0
  43. package/dist/template-strategies/nextjs-template.d.ts.map +1 -0
  44. package/dist/template-strategies/nextjs-template.js +123 -0
  45. package/dist/template-strategies/nextjs-template.js.map +1 -0
  46. package/dist/types.d.ts +67 -0
  47. package/dist/types.d.ts.map +1 -0
  48. package/dist/types.js +16 -0
  49. package/dist/types.js.map +1 -0
  50. package/dist/utils/fs.d.ts +11 -0
  51. package/dist/utils/fs.d.ts.map +1 -0
  52. package/dist/utils/fs.js +38 -0
  53. package/dist/utils/fs.js.map +1 -0
  54. package/dist/utils/logger.d.ts +10 -0
  55. package/dist/utils/logger.d.ts.map +1 -0
  56. package/dist/utils/logger.js +25 -19
  57. package/dist/utils/logger.js.map +1 -0
  58. package/package.json +35 -30
  59. package/templates/expo/_env.example +3 -0
  60. package/templates/expo/_gitignore +48 -0
  61. package/templates/expo/_yarnrc.yml +1 -0
  62. package/templates/expo/app/(auth)/_layout.tsx +12 -0
  63. package/templates/expo/app/(auth)/index.tsx.template +86 -0
  64. package/templates/expo/app/(tabs)/_layout.tsx +16 -0
  65. package/templates/expo/app/(tabs)/index.tsx +112 -0
  66. package/templates/expo/app/(tabs)/send.tsx +111 -0
  67. package/templates/expo/app/_layout.tsx +17 -0
  68. package/templates/expo/app/index.tsx +22 -0
  69. package/templates/expo/app.json.template +32 -0
  70. package/templates/expo/assets/adaptive-icon.png +0 -0
  71. package/templates/expo/assets/favicon.png +0 -0
  72. package/templates/expo/assets/icon.png +0 -0
  73. package/templates/expo/assets/splash.png +0 -0
  74. package/templates/expo/babel.config.cjs +12 -0
  75. package/templates/expo/components/features/AuthForm.tsx.template +138 -0
  76. package/templates/expo/components/features/OAuthButtons.tsx.template +27 -0
  77. package/templates/expo/components/features/index.ts.template +4 -0
  78. package/templates/expo/components/ui/Button.tsx +58 -0
  79. package/templates/expo/components/ui/Card.tsx +11 -0
  80. package/templates/expo/components/ui/Divider.tsx +19 -0
  81. package/templates/expo/components/ui/Input.tsx +23 -0
  82. package/templates/expo/components/ui/WalletCard.tsx +44 -0
  83. package/templates/expo/components/ui/index.ts +5 -0
  84. package/templates/expo/eslint.config.cjs +15 -0
  85. package/templates/expo/global.css +3 -0
  86. package/templates/expo/hooks/useOneClickLogin.ts.template +161 -0
  87. package/templates/expo/hooks/useViemClient.ts +118 -0
  88. package/templates/expo/hooks/useWallets.ts +52 -0
  89. package/templates/expo/index.js +2 -0
  90. package/templates/expo/lib/auth.ts +54 -0
  91. package/templates/expo/lib/constants.ts.template +2 -0
  92. package/templates/expo/lib/para.ts +13 -0
  93. package/templates/expo/metro.config.cjs +14 -0
  94. package/templates/expo/nativewind-env.d.ts +2 -0
  95. package/templates/expo/prettier.config.cjs +10 -0
  96. package/templates/expo/providers/ParaProvider.tsx +140 -0
  97. package/templates/expo/tailwind.config.cjs +23 -0
  98. package/templates/expo/tsconfig.json +11 -0
  99. package/templates/expo/types/index.ts +28 -0
  100. package/templates/nextjs/README.md +69 -0
  101. package/templates/nextjs/_env.example +8 -0
  102. package/templates/nextjs/_gitignore +36 -0
  103. package/templates/nextjs/_yarnrc.yml +1 -0
  104. package/templates/nextjs/next.config.ts +5 -0
  105. package/templates/nextjs/postcss.config.mjs +7 -0
  106. package/templates/nextjs/public/para.svg +3 -0
  107. package/templates/nextjs/src/app/layout.tsx +30 -0
  108. package/templates/nextjs/src/app/page.tsx +40 -0
  109. package/templates/nextjs/src/components/ParaProvider.tsx +116 -0
  110. package/templates/nextjs/src/components/layout/Header.tsx +44 -0
  111. package/templates/nextjs/src/components/ui/ConnectCard.tsx +24 -0
  112. package/templates/nextjs/src/components/ui/SignMessage.tsx +53 -0
  113. package/templates/nextjs/src/components/ui/WalletInfo.tsx +22 -0
  114. package/templates/nextjs/src/hooks/useSignHelloWorld.ts +23 -0
  115. package/templates/nextjs/src/styles/globals.css +1 -0
  116. package/templates/nextjs/tsconfig.json +27 -0
  117. package/dist/bin/index.js +0 -2
  118. package/dist/commands/scaffold.js +0 -137
  119. package/dist/integrations/codeGenerators.js +0 -637
  120. package/dist/integrations/packageJsonHelpers.js +0 -77
  121. package/dist/integrations/sdkSetup.js +0 -24
  122. package/dist/integrations/sdkSetupNextjs.js +0 -111
  123. package/dist/integrations/sdkSetupVite.js +0 -82
  124. package/dist/package.json +0 -44
  125. package/dist/prompts/interactive.js +0 -98
  126. package/dist/src/commands/scaffold.js +0 -157
  127. package/dist/src/index.js +0 -48
  128. package/dist/src/integrations/codeGenerators.js +0 -637
  129. package/dist/src/integrations/packageJsonHelpers.js +0 -77
  130. package/dist/src/integrations/sdkSetup.js +0 -24
  131. package/dist/src/integrations/sdkSetupNextjs.js +0 -111
  132. package/dist/src/integrations/sdkSetupVite.js +0 -82
  133. package/dist/src/prompts/interactive.js +0 -98
  134. package/dist/src/templates/nextjs.js +0 -68
  135. package/dist/src/templates/vite-react.js +0 -52
  136. package/dist/src/utils/exec.js +0 -16
  137. package/dist/src/utils/formatting.js +0 -31
  138. package/dist/src/utils/logger.js +0 -19
  139. package/dist/templates/nextjs.js +0 -61
  140. package/dist/templates/vite-react.js +0 -43
  141. package/dist/utils/exec.js +0 -16
  142. package/dist/utils/formatting.js +0 -31
package/package.json CHANGED
@@ -1,44 +1,49 @@
1
1
  {
2
2
  "name": "@getpara/create-para-app",
3
- "version": "0.5.0",
4
- "main": "dist/src/index.js",
3
+ "version": "2.7.0",
4
+ "description": "Create Para SDK applications with one command",
5
5
  "type": "module",
6
6
  "bin": {
7
- "create-para-app": "dist/bin/index.js"
7
+ "create-para-app": "./dist/index.js"
8
8
  },
9
+ "main": "./dist/index.js",
9
10
  "files": [
10
- "dist"
11
+ "dist",
12
+ "templates"
11
13
  ],
12
- "engines": {
13
- "node": ">=14.0.0"
14
- },
15
- "license": "MIT",
16
14
  "scripts": {
17
15
  "build": "tsc",
18
- "start": "node --no-warnings dist/bin/index.js",
19
- "dev": "node --no-warnings --loader ts-node/esm src/index.ts"
16
+ "dev": "tsc --watch",
17
+ "typecheck": "tsc --noEmit",
18
+ "prepublishOnly": "yarn build"
20
19
  },
21
20
  "dependencies": {
22
- "chalk": "5.4.1",
23
- "commander": "13.1.0",
24
- "execa": "9.5.2",
25
- "fs-extra": "11.3.0",
26
- "inquirer": "12.4.1",
27
- "ora": "8.2.0",
28
- "prettier": "3.5.0",
29
- "update-notifier": "7.3.1"
21
+ "@clack/prompts": "^0.9.1",
22
+ "picocolors": "^1.1.1"
30
23
  },
31
24
  "devDependencies": {
32
- "@types/chalk": "^2.2.4",
33
- "@types/commander": "^2.12.5",
34
- "@types/inquirer": "^9.0.7",
35
- "@types/jest": "^29.5.14",
36
- "@types/node": "^22.13.1",
37
- "@types/update-notifier": "6.0.8",
38
- "@types/fs-extra": "11.0.4",
39
- "jest": "^29.7.0",
40
- "ts-jest": "^29.2.5",
41
- "ts-node": "^10.9.2",
42
- "typescript": "^5.7.3"
43
- }
25
+ "@types/node": "^22.13.10",
26
+ "typescript": "^5.8.3"
27
+ },
28
+ "engines": {
29
+ "node": ">=18.0.0"
30
+ },
31
+ "keywords": [
32
+ "para",
33
+ "wallet",
34
+ "web3",
35
+ "scaffolding",
36
+ "cli",
37
+ "create-app",
38
+ "nextjs"
39
+ ],
40
+ "repository": {
41
+ "type": "git",
42
+ "url": "https://github.com/para-space/web-sdk.git",
43
+ "directory": "packages/create-para-app"
44
+ },
45
+ "publishConfig": {
46
+ "access": "public"
47
+ },
48
+ "license": "MIT"
44
49
  }
@@ -0,0 +1,3 @@
1
+ # Para API Key
2
+ # Get your API key at https://developer.getpara.com
3
+ EXPO_PUBLIC_PARA_API_KEY=your_api_key_here
@@ -0,0 +1,48 @@
1
+ # Dependencies
2
+ node_modules/
3
+
4
+ # Expo
5
+ .expo/
6
+ dist/
7
+ web-build/
8
+
9
+ # Native builds
10
+ *.orig.*
11
+ *.jks
12
+ *.p8
13
+ *.p12
14
+ *.key
15
+ *.mobileprovision
16
+
17
+ # iOS
18
+ ios/
19
+ *.xcworkspace
20
+ *.xcodeproj
21
+
22
+ # Android
23
+ android/
24
+ *.apk
25
+ *.aab
26
+
27
+ # Environment
28
+ .env
29
+ .env.local
30
+ .env.*.local
31
+
32
+ # IDE
33
+ .idea/
34
+ .vscode/
35
+ *.swp
36
+ *.swo
37
+
38
+ # OS
39
+ .DS_Store
40
+ Thumbs.db
41
+
42
+ # Logs
43
+ npm-debug.log*
44
+ yarn-debug.log*
45
+ yarn-error.log*
46
+
47
+ # TypeScript
48
+ *.tsbuildinfo
@@ -0,0 +1 @@
1
+ nodeLinker: node-modules
@@ -0,0 +1,12 @@
1
+ import { Stack } from 'expo-router';
2
+
3
+ export default function AuthLayout() {
4
+ return (
5
+ <Stack
6
+ screenOptions={{
7
+ headerShown: false,
8
+ contentStyle: { backgroundColor: '#fff' },
9
+ }}
10
+ />
11
+ );
12
+ }
@@ -0,0 +1,86 @@
1
+ import { View, Text, ScrollView, KeyboardAvoidingView, Platform } from 'react-native';
2
+ import { SafeAreaView } from 'react-native-safe-area-context';
3
+ import { Ionicons } from '@expo/vector-icons';
4
+ import { useRouter } from 'expo-router';
5
+
6
+ // @if:hasOAuth
7
+ import { Divider } from '@/components/ui';
8
+ // @endif
9
+ import { AuthForm } from '@/components/features';
10
+ // @if:hasOAuth
11
+ import { OAuthButtons } from '@/components/features';
12
+ // @endif
13
+ import { useOneClickLogin } from '@/hooks/useOneClickLogin';
14
+ import { usePara } from '@/providers/ParaProvider';
15
+
16
+ export default function LoginScreen() {
17
+ const router = useRouter();
18
+ const { setAuthenticated } = usePara();
19
+
20
+ const handleSuccess = () => {
21
+ setAuthenticated(true);
22
+ router.replace('/(tabs)');
23
+ };
24
+
25
+ const {
26
+ // @if:hasEmail
27
+ loginWithEmail,
28
+ // @endif
29
+ // @if:hasPhone
30
+ loginWithPhone,
31
+ // @endif
32
+ // @if:hasOAuth
33
+ loginWithGoogle,
34
+ // @endif
35
+ status,
36
+ error,
37
+ } = useOneClickLogin(handleSuccess);
38
+
39
+ const isLoading = status === 'loading' || status === 'verifying' || status === 'completing';
40
+
41
+ const handleAuthSubmit = async (value: string, method: 'email' | 'phone') => {
42
+ // @if:hasEmail
43
+ if (method === 'email') {
44
+ await loginWithEmail(value);
45
+ }
46
+ // @endif
47
+ // @if:hasPhone
48
+ if (method === 'phone') {
49
+ await loginWithPhone(value);
50
+ }
51
+ // @endif
52
+ };
53
+
54
+ return (
55
+ <SafeAreaView className="flex-1 bg-white">
56
+ <KeyboardAvoidingView
57
+ behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
58
+ className="flex-1">
59
+ <ScrollView
60
+ contentContainerStyle={{ flexGrow: 1 }}
61
+ keyboardShouldPersistTaps="handled"
62
+ className="flex-1">
63
+ <View className="flex-1 px-6 pb-8 pt-12">
64
+ <View className="mb-10 items-center">
65
+ <View className="mb-4 h-16 w-16 items-center justify-center rounded-2xl bg-gray-900">
66
+ <Ionicons name="wallet-outline" size={32} color="#fff" />
67
+ </View>
68
+ <Text className="mb-2 text-2xl font-bold text-gray-900">{{expoAppName}}</Text>
69
+ <Text className="text-center text-gray-500">Sign in to access your wallet</Text>
70
+ </View>
71
+ <AuthForm onSubmit={handleAuthSubmit} loading={isLoading} error={error} />
72
+ // @if:hasOAuth
73
+ <Divider text="or continue with" />
74
+ <OAuthButtons onGooglePress={loginWithGoogle} disabled={isLoading} />
75
+ // @endif
76
+ </View>
77
+ <View className="px-6 pb-4">
78
+ <Text className="text-center text-xs text-gray-400">
79
+ By continuing, you agree to our Terms of Service and Privacy Policy
80
+ </Text>
81
+ </View>
82
+ </ScrollView>
83
+ </KeyboardAvoidingView>
84
+ </SafeAreaView>
85
+ );
86
+ }
@@ -0,0 +1,16 @@
1
+ import { Stack } from 'expo-router';
2
+
3
+ export default function TabsLayout() {
4
+ return (
5
+ <Stack>
6
+ <Stack.Screen name="index" options={{ headerShown: false }} />
7
+ <Stack.Screen
8
+ name="send"
9
+ options={{
10
+ title: 'Send',
11
+ headerBackTitle: 'Back',
12
+ }}
13
+ />
14
+ </Stack>
15
+ );
16
+ }
@@ -0,0 +1,112 @@
1
+ import { View, Text, ScrollView, RefreshControl, Alert } from 'react-native';
2
+ import { SafeAreaView } from 'react-native-safe-area-context';
3
+ import { Ionicons } from '@expo/vector-icons';
4
+ import { useRouter } from 'expo-router';
5
+ import { useState, useCallback, useEffect } from 'react';
6
+
7
+ import { WalletCard, Button } from '@/components/ui';
8
+ import { usePara } from '@/providers/ParaProvider';
9
+ import { useViemClient } from '@/hooks/useViemClient';
10
+
11
+ export default function HomeScreen() {
12
+ const router = useRouter();
13
+ const { wallets, refreshAuth, logout } = usePara();
14
+ const { isReady, getBalance } = useViemClient();
15
+ const [refreshing, setRefreshing] = useState(false);
16
+ const [balance, setBalance] = useState<string | null>(null);
17
+
18
+ const fetchBalance = useCallback(async () => {
19
+ if (isReady) {
20
+ const bal = await getBalance();
21
+ setBalance(bal);
22
+ }
23
+ }, [isReady, getBalance]);
24
+
25
+ useEffect(() => {
26
+ fetchBalance();
27
+ }, [fetchBalance]);
28
+
29
+ const onRefresh = useCallback(async () => {
30
+ setRefreshing(true);
31
+ await refreshAuth();
32
+ await fetchBalance();
33
+ setRefreshing(false);
34
+ }, [refreshAuth, fetchBalance]);
35
+
36
+ const handleLogout = () => {
37
+ Alert.alert('Sign Out', 'Are you sure you want to sign out?', [
38
+ { text: 'Cancel', style: 'cancel' },
39
+ {
40
+ text: 'Sign Out',
41
+ style: 'destructive',
42
+ onPress: async () => {
43
+ await logout();
44
+ router.replace('/(auth)');
45
+ },
46
+ },
47
+ ]);
48
+ };
49
+
50
+ const handleSend = () => {
51
+ router.push('/(tabs)/send');
52
+ };
53
+
54
+ const primaryWallet = wallets[0];
55
+ const displayBalance = balance ? `${parseFloat(balance).toFixed(6)} ETH` : 'Loading...';
56
+
57
+ return (
58
+ <SafeAreaView testID="walletsView" className="flex-1 bg-gray-50">
59
+ <ScrollView
60
+ className="flex-1"
61
+ contentContainerStyle={{ padding: 24 }}
62
+ refreshControl={<RefreshControl refreshing={refreshing} onRefresh={onRefresh} />}>
63
+ <View className="mb-6">
64
+ <Text className="text-2xl font-bold text-gray-900">Your Wallet</Text>
65
+ <Text className="mt-1 text-gray-500">Manage your assets</Text>
66
+ </View>
67
+ {primaryWallet ? (
68
+ <WalletCard
69
+ address={primaryWallet.address}
70
+ balance={displayBalance}
71
+ network="Sepolia"
72
+ onSend={handleSend}
73
+ />
74
+ ) : (
75
+ <View className="items-center rounded-2xl bg-white p-6">
76
+ <Text className="text-center text-gray-500">No wallet found. Pull to refresh.</Text>
77
+ </View>
78
+ )}
79
+ {wallets.length > 1 && (
80
+ <View className="mt-6">
81
+ <Text className="mb-3 text-lg font-semibold text-gray-900">Other Wallets</Text>
82
+ {wallets.slice(1).map((wallet) => (
83
+ <View
84
+ key={wallet.id}
85
+ className="mb-2 flex-row items-center justify-between rounded-xl bg-white p-4">
86
+ <Text className="font-mono text-sm text-gray-600">
87
+ {wallet.address.slice(0, 10)}...{wallet.address.slice(-8)}
88
+ </Text>
89
+ <View className="rounded bg-gray-100 px-2 py-1">
90
+ <Text className="text-xs text-gray-600">{wallet.type}</Text>
91
+ </View>
92
+ </View>
93
+ ))}
94
+ </View>
95
+ )}
96
+
97
+ <View className="mt-8">
98
+ <Button
99
+ title="Sign Out"
100
+ variant="danger"
101
+ onPress={handleLogout}
102
+ icon={<Ionicons name="log-out-outline" size={20} color="#fff" />}
103
+ />
104
+ </View>
105
+
106
+ <View className="mt-8 items-center">
107
+ <Text className="text-sm text-gray-400">Powered by Para</Text>
108
+ </View>
109
+ </ScrollView>
110
+ </SafeAreaView>
111
+ );
112
+ }
@@ -0,0 +1,111 @@
1
+ import { View, Text, TextInput, Alert, KeyboardAvoidingView, Platform } from 'react-native';
2
+ import { SafeAreaView } from 'react-native-safe-area-context';
3
+ import { Ionicons } from '@expo/vector-icons';
4
+ import { useRouter } from 'expo-router';
5
+ import { useState, useCallback, useEffect } from 'react';
6
+ import type { Hex } from 'viem';
7
+
8
+ import { Button } from '@/components/ui';
9
+ import { useViemClient } from '@/hooks/useViemClient';
10
+
11
+ export default function SendScreen() {
12
+ const router = useRouter();
13
+ const { isReady, getBalance, sendTransaction, isLoading, error } = useViemClient();
14
+
15
+ const [balance, setBalance] = useState<string | null>(null);
16
+ const [recipient, setRecipient] = useState('');
17
+ const [amount, setAmount] = useState('');
18
+
19
+ const fetchBalance = useCallback(async () => {
20
+ if (isReady) {
21
+ const bal = await getBalance();
22
+ setBalance(bal);
23
+ }
24
+ }, [isReady, getBalance]);
25
+
26
+ useEffect(() => {
27
+ fetchBalance();
28
+ }, [fetchBalance]);
29
+
30
+ const handleSend = async () => {
31
+ if (!recipient) {
32
+ Alert.alert('Error', 'Please enter a recipient address');
33
+ return;
34
+ }
35
+ if (!amount || parseFloat(amount) <= 0) {
36
+ Alert.alert('Error', 'Please enter a valid amount');
37
+ return;
38
+ }
39
+
40
+ Alert.alert(
41
+ 'Confirm Transaction',
42
+ `Send ${amount} ETH to\n${recipient.slice(0, 16)}...?`,
43
+ [
44
+ { text: 'Cancel', style: 'cancel' },
45
+ {
46
+ text: 'Send',
47
+ onPress: async () => {
48
+ const hash = await sendTransaction(recipient as Hex, amount);
49
+ if (hash) {
50
+ Alert.alert('Success', `Transaction sent!\n\nHash: ${hash.slice(0, 20)}...`, [
51
+ { text: 'OK', onPress: () => router.back() },
52
+ ]);
53
+ } else if (error) {
54
+ Alert.alert('Error', error);
55
+ }
56
+ },
57
+ },
58
+ ]
59
+ );
60
+ };
61
+
62
+ const displayBalance = balance ? `${parseFloat(balance).toFixed(6)} ETH` : 'Loading...';
63
+
64
+ return (
65
+ <SafeAreaView className="flex-1 bg-gray-50" edges={['bottom']}>
66
+ <KeyboardAvoidingView
67
+ behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
68
+ className="flex-1">
69
+ <View className="flex-1 p-6">
70
+ {/* Balance Display */}
71
+ <View className="mb-6 rounded-2xl bg-white p-4">
72
+ <Text className="text-sm text-gray-500">Available Balance</Text>
73
+ <Text className="text-2xl font-bold text-gray-900">{displayBalance}</Text>
74
+ </View>
75
+
76
+ {/* Form */}
77
+ <View className="flex-1">
78
+ <Text className="mb-2 text-sm font-medium text-gray-700">Recipient Address</Text>
79
+ <TextInput
80
+ className="mb-4 rounded-xl border border-gray-200 bg-white p-4 font-mono text-sm text-gray-900"
81
+ placeholder="0x..."
82
+ placeholderTextColor="#9ca3af"
83
+ value={recipient}
84
+ onChangeText={setRecipient}
85
+ autoCapitalize="none"
86
+ autoCorrect={false}
87
+ />
88
+
89
+ <Text className="mb-2 text-sm font-medium text-gray-700">Amount (ETH)</Text>
90
+ <TextInput
91
+ className="mb-6 rounded-xl border border-gray-200 bg-white p-4 text-sm text-gray-900"
92
+ placeholder="0.001"
93
+ placeholderTextColor="#9ca3af"
94
+ value={amount}
95
+ onChangeText={setAmount}
96
+ keyboardType="decimal-pad"
97
+ />
98
+ </View>
99
+
100
+ {/* Send Button */}
101
+ <Button
102
+ title={isLoading ? 'Sending...' : 'Send ETH'}
103
+ onPress={handleSend}
104
+ disabled={isLoading || !isReady}
105
+ icon={<Ionicons name="send-outline" size={20} color="#fff" />}
106
+ />
107
+ </View>
108
+ </KeyboardAvoidingView>
109
+ </SafeAreaView>
110
+ );
111
+ }
@@ -0,0 +1,17 @@
1
+ import '../global.css';
2
+
3
+ import { Stack } from 'expo-router';
4
+ import { StatusBar } from 'expo-status-bar';
5
+ import { SafeAreaProvider } from 'react-native-safe-area-context';
6
+ import { ParaProvider } from '@/providers/ParaProvider';
7
+
8
+ export default function RootLayout() {
9
+ return (
10
+ <SafeAreaProvider>
11
+ <ParaProvider>
12
+ <StatusBar style="auto" />
13
+ <Stack screenOptions={{ headerShown: false }} />
14
+ </ParaProvider>
15
+ </SafeAreaProvider>
16
+ );
17
+ }
@@ -0,0 +1,22 @@
1
+ import { Redirect } from 'expo-router';
2
+ import { View, ActivityIndicator, Text } from 'react-native';
3
+ import { usePara } from '@/providers/ParaProvider';
4
+
5
+ export default function Index() {
6
+ const { isAuthenticated, isLoading } = usePara();
7
+
8
+ if (isLoading) {
9
+ return (
10
+ <View className="flex-1 items-center justify-center bg-white">
11
+ <ActivityIndicator size="large" color="#4F46E5" />
12
+ <Text className="mt-4 text-gray-500">Loading...</Text>
13
+ </View>
14
+ );
15
+ }
16
+
17
+ if (isAuthenticated) {
18
+ return <Redirect href="/(tabs)" />;
19
+ }
20
+
21
+ return <Redirect href="/(auth)" />;
22
+ }
@@ -0,0 +1,32 @@
1
+ {
2
+ "expo": {
3
+ "name": "{{expoAppName}}",
4
+ "slug": "{{expoSlug}}",
5
+ "version": "1.0.0",
6
+ "scheme": "{{expoScheme}}",
7
+ "platforms": ["ios", "android"],
8
+ "orientation": "portrait",
9
+ "icon": "./assets/icon.png",
10
+ "userInterfaceStyle": "automatic",
11
+ "splash": {
12
+ "image": "./assets/splash.png",
13
+ "resizeMode": "contain",
14
+ "backgroundColor": "#ffffff"
15
+ },
16
+ "ios": {
17
+ "supportsTablet": true,
18
+ "bundleIdentifier": "{{bundleId}}"
19
+ },
20
+ "android": {
21
+ "package": "{{bundleId}}",
22
+ "adaptiveIcon": {
23
+ "foregroundImage": "./assets/adaptive-icon.png",
24
+ "backgroundColor": "#ffffff"
25
+ }
26
+ },
27
+ "plugins": ["expo-router"],
28
+ "experiments": {
29
+ "typedRoutes": true
30
+ }
31
+ }
32
+ }
Binary file
Binary file
@@ -0,0 +1,12 @@
1
+ module.exports = function (api) {
2
+ api.cache(true);
3
+ let plugins = [];
4
+
5
+ plugins.push('react-native-worklets/plugin');
6
+
7
+ return {
8
+ presets: [['babel-preset-expo', { jsxImportSource: 'nativewind' }], 'nativewind/babel'],
9
+
10
+ plugins,
11
+ };
12
+ };