create-awarizon-app 1.0.10 → 1.0.11

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 (32) hide show
  1. package/dist/index.js +0 -0
  2. package/package.json +8 -8
  3. package/templates/expo/app/(tabs)/_layout.tsx +3 -1
  4. package/templates/expo/app/(tabs)/index.tsx +40 -68
  5. package/templates/expo/app/(tabs)/nft.tsx +142 -0
  6. package/templates/expo/app/(tabs)/wallet.tsx +127 -0
  7. package/templates/expo/package.json +22 -23
  8. package/templates/expo-js/app/(tabs)/_layout.jsx +3 -1
  9. package/templates/expo-js/app/(tabs)/index.jsx +40 -68
  10. package/templates/expo-js/app/(tabs)/nft.jsx +141 -0
  11. package/templates/expo-js/app/(tabs)/wallet.jsx +128 -0
  12. package/templates/expo-js/package.json +19 -20
  13. package/templates/nextjs/app/layout.tsx +5 -1
  14. package/templates/nextjs/app/nft/page.tsx +140 -0
  15. package/templates/nextjs/app/page.tsx +37 -142
  16. package/templates/nextjs/app/wallet/page.tsx +116 -0
  17. package/templates/nextjs/components/Header.tsx +56 -0
  18. package/templates/nextjs/package.json +15 -16
  19. package/templates/nextjs-js/app/layout.jsx +5 -1
  20. package/templates/nextjs-js/app/nft/page.jsx +139 -0
  21. package/templates/nextjs-js/app/page.jsx +38 -130
  22. package/templates/nextjs-js/app/wallet/page.jsx +116 -0
  23. package/templates/nextjs-js/components/Header.jsx +56 -0
  24. package/templates/nextjs-js/package.json +11 -12
  25. package/templates/react/package.json +14 -15
  26. package/templates/react/src/App.tsx +88 -145
  27. package/templates/react/src/pages/NftPage.tsx +138 -0
  28. package/templates/react/src/pages/WalletPage.tsx +114 -0
  29. package/templates/react-js/package.json +11 -12
  30. package/templates/react-js/src/App.jsx +86 -119
  31. package/templates/react-js/src/pages/NftPage.jsx +137 -0
  32. package/templates/react-js/src/pages/WalletPage.jsx +114 -0
package/dist/index.js CHANGED
File without changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-awarizon-app",
3
- "version": "1.0.10",
3
+ "version": "1.0.11",
4
4
  "description": "Scaffold a new project with the Awarizon Web3 SDK",
5
5
  "keywords": [
6
6
  "awarizon",
@@ -20,12 +20,6 @@
20
20
  "dist",
21
21
  "templates"
22
22
  ],
23
- "scripts": {
24
- "build": "tsup",
25
- "dev": "tsup --watch",
26
- "test": "vitest run",
27
- "clean": "rimraf dist"
28
- },
29
23
  "dependencies": {
30
24
  "@clack/prompts": "^0.9.1",
31
25
  "chalk": "^5.3.0",
@@ -43,5 +37,11 @@
43
37
  },
44
38
  "engines": {
45
39
  "node": ">=18"
40
+ },
41
+ "scripts": {
42
+ "build": "tsup",
43
+ "dev": "tsup --watch",
44
+ "test": "vitest run",
45
+ "clean": "rimraf dist"
46
46
  }
47
- }
47
+ }
@@ -17,7 +17,9 @@ export default function TabLayout() {
17
17
  headerTintColor: '#fff',
18
18
  }}
19
19
  >
20
- <Tabs.Screen name="index" options={{ title: 'Home' }} />
20
+ <Tabs.Screen name="index" options={{ title: 'Home' }} />
21
+ <Tabs.Screen name="nft" options={{ title: 'NFT' }} />
22
+ <Tabs.Screen name="wallet" options={{ title: 'Wallet' }} />
21
23
  </Tabs>
22
24
  )
23
25
  }
@@ -1,65 +1,41 @@
1
1
  import { ScrollView, View, Text, StyleSheet, ActivityIndicator } from 'react-native'
2
- import { contract, useRead, usePrice } from '@awarizon/react-native'
3
- import { ERC20_ABI } from '@awarizon/web3'
2
+ import { usePrices } from '@awarizon/react-native'
4
3
 
5
- const USDC_BASE = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913'
6
- const USDC = contract(USDC_BASE, ERC20_ABI)
7
-
8
- function Row({ label, value }: { label: string; value?: string }) {
9
- return (
10
- <View style={s.row}>
11
- <Text style={s.label}>{label}</Text>
12
- {value === undefined
13
- ? <ActivityIndicator size="small" color="#FFE500" />
14
- : <Text style={s.value}>{value}</Text>}
15
- </View>
16
- )
17
- }
4
+ const TOKENS = [
5
+ { symbol: 'ETH', name: 'Ethereum', color: '#627EEA' },
6
+ { symbol: 'BTC', name: 'Bitcoin', color: '#F7931A' },
7
+ { symbol: 'USDC', name: 'USD Coin', color: '#2775CA' },
8
+ { symbol: 'BNB', name: 'BNB', color: '#F3BA2F' },
9
+ { symbol: 'SOL', name: 'Solana', color: '#9945FF' },
10
+ ]
18
11
 
19
12
  export default function HomeScreen() {
20
- const { data: name, isLoading: l1 } = useRead<string>(USDC.name())
21
- const { data: symbol, isLoading: l2 } = useRead<string>(USDC.symbol())
22
- const { data: decimals, isLoading: l3 } = useRead<bigint>(USDC.decimals())
23
- const { data: totalSupply, isLoading: l4 } = useRead<bigint>(USDC.totalSupply())
24
- const { price: ethPrice, isLoading: lp } = usePrice('ETH')
25
-
26
- const formatted =
27
- totalSupply !== undefined && decimals !== undefined
28
- ? (Number(totalSupply) / 10 ** Number(decimals)).toLocaleString(undefined, { maximumFractionDigits: 2 })
29
- : undefined
13
+ const { prices, isLoading } = usePrices(TOKENS.map(t => t.symbol))
30
14
 
31
15
  return (
32
16
  <ScrollView style={s.bg} contentContainerStyle={s.container}>
33
- <View style={s.hero}>
34
- <Text style={s.badge}>{'{{ProjectName}}'}</Text>
35
- <Text style={s.heading}>Live on-chain data</Text>
36
- <Text style={s.sub}>Reading USDC on Base — no backend required.</Text>
37
- </View>
38
-
39
- <View style={s.card}>
40
- <Text style={s.cardTitle}>ETH Price</Text>
41
- <Row
42
- label="ETH / USD"
43
- value={lp ? undefined : ethPrice != null ? `$${ethPrice.toLocaleString(undefined, { maximumFractionDigits: 2 })}` : '—'}
44
- />
45
- <Text style={s.hint}>via usePrice · Chainlink + CoinGecko fallback</Text>
46
- </View>
47
-
48
- <View style={s.card}>
49
- <Text style={s.cardTitle}>USDC — Base</Text>
50
- <Row label="Name" value={l1 ? undefined : name ?? '—'} />
51
- <Row label="Symbol" value={l2 ? undefined : symbol ?? '—'} />
52
- <Row label="Decimals" value={l3 ? undefined : decimals !== undefined ? String(decimals) : '—'} />
53
- <Row label="Total Supply" value={l4 ? undefined : formatted} />
54
- </View>
55
-
56
- <View style={s.card}>
57
- <Text style={s.cardTitle}>Next steps</Text>
58
- <Text style={s.step}>1. Replace USDC_BASE with your contract address</Text>
59
- <Text style={s.step}>2. Swap ERC20_ABI for your contract ABI</Text>
60
- <Text style={s.step}>3. Change the chain in app/_layout.tsx</Text>
61
- <Text style={s.step}>4. Use <Text style={s.code}>useWrite(USDC.transfer)</Text> for transactions</Text>
62
- <Text style={s.step}>5. Use <Text style={s.code}>usePrices(["ETH","BTC"])</Text> for multiple prices</Text>
17
+ <View style={s.grid}>
18
+ {TOKENS.map(({ symbol, name, color }) => {
19
+ const price = prices[symbol]?.price
20
+ return (
21
+ <View key={symbol} style={s.card}>
22
+ <View style={[s.icon, { backgroundColor: color + '22', borderColor: color + '35' }]}>
23
+ <Text style={[s.iconText, { color }]}>{symbol.slice(0, 3)}</Text>
24
+ </View>
25
+ <Text style={s.symbol}>{symbol}</Text>
26
+ <Text style={s.name}>{name}</Text>
27
+ <View style={s.priceRow}>
28
+ {isLoading
29
+ ? <ActivityIndicator size="small" color="#FFE500" />
30
+ : <Text style={s.price}>
31
+ {price != null
32
+ ? `$${price.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`
33
+ : '—'}
34
+ </Text>}
35
+ </View>
36
+ </View>
37
+ )
38
+ })}
63
39
  </View>
64
40
  </ScrollView>
65
41
  )
@@ -67,17 +43,13 @@ export default function HomeScreen() {
67
43
 
68
44
  const s = StyleSheet.create({
69
45
  bg: { flex: 1, backgroundColor: '#0a0a0a' },
70
- container: { padding: 20, paddingBottom: 40 },
71
- hero: { marginBottom: 24 },
72
- badge: { color: '#FFE500', fontSize: 12, fontWeight: '700', textTransform: 'uppercase', letterSpacing: 1.5, marginBottom: 8 },
73
- heading: { color: '#fff', fontSize: 28, fontWeight: '800', marginBottom: 6 },
74
- sub: { color: '#9ca3af', fontSize: 15, lineHeight: 22 },
75
- card: { backgroundColor: '#111', borderWidth: 1, borderColor: '#1f1f1f', borderRadius: 12, padding: 16, marginBottom: 16 },
76
- cardTitle: { color: '#fff', fontWeight: '700', fontSize: 15, marginBottom: 12 },
77
- hint: { color: '#4b5563', fontSize: 11, marginTop: 8, fontStyle: 'italic' },
78
- row: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', paddingVertical: 8, borderBottomWidth: 1, borderBottomColor: '#1f1f1f' },
79
- label: { color: '#9ca3af', fontSize: 13 },
80
- value: { color: '#e5e7eb', fontSize: 13, fontWeight: '500', fontFamily: 'monospace' },
81
- step: { color: '#9ca3af', fontSize: 13, lineHeight: 22, marginBottom: 4 },
82
- code: { color: '#FFE500', fontFamily: 'monospace' },
46
+ container: { padding: 16, paddingBottom: 40 },
47
+ grid: { flexDirection: 'row', flexWrap: 'wrap', gap: 12 },
48
+ card: { width: '47%', backgroundColor: '#111827', borderWidth: 1, borderColor: '#1f2937', borderRadius: 16, padding: 16 },
49
+ icon: { width: 40, height: 40, borderRadius: 20, borderWidth: 1, alignItems: 'center', justifyContent: 'center', marginBottom: 12 },
50
+ iconText: { fontSize: 10, fontWeight: '700', letterSpacing: 0.5 },
51
+ symbol: { color: '#6b7280', fontSize: 11, fontWeight: '600', marginBottom: 2 },
52
+ name: { color: '#374151', fontSize: 11, marginBottom: 10 },
53
+ priceRow: { minHeight: 22, justifyContent: 'center' },
54
+ price: { color: '#f9fafb', fontSize: 16, fontWeight: '600', letterSpacing: -0.3 },
83
55
  })
@@ -0,0 +1,142 @@
1
+ import { useState, useEffect } from 'react'
2
+ import { ScrollView, View, Text, TextInput, TouchableOpacity, StyleSheet, ActivityIndicator } from 'react-native'
3
+ import { contract, useRead, useSDK } from '@awarizon/react-native'
4
+ import { ERC721_ABI } from '@awarizon/web3'
5
+ import type { Address } from '@awarizon/web3'
6
+
7
+ // Replace with your ERC-721 contract address on your configured chain.
8
+ const NFT_ADDRESS = '0x0000000000000000000000000000000000000000' as Address
9
+ const NFT = contract(NFT_ADDRESS, ERC721_ABI)
10
+
11
+ function Row({ label, value }: { label: string; value?: string }) {
12
+ return (
13
+ <View style={s.row}>
14
+ <Text style={s.label}>{label}</Text>
15
+ {value === undefined
16
+ ? <ActivityIndicator size="small" color="#FFE500" />
17
+ : <Text style={s.value}>{value}</Text>}
18
+ </View>
19
+ )
20
+ }
21
+
22
+ export default function NftScreen() {
23
+ const { data: name, isLoading: l1, error: e1 } = useRead<string>(NFT.name())
24
+ const { data: symbol, isLoading: l2, error: e2 } = useRead<string>(NFT.symbol())
25
+ const awarizon = useSDK()
26
+
27
+ const [tokenId, setTokenId] = useState('')
28
+ const [owner, setOwner] = useState<string | null>(null)
29
+ const [uri, setUri] = useState<string | null>(null)
30
+ const [lookupLoading, setLookupLoading] = useState(false)
31
+ const [lookupError, setLookupError] = useState<string | null>(null)
32
+
33
+ const collectionError = e1?.message ?? e2?.message ?? null
34
+
35
+ async function handleLookup() {
36
+ if (!tokenId) return
37
+ setLookupLoading(true)
38
+ setLookupError(null)
39
+ setOwner(null)
40
+ setUri(null)
41
+ try {
42
+ const id = BigInt(tokenId)
43
+ const nft = await awarizon.contract({ address: NFT_ADDRESS, abi: ERC721_ABI })
44
+ const [o, u] = await Promise.all([nft.ownerOf(id), nft.tokenURI(id)])
45
+ setOwner(String(o))
46
+ setUri(String(u))
47
+ } catch (e) {
48
+ setLookupError(e instanceof Error ? e.message : 'Lookup failed')
49
+ } finally {
50
+ setLookupLoading(false)
51
+ }
52
+ }
53
+
54
+ return (
55
+ <ScrollView style={s.bg} contentContainerStyle={s.container}>
56
+ <View style={s.hero}>
57
+ <Text style={s.badge}>ERC-721</Text>
58
+ <Text style={s.heading}>NFT Collection</Text>
59
+ <Text style={s.sub}>
60
+ Replace NFT_ADDRESS in this file with your ERC-721 contract address.
61
+ </Text>
62
+ </View>
63
+
64
+ {collectionError && (
65
+ <View style={s.errorBox}>
66
+ <Text style={s.errorText}>{collectionError}</Text>
67
+ </View>
68
+ )}
69
+
70
+ <View style={s.card}>
71
+ <Text style={s.cardTitle}>Collection Info</Text>
72
+ <Text style={s.hint}>{NFT_ADDRESS.slice(0, 6)}…{NFT_ADDRESS.slice(-4)}</Text>
73
+ <Row label="name()" value={l1 ? undefined : name ?? '—'} />
74
+ <Row label="symbol()" value={l2 ? undefined : symbol ?? '—'} />
75
+ </View>
76
+
77
+ <View style={s.card}>
78
+ <Text style={s.cardTitle}>Token Lookup</Text>
79
+ <View style={s.inputRow}>
80
+ <TextInput
81
+ style={s.input}
82
+ value={tokenId}
83
+ onChangeText={setTokenId}
84
+ placeholder="Token ID"
85
+ placeholderTextColor="#4b5563"
86
+ keyboardType="numeric"
87
+ />
88
+ <TouchableOpacity
89
+ style={[s.button, (!tokenId || lookupLoading) && s.buttonDisabled]}
90
+ onPress={handleLookup}
91
+ disabled={!tokenId || lookupLoading}
92
+ >
93
+ <Text style={s.buttonText}>{lookupLoading ? 'Loading…' : 'Look up'}</Text>
94
+ </TouchableOpacity>
95
+ </View>
96
+ {lookupError && <Text style={s.errorText}>{lookupError}</Text>}
97
+ {owner !== null && (
98
+ <Row label="ownerOf()" value={`${owner.slice(0, 6)}…${owner.slice(-4)}`} />
99
+ )}
100
+ {uri !== null && (
101
+ <View>
102
+ <Text style={s.hint}>tokenURI()</Text>
103
+ <Text style={s.uriText}>{uri}</Text>
104
+ </View>
105
+ )}
106
+ </View>
107
+
108
+ <View style={s.card}>
109
+ <Text style={s.cardTitle}>Usage Notes</Text>
110
+ <Text style={s.step}>1. Replace NFT_ADDRESS at the top of this file</Text>
111
+ <Text style={s.step}>2. No ABI needed — ERC721_ABI is built into the SDK</Text>
112
+ <Text style={s.step}>3. Enter a token ID and tap Look up to call <Text style={s.code}>ownerOf</Text> and <Text style={s.code}>tokenURI</Text></Text>
113
+ <Text style={s.step}>4. Use <Text style={s.code}>awarizon.contract()</Text> for on-demand reads in handlers</Text>
114
+ </View>
115
+ </ScrollView>
116
+ )
117
+ }
118
+
119
+ const s = StyleSheet.create({
120
+ bg: { flex: 1, backgroundColor: '#0a0a0a' },
121
+ container: { padding: 20, paddingBottom: 40 },
122
+ hero: { marginBottom: 24 },
123
+ badge: { color: '#FFE500', fontSize: 12, fontWeight: '700', textTransform: 'uppercase', letterSpacing: 1.5, marginBottom: 8 },
124
+ heading: { color: '#fff', fontSize: 28, fontWeight: '800', marginBottom: 6 },
125
+ sub: { color: '#9ca3af', fontSize: 15, lineHeight: 22 },
126
+ card: { backgroundColor: '#111', borderWidth: 1, borderColor: '#1f1f1f', borderRadius: 12, padding: 16, marginBottom: 16 },
127
+ cardTitle: { color: '#fff', fontWeight: '700', fontSize: 15, marginBottom: 12 },
128
+ hint: { color: '#4b5563', fontSize: 11, marginBottom: 8 },
129
+ row: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', paddingVertical: 8, borderBottomWidth: 1, borderBottomColor: '#1f1f1f' },
130
+ label: { color: '#9ca3af', fontSize: 13 },
131
+ value: { color: '#e5e7eb', fontSize: 13, fontWeight: '500', fontFamily: 'monospace' },
132
+ inputRow: { flexDirection: 'row', gap: 8, marginBottom: 12 },
133
+ input: { flex: 1, backgroundColor: '#1a1a1a', borderWidth: 1, borderColor: '#333', borderRadius: 8, paddingHorizontal: 12, paddingVertical: 8, color: '#e5e7eb', fontFamily: 'monospace', fontSize: 13 },
134
+ button: { backgroundColor: '#FFE500', borderRadius: 8, paddingHorizontal: 16, paddingVertical: 8, justifyContent: 'center' },
135
+ buttonDisabled: { opacity: 0.4 },
136
+ buttonText: { color: '#000', fontWeight: '700', fontSize: 12 },
137
+ errorBox: { backgroundColor: 'rgba(239,68,68,0.1)', borderWidth: 1, borderColor: 'rgba(239,68,68,0.2)', borderRadius: 8, padding: 12, marginBottom: 16 },
138
+ errorText: { color: '#f87171', fontSize: 12, fontFamily: 'monospace' },
139
+ uriText: { color: '#e5e7eb', fontSize: 12, fontFamily: 'monospace', marginTop: 4 },
140
+ step: { color: '#9ca3af', fontSize: 13, lineHeight: 22, marginBottom: 4 },
141
+ code: { color: '#FFE500', fontFamily: 'monospace' },
142
+ })
@@ -0,0 +1,127 @@
1
+ import { useState, useEffect } from 'react'
2
+ import { ScrollView, View, Text, TouchableOpacity, StyleSheet, ActivityIndicator } from 'react-native'
3
+ import { useSDK, useRNWallet, usePrices } from '@awarizon/react-native'
4
+ import { formatEther } from 'viem'
5
+ import type { Address } from '@awarizon/web3'
6
+
7
+ const SYMBOLS = ['ETH', 'BTC', 'USDC']
8
+
9
+ function Row({ label, value, loading = false }: { label: string; value?: string; loading?: boolean }) {
10
+ return (
11
+ <View style={s.row}>
12
+ <Text style={s.label}>{label}</Text>
13
+ {loading
14
+ ? <ActivityIndicator size="small" color="#FFE500" />
15
+ : <Text style={s.value}>{value ?? '—'}</Text>}
16
+ </View>
17
+ )
18
+ }
19
+
20
+ export default function WalletScreen() {
21
+ const awarizon = useSDK()
22
+ const { address, isConnected, isLoading: walletLoading, create } = useRNWallet({ awarizon })
23
+ const { prices, isLoading: pricesLoading } = usePrices(SYMBOLS)
24
+
25
+ const [nativeBalance, setNativeBalance] = useState<string | null>(null)
26
+ const [balanceLoading, setBalanceLoading] = useState(false)
27
+
28
+ useEffect(() => {
29
+ if (!address) { setNativeBalance(null); return }
30
+ setBalanceLoading(true)
31
+ awarizon.publicClient
32
+ .getBalance({ address: address as Address })
33
+ .then(bal => setNativeBalance(formatEther(bal)))
34
+ .catch(() => setNativeBalance(null))
35
+ .finally(() => setBalanceLoading(false))
36
+ }, [address, awarizon])
37
+
38
+ const fmtPrice = (sym: string) =>
39
+ prices[sym]?.price != null
40
+ ? `$${prices[sym].price.toLocaleString(undefined, { maximumFractionDigits: 2 })}`
41
+ : '—'
42
+
43
+ const ethFiatValue =
44
+ nativeBalance != null && prices['ETH']?.price != null
45
+ ? `$${(parseFloat(nativeBalance) * prices['ETH'].price).toLocaleString(undefined, { maximumFractionDigits: 2 })}`
46
+ : null
47
+
48
+ return (
49
+ <ScrollView style={s.bg} contentContainerStyle={s.container}>
50
+ <View style={s.hero}>
51
+ <Text style={s.badge}>Portfolio</Text>
52
+ <Text style={s.heading}>Wallet</Text>
53
+ <Text style={s.sub}>
54
+ Native balance and market prices via <Text style={s.code}>useRNWallet</Text> and <Text style={s.code}>usePrices</Text>.
55
+ </Text>
56
+ </View>
57
+
58
+ {!isConnected ? (
59
+ <View style={s.card}>
60
+ <Text style={s.cardTitle}>No Wallet</Text>
61
+ <Text style={s.sub}>Create a wallet to view your balance and portfolio.</Text>
62
+ <TouchableOpacity
63
+ style={[s.button, walletLoading && s.buttonDisabled]}
64
+ onPress={create}
65
+ disabled={walletLoading}
66
+ >
67
+ {walletLoading
68
+ ? <ActivityIndicator size="small" color="#000" />
69
+ : <Text style={s.buttonText}>Create Wallet</Text>}
70
+ </TouchableOpacity>
71
+ </View>
72
+ ) : (
73
+ <>
74
+ <View style={s.card}>
75
+ <Text style={s.cardTitle}>Account</Text>
76
+ <Row label="Address" value={address ? `${address.slice(0, 6)}…${address.slice(-4)}` : undefined} />
77
+ </View>
78
+
79
+ <View style={s.card}>
80
+ <Text style={s.cardTitle}>Balance</Text>
81
+ <Row
82
+ label="Native"
83
+ value={nativeBalance != null ? `${parseFloat(nativeBalance).toFixed(6)} ETH` : undefined}
84
+ loading={balanceLoading}
85
+ />
86
+ <Row
87
+ label="Est. Value"
88
+ value={ethFiatValue ?? undefined}
89
+ loading={balanceLoading || pricesLoading}
90
+ />
91
+ <Text style={s.hint}>via publicClient.getBalance · ETH × market price</Text>
92
+ </View>
93
+
94
+ <View style={s.card}>
95
+ <Text style={s.cardTitle}>Market Prices</Text>
96
+ {pricesLoading
97
+ ? <ActivityIndicator size="small" color="#FFE500" style={{ marginVertical: 8 }} />
98
+ : SYMBOLS.map(sym => (
99
+ <Row key={sym} label={sym} value={fmtPrice(sym)} />
100
+ ))
101
+ }
102
+ <Text style={s.hint}>via usePrices · Chainlink + CoinGecko fallback</Text>
103
+ </View>
104
+ </>
105
+ )}
106
+ </ScrollView>
107
+ )
108
+ }
109
+
110
+ const s = StyleSheet.create({
111
+ bg: { flex: 1, backgroundColor: '#0a0a0a' },
112
+ container: { padding: 20, paddingBottom: 40 },
113
+ hero: { marginBottom: 24 },
114
+ badge: { color: '#FFE500', fontSize: 12, fontWeight: '700', textTransform: 'uppercase', letterSpacing: 1.5, marginBottom: 8 },
115
+ heading: { color: '#fff', fontSize: 28, fontWeight: '800', marginBottom: 6 },
116
+ sub: { color: '#9ca3af', fontSize: 15, lineHeight: 22 },
117
+ card: { backgroundColor: '#111', borderWidth: 1, borderColor: '#1f1f1f', borderRadius: 12, padding: 16, marginBottom: 16 },
118
+ cardTitle: { color: '#fff', fontWeight: '700', fontSize: 15, marginBottom: 12 },
119
+ hint: { color: '#4b5563', fontSize: 11, marginTop: 8 },
120
+ row: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', paddingVertical: 8, borderBottomWidth: 1, borderBottomColor: '#1f1f1f' },
121
+ label: { color: '#9ca3af', fontSize: 13 },
122
+ value: { color: '#e5e7eb', fontSize: 13, fontWeight: '500', fontFamily: 'monospace' },
123
+ button: { backgroundColor: '#FFE500', borderRadius: 8, paddingHorizontal: 16, paddingVertical: 10, alignItems: 'center', marginTop: 16 },
124
+ buttonDisabled: { opacity: 0.4 },
125
+ buttonText: { color: '#000', fontWeight: '700', fontSize: 13 },
126
+ code: { color: '#FFE500', fontFamily: 'monospace' },
127
+ })
@@ -3,34 +3,33 @@
3
3
  "version": "1.0.0",
4
4
  "main": "expo-router/entry",
5
5
  "scripts": {
6
- "start": "expo start",
6
+ "start": "expo start",
7
7
  "android": "expo start --android",
8
- "ios": "expo start --ios",
9
- "web": "expo start --web"
8
+ "ios": "expo start --ios",
9
+ "web": "expo start --web"
10
10
  },
11
11
  "dependencies": {
12
- "@awarizon/react-native": "^1.2.0",
13
- "@awarizon/web3": "^1.3.4",
14
- "@expo/vector-icons": "^14.0.2",
15
- "expo": "~51.0.0",
16
- "expo-constants": "~16.0.2",
17
- "expo-font": "~12.0.9",
18
- "expo-linking": "~6.3.1",
19
- "expo-router": "~3.5.17",
20
- "expo-secure-store": "~13.0.2",
21
- "expo-splash-screen": "~0.27.5",
22
- "expo-status-bar": "~1.12.1",
23
- "expo-system-ui": "~3.0.7",
24
- "react": "18.2.0",
25
- "react-dom": "18.2.0",
26
- "react-native": "0.74.5",
12
+ "@awarizon/react-native": "^1.2.1",
13
+ "@awarizon/web3": "^1.3.5",
14
+ "@expo/vector-icons": "^14.0.2",
15
+ "expo": "~51.0.0",
16
+ "expo-constants": "~16.0.2",
17
+ "expo-font": "~12.0.9",
18
+ "expo-linking": "~6.3.1",
19
+ "expo-router": "~3.5.17",
20
+ "expo-secure-store": "~13.0.2",
21
+ "expo-splash-screen": "~0.27.5",
22
+ "expo-status-bar": "~1.12.1",
23
+ "expo-system-ui": "~3.0.7",
24
+ "react": "18.2.0",
25
+ "react-dom": "18.2.0",
26
+ "react-native": "0.74.5",
27
27
  "react-native-safe-area-context": "4.10.5",
28
- "react-native-screens": "3.31.1"
28
+ "react-native-screens": "3.31.1"
29
29
  },
30
30
  "devDependencies": {
31
- "@babel/core": "^7.24.0",
32
- "@types/react": "~18.2.45",
33
- "typescript": "^5.3.3"
31
+ "@babel/core": "^7.24.0",
32
+ "@types/react": "~18.2.45",
33
+ "typescript": "^5.3.3"
34
34
  }
35
35
  }
36
-
@@ -17,7 +17,9 @@ export default function TabLayout() {
17
17
  headerTintColor: '#fff',
18
18
  }}
19
19
  >
20
- <Tabs.Screen name="index" options={{ title: 'Home' }} />
20
+ <Tabs.Screen name="index" options={{ title: 'Home' }} />
21
+ <Tabs.Screen name="nft" options={{ title: 'NFT' }} />
22
+ <Tabs.Screen name="wallet" options={{ title: 'Wallet' }} />
21
23
  </Tabs>
22
24
  )
23
25
  }
@@ -1,65 +1,41 @@
1
1
  import { ScrollView, View, Text, StyleSheet, ActivityIndicator } from 'react-native'
2
- import { contract, useRead, usePrice } from '@awarizon/react-native'
3
- import { ERC20_ABI } from '@awarizon/web3'
2
+ import { usePrices } from '@awarizon/react-native'
4
3
 
5
- const USDC_BASE = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913'
6
- const USDC = contract(USDC_BASE, ERC20_ABI)
7
-
8
- function Row({ label, value }) {
9
- return (
10
- <View style={s.row}>
11
- <Text style={s.label}>{label}</Text>
12
- {value === undefined
13
- ? <ActivityIndicator size="small" color="#FFE500" />
14
- : <Text style={s.value}>{value}</Text>}
15
- </View>
16
- )
17
- }
4
+ const TOKENS = [
5
+ { symbol: 'ETH', name: 'Ethereum', color: '#627EEA' },
6
+ { symbol: 'BTC', name: 'Bitcoin', color: '#F7931A' },
7
+ { symbol: 'USDC', name: 'USD Coin', color: '#2775CA' },
8
+ { symbol: 'BNB', name: 'BNB', color: '#F3BA2F' },
9
+ { symbol: 'SOL', name: 'Solana', color: '#9945FF' },
10
+ ]
18
11
 
19
12
  export default function HomeScreen() {
20
- const { data: name, isLoading: l1 } = useRead(USDC.name())
21
- const { data: symbol, isLoading: l2 } = useRead(USDC.symbol())
22
- const { data: decimals, isLoading: l3 } = useRead(USDC.decimals())
23
- const { data: totalSupply, isLoading: l4 } = useRead(USDC.totalSupply())
24
- const { price: ethPrice, isLoading: lp } = usePrice('ETH')
25
-
26
- const formatted =
27
- totalSupply !== undefined && decimals !== undefined
28
- ? (Number(totalSupply) / 10 ** Number(decimals)).toLocaleString(undefined, { maximumFractionDigits: 2 })
29
- : undefined
13
+ const { prices, isLoading } = usePrices(TOKENS.map(t => t.symbol))
30
14
 
31
15
  return (
32
16
  <ScrollView style={s.bg} contentContainerStyle={s.container}>
33
- <View style={s.hero}>
34
- <Text style={s.badge}>{'{{ProjectName}}'}</Text>
35
- <Text style={s.heading}>Live on-chain data</Text>
36
- <Text style={s.sub}>Reading USDC on Base — no backend required.</Text>
37
- </View>
38
-
39
- <View style={s.card}>
40
- <Text style={s.cardTitle}>ETH Price</Text>
41
- <Row
42
- label="ETH / USD"
43
- value={lp ? undefined : ethPrice != null ? `$${ethPrice.toLocaleString(undefined, { maximumFractionDigits: 2 })}` : '—'}
44
- />
45
- <Text style={s.hint}>via usePrice · Chainlink + CoinGecko fallback</Text>
46
- </View>
47
-
48
- <View style={s.card}>
49
- <Text style={s.cardTitle}>USDC — Base</Text>
50
- <Row label="Name" value={l1 ? undefined : name != null ? String(name) : '—'} />
51
- <Row label="Symbol" value={l2 ? undefined : symbol != null ? String(symbol) : '—'} />
52
- <Row label="Decimals" value={l3 ? undefined : decimals != null ? String(decimals) : '—'} />
53
- <Row label="Total Supply" value={l4 ? undefined : formatted} />
54
- </View>
55
-
56
- <View style={s.card}>
57
- <Text style={s.cardTitle}>Next steps</Text>
58
- <Text style={s.step}>1. Replace USDC_BASE with your contract address</Text>
59
- <Text style={s.step}>2. Swap ERC20_ABI for your contract ABI</Text>
60
- <Text style={s.step}>3. Change the chain in app/_layout.jsx</Text>
61
- <Text style={s.step}>4. Use <Text style={s.code}>useWrite(USDC.transfer)</Text> for transactions</Text>
62
- <Text style={s.step}>5. Use <Text style={s.code}>usePrices(["ETH","BTC"])</Text> for multiple prices</Text>
17
+ <View style={s.grid}>
18
+ {TOKENS.map(({ symbol, name, color }) => {
19
+ const price = prices[symbol]?.price
20
+ return (
21
+ <View key={symbol} style={s.card}>
22
+ <View style={[s.icon, { backgroundColor: color + '22', borderColor: color + '35' }]}>
23
+ <Text style={[s.iconText, { color }]}>{symbol.slice(0, 3)}</Text>
24
+ </View>
25
+ <Text style={s.symbol}>{symbol}</Text>
26
+ <Text style={s.name}>{name}</Text>
27
+ <View style={s.priceRow}>
28
+ {isLoading
29
+ ? <ActivityIndicator size="small" color="#FFE500" />
30
+ : <Text style={s.price}>
31
+ {price != null
32
+ ? `$${price.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`
33
+ : '—'}
34
+ </Text>}
35
+ </View>
36
+ </View>
37
+ )
38
+ })}
63
39
  </View>
64
40
  </ScrollView>
65
41
  )
@@ -67,17 +43,13 @@ export default function HomeScreen() {
67
43
 
68
44
  const s = StyleSheet.create({
69
45
  bg: { flex: 1, backgroundColor: '#0a0a0a' },
70
- container: { padding: 20, paddingBottom: 40 },
71
- hero: { marginBottom: 24 },
72
- badge: { color: '#FFE500', fontSize: 12, fontWeight: '700', textTransform: 'uppercase', letterSpacing: 1.5, marginBottom: 8 },
73
- heading: { color: '#fff', fontSize: 28, fontWeight: '800', marginBottom: 6 },
74
- sub: { color: '#9ca3af', fontSize: 15, lineHeight: 22 },
75
- card: { backgroundColor: '#111', borderWidth: 1, borderColor: '#1f1f1f', borderRadius: 12, padding: 16, marginBottom: 16 },
76
- cardTitle: { color: '#fff', fontWeight: '700', fontSize: 15, marginBottom: 12 },
77
- hint: { color: '#4b5563', fontSize: 11, marginTop: 8, fontStyle: 'italic' },
78
- row: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', paddingVertical: 8, borderBottomWidth: 1, borderBottomColor: '#1f1f1f' },
79
- label: { color: '#9ca3af', fontSize: 13 },
80
- value: { color: '#e5e7eb', fontSize: 13, fontWeight: '500', fontFamily: 'monospace' },
81
- step: { color: '#9ca3af', fontSize: 13, lineHeight: 22, marginBottom: 4 },
82
- code: { color: '#FFE500', fontFamily: 'monospace' },
46
+ container: { padding: 16, paddingBottom: 40 },
47
+ grid: { flexDirection: 'row', flexWrap: 'wrap', gap: 12 },
48
+ card: { width: '47%', backgroundColor: '#111827', borderWidth: 1, borderColor: '#1f2937', borderRadius: 16, padding: 16 },
49
+ icon: { width: 40, height: 40, borderRadius: 20, borderWidth: 1, alignItems: 'center', justifyContent: 'center', marginBottom: 12 },
50
+ iconText: { fontSize: 10, fontWeight: '700', letterSpacing: 0.5 },
51
+ symbol: { color: '#6b7280', fontSize: 11, fontWeight: '600', marginBottom: 2 },
52
+ name: { color: '#374151', fontSize: 11, marginBottom: 10 },
53
+ priceRow: { minHeight: 22, justifyContent: 'center' },
54
+ price: { color: '#f9fafb', fontSize: 16, fontWeight: '600', letterSpacing: -0.3 },
83
55
  })