create-awarizon-app 1.0.10 → 1.0.12

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 (31) hide show
  1. package/package.json +1 -1
  2. package/templates/expo/app/(tabs)/_layout.tsx +3 -1
  3. package/templates/expo/app/(tabs)/index.tsx +39 -68
  4. package/templates/expo/app/(tabs)/nft.tsx +142 -0
  5. package/templates/expo/app/(tabs)/wallet.tsx +151 -0
  6. package/templates/expo/package.json +22 -23
  7. package/templates/expo-js/app/(tabs)/_layout.jsx +3 -1
  8. package/templates/expo-js/app/(tabs)/index.jsx +39 -68
  9. package/templates/expo-js/app/(tabs)/nft.jsx +141 -0
  10. package/templates/expo-js/app/(tabs)/wallet.jsx +152 -0
  11. package/templates/expo-js/package.json +19 -20
  12. package/templates/nextjs/app/layout.tsx +5 -1
  13. package/templates/nextjs/app/nft/page.tsx +140 -0
  14. package/templates/nextjs/app/page.tsx +34 -142
  15. package/templates/nextjs/app/wallet/page.tsx +145 -0
  16. package/templates/nextjs/components/Header.tsx +60 -0
  17. package/templates/nextjs/package.json +15 -16
  18. package/templates/nextjs-js/app/layout.jsx +5 -1
  19. package/templates/nextjs-js/app/nft/page.jsx +139 -0
  20. package/templates/nextjs-js/app/page.jsx +35 -130
  21. package/templates/nextjs-js/app/wallet/page.jsx +145 -0
  22. package/templates/nextjs-js/components/Header.jsx +60 -0
  23. package/templates/nextjs-js/package.json +11 -12
  24. package/templates/react/package.json +14 -15
  25. package/templates/react/src/App.tsx +88 -145
  26. package/templates/react/src/pages/NftPage.tsx +138 -0
  27. package/templates/react/src/pages/WalletPage.tsx +143 -0
  28. package/templates/react-js/package.json +11 -12
  29. package/templates/react-js/src/App.jsx +86 -119
  30. package/templates/react-js/src/pages/NftPage.jsx +137 -0
  31. package/templates/react-js/src/pages/WalletPage.jsx +143 -0
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.12",
4
4
  "description": "Scaffold a new project with the Awarizon Web3 SDK",
5
5
  "keywords": [
6
6
  "awarizon",
@@ -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, TokenIcon } 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' },
6
+ { symbol: 'BTC', name: 'Bitcoin' },
7
+ { symbol: 'USDC', name: 'USD Coin' },
8
+ { symbol: 'BNB', name: 'BNB' },
9
+ { symbol: 'HYPE', name: 'Hyperliquid' },
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 }) => {
19
+ const price = prices[symbol]?.price
20
+ return (
21
+ <View key={symbol} style={s.card}>
22
+ <View style={s.iconWrap}>
23
+ <TokenIcon symbol={symbol} size={40} radiusFraction={0.28} />
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,12 @@ 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
+ iconWrap: { marginBottom: 12 },
50
+ symbol: { color: '#6b7280', fontSize: 11, fontWeight: '600', marginBottom: 2 },
51
+ name: { color: '#374151', fontSize: 11, marginBottom: 10 },
52
+ priceRow: { minHeight: 22, justifyContent: 'center' },
53
+ price: { color: '#f9fafb', fontSize: 16, fontWeight: '600', letterSpacing: -0.3 },
83
54
  })
@@ -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,151 @@
1
+ import { useState, useEffect } from 'react'
2
+ import { ScrollView, View, Text, TouchableOpacity, StyleSheet, ActivityIndicator } from 'react-native'
3
+ import { useSDK, useRNWallet, usePrices, TokenIcon } from '@awarizon/react-native'
4
+ import { formatEther } from 'viem'
5
+ import type { Address } from '@awarizon/web3'
6
+
7
+ const ASSETS = [
8
+ { symbol: 'ETH', name: 'Ethereum' },
9
+ { symbol: 'BTC', name: 'Bitcoin' },
10
+ { symbol: 'USDC', name: 'USD Coin' },
11
+ { symbol: 'BNB', name: 'BNB' },
12
+ { symbol: 'HYPE', name: 'Hyperliquid' },
13
+ ]
14
+
15
+ export default function WalletScreen() {
16
+ const awarizon = useSDK()
17
+ const { address, isConnected, isLoading: walletLoading, create } = useRNWallet({ awarizon })
18
+ const { prices, isLoading: pricesLoading } = usePrices(ASSETS.map(a => a.symbol))
19
+
20
+ const [nativeBalance, setNativeBalance] = useState<string | null>(null)
21
+ const [balanceLoading, setBalanceLoading] = useState(false)
22
+
23
+ useEffect(() => {
24
+ if (!address) { setNativeBalance(null); return }
25
+ setBalanceLoading(true)
26
+ awarizon.publicClient
27
+ .getBalance({ address: address as Address })
28
+ .then(bal => setNativeBalance(formatEther(bal)))
29
+ .catch(() => setNativeBalance(null))
30
+ .finally(() => setBalanceLoading(false))
31
+ }, [address, awarizon])
32
+
33
+ const ethPrice = prices['ETH']?.price
34
+ const ethFiatValue =
35
+ nativeBalance != null && ethPrice != null
36
+ ? `$${(parseFloat(nativeBalance) * ethPrice).toLocaleString(undefined, { maximumFractionDigits: 2 })}`
37
+ : null
38
+
39
+ return (
40
+ <ScrollView style={s.bg} contentContainerStyle={s.container}>
41
+ <View style={s.hero}>
42
+ <Text style={s.badge}>Portfolio</Text>
43
+ <Text style={s.heading}>Wallet</Text>
44
+ <Text style={s.sub}>
45
+ Native balance and market prices via <Text style={s.code}>useRNWallet</Text> and <Text style={s.code}>usePrices</Text>.
46
+ </Text>
47
+ </View>
48
+
49
+ {!isConnected ? (
50
+ <View style={s.card}>
51
+ <Text style={s.cardTitle}>No Wallet</Text>
52
+ <Text style={s.sub}>Create a wallet to view your balance and portfolio.</Text>
53
+ <TouchableOpacity
54
+ style={[s.button, walletLoading && s.buttonDisabled]}
55
+ onPress={create}
56
+ disabled={walletLoading}
57
+ >
58
+ {walletLoading
59
+ ? <ActivityIndicator size="small" color="#000" />
60
+ : <Text style={s.buttonText}>Create Wallet</Text>}
61
+ </TouchableOpacity>
62
+ </View>
63
+ ) : (
64
+ <>
65
+ <View style={s.card}>
66
+ <Text style={s.cardTitle}>Account</Text>
67
+ <View style={s.dataRow}>
68
+ <Text style={s.dataLabel}>Address</Text>
69
+ <Text style={s.dataValue}>{address ? `${address.slice(0, 6)}…${address.slice(-4)}` : '—'}</Text>
70
+ </View>
71
+ </View>
72
+
73
+ <View style={s.card}>
74
+ <Text style={s.cardTitle}>Balance</Text>
75
+ <View style={s.assetRow}>
76
+ <TokenIcon symbol="ETH" size={36} radiusFraction={0.5} />
77
+ <View style={s.assetMeta}>
78
+ <Text style={s.assetSymbol}>ETH</Text>
79
+ <Text style={s.assetName}>Ethereum</Text>
80
+ </View>
81
+ <View style={s.assetRight}>
82
+ {balanceLoading
83
+ ? <ActivityIndicator size="small" color="#FFE500" />
84
+ : <>
85
+ <Text style={s.assetValue}>
86
+ {nativeBalance != null ? `${parseFloat(nativeBalance).toFixed(6)} ETH` : '—'}
87
+ </Text>
88
+ {ethFiatValue && <Text style={s.assetSub}>{ethFiatValue}</Text>}
89
+ </>
90
+ }
91
+ </View>
92
+ </View>
93
+ </View>
94
+
95
+ <View style={s.card}>
96
+ <Text style={s.cardTitle}>Assets</Text>
97
+ {ASSETS.map(({ symbol, name }, i) => {
98
+ const price = prices[symbol]?.price
99
+ return (
100
+ <View key={symbol} style={[s.assetRow, i < ASSETS.length - 1 && s.assetBorder]}>
101
+ <TokenIcon symbol={symbol} size={36} radiusFraction={0.28} />
102
+ <View style={s.assetMeta}>
103
+ <Text style={s.assetSymbol}>{symbol}</Text>
104
+ <Text style={s.assetName}>{name}</Text>
105
+ </View>
106
+ <View style={s.assetRight}>
107
+ {pricesLoading
108
+ ? <ActivityIndicator size="small" color="#FFE500" />
109
+ : <Text style={s.assetValue}>
110
+ {price != null
111
+ ? `$${price.toLocaleString(undefined, { maximumFractionDigits: 2 })}`
112
+ : '—'}
113
+ </Text>
114
+ }
115
+ <Text style={s.assetSub}>USD</Text>
116
+ </View>
117
+ </View>
118
+ )
119
+ })}
120
+ </View>
121
+ </>
122
+ )}
123
+ </ScrollView>
124
+ )
125
+ }
126
+
127
+ const s = StyleSheet.create({
128
+ bg: { flex: 1, backgroundColor: '#0a0a0a' },
129
+ container: { padding: 20, paddingBottom: 40 },
130
+ hero: { marginBottom: 24 },
131
+ badge: { color: '#FFE500', fontSize: 12, fontWeight: '700', textTransform: 'uppercase', letterSpacing: 1.5, marginBottom: 8 },
132
+ heading: { color: '#fff', fontSize: 28, fontWeight: '800', marginBottom: 6 },
133
+ sub: { color: '#9ca3af', fontSize: 15, lineHeight: 22 },
134
+ card: { backgroundColor: '#111', borderWidth: 1, borderColor: '#1f1f1f', borderRadius: 12, padding: 16, marginBottom: 16 },
135
+ cardTitle: { color: '#fff', fontWeight: '700', fontSize: 15, marginBottom: 12 },
136
+ dataRow: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', paddingVertical: 8 },
137
+ dataLabel: { color: '#9ca3af', fontSize: 13 },
138
+ dataValue: { color: '#e5e7eb', fontSize: 13, fontWeight: '500', fontFamily: 'monospace' },
139
+ assetRow: { flexDirection: 'row', alignItems: 'center', gap: 12, paddingVertical: 10 },
140
+ assetBorder: { borderBottomWidth: 1, borderBottomColor: '#1f1f1f' },
141
+ assetMeta: { flex: 1 },
142
+ assetSymbol: { color: '#fff', fontSize: 14, fontWeight: '600' },
143
+ assetName: { color: '#6b7280', fontSize: 12, marginTop: 2 },
144
+ assetRight: { alignItems: 'flex-end' },
145
+ assetValue: { color: '#e5e7eb', fontSize: 14, fontWeight: '500' },
146
+ assetSub: { color: '#6b7280', fontSize: 11, marginTop: 2 },
147
+ button: { backgroundColor: '#FFE500', borderRadius: 8, paddingHorizontal: 16, paddingVertical: 10, alignItems: 'center', marginTop: 16 },
148
+ buttonDisabled: { opacity: 0.4 },
149
+ buttonText: { color: '#000', fontWeight: '700', fontSize: 13 },
150
+ code: { color: '#FFE500', fontFamily: 'monospace' },
151
+ })
@@ -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.2",
13
+ "@awarizon/web3": "^1.3.7",
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, TokenIcon } 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' },
6
+ { symbol: 'BTC', name: 'Bitcoin' },
7
+ { symbol: 'USDC', name: 'USD Coin' },
8
+ { symbol: 'BNB', name: 'BNB' },
9
+ { symbol: 'HYPE', name: 'Hyperliquid' },
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 }) => {
19
+ const price = prices[symbol]?.price
20
+ return (
21
+ <View key={symbol} style={s.card}>
22
+ <View style={s.iconWrap}>
23
+ <TokenIcon symbol={symbol} size={40} radiusFraction={0.28} />
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,12 @@ 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
+ iconWrap: { marginBottom: 12 },
50
+ symbol: { color: '#6b7280', fontSize: 11, fontWeight: '600', marginBottom: 2 },
51
+ name: { color: '#374151', fontSize: 11, marginBottom: 10 },
52
+ priceRow: { minHeight: 22, justifyContent: 'center' },
53
+ price: { color: '#f9fafb', fontSize: 16, fontWeight: '600', letterSpacing: -0.3 },
83
54
  })