464 lines
14 KiB
TypeScript
464 lines
14 KiB
TypeScript
import React, { useEffect, useState } from 'react';
|
|
import { Image, StyleSheet, Pressable, Platform, View, Alert, ActivityIndicator, ToastAndroid, Button } from 'react-native';
|
|
import { useCameraPermissions } from 'expo-camera';
|
|
import Link, { } from 'expo-router/link';
|
|
import { ethers } from 'ethers';
|
|
|
|
import ParallaxScrollView from '@/components/ParallaxScrollView';
|
|
import { ThemedText } from '@/components/ThemedText';
|
|
import { ThemedView } from '@/components/ThemedView';
|
|
import { WalletConnectModal, useWalletConnectModal } from '@walletconnect/modal-react-native';
|
|
import { CHAIN_ID, CONTRACT_ADDRESS, projectId, providerMetadata } from './shared';
|
|
import { contractABI } from './ABI/transfer_contract';
|
|
// import { GoogleSignin } from 'react-native-google-signin';
|
|
import { GoogleSignin, statusCodes, GoogleSigninButton, isErrorWithCode, User } from '@react-native-google-signin/google-signin';
|
|
import auth, { getAuth, signInWithCredential } from '@react-native-firebase/auth';
|
|
import { app, authInstance } from '@/components/FirebaseConfig';
|
|
import Clipboard from '@react-native-clipboard/clipboard';
|
|
import FontAwesome5 from '@expo/vector-icons/FontAwesome5';
|
|
export default function HomeScreen() {
|
|
const [loading, setLoading] = useState(false);
|
|
const [transferLoading, setTransferLoading] = useState(false);
|
|
|
|
const [firebaseUser, setFirebaseUser] = useState<User | null>();
|
|
const [permission, requestPermission] = useCameraPermissions();
|
|
const [embeddedWallet, setEmbeddedWallet] = useState("");
|
|
const isPermissionGranted = Boolean(permission?.granted);
|
|
|
|
const { open, isConnected, address, provider } = useWalletConnectModal();
|
|
const activeAddress = isConnected ? address : embeddedWallet;
|
|
const [ethBalance, setEthBalance] = useState(null);
|
|
const [usdtBalance, setUsdtBalance] = useState(null);
|
|
|
|
useEffect(() => {
|
|
const fetchBalances = async () => {
|
|
if (activeAddress) {
|
|
try {
|
|
// Fetch ETH balance
|
|
const ethResponse = await fetch(
|
|
`http://vps.playpoolstudios.com:30117/ethBalance/${activeAddress}`
|
|
);
|
|
const ethData = await ethResponse.json();
|
|
setEthBalance(ethData.ethBalance);
|
|
|
|
// Fetch USDT balance
|
|
const usdtResponse = await fetch(
|
|
`http://vps.playpoolstudios.com:30117/usdtBalance/${activeAddress}`
|
|
);
|
|
const usdtData = await usdtResponse.json();
|
|
setUsdtBalance(usdtData.tokenBalance);
|
|
} catch (error) {
|
|
console.error("Error fetching balances:", error);
|
|
setEthBalance(null);
|
|
setUsdtBalance(null);
|
|
}
|
|
}
|
|
};
|
|
|
|
fetchBalances();
|
|
}, [activeAddress]);
|
|
|
|
useEffect(() => {
|
|
setLoading(true);
|
|
|
|
GoogleSignin.configure({
|
|
webClientId: '652710461284-7e2909o0bmsqqo17l4fhtkm33emh35vj.apps.googleusercontent.com',
|
|
});
|
|
// Check if the user is already signed in
|
|
const checkSignInStatus = async () => {
|
|
try {
|
|
const userInfo = await GoogleSignin.signInSilently();
|
|
|
|
if (userInfo != null) {
|
|
console.log("Setting firebase user silently");
|
|
setFirebaseUser(userInfo.data);
|
|
|
|
fetchEmbeddedWallet();
|
|
}
|
|
} catch (error) {
|
|
console.error('Failed to check Google sign-in status:', error);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
checkSignInStatus();
|
|
}, [])
|
|
|
|
useEffect(() => {
|
|
fetchEmbeddedWallet();
|
|
}, [firebaseUser])
|
|
|
|
const fetchEmbeddedWallet = async () => {
|
|
// console.log(firebaseUser?.user.email);
|
|
// const googleCredential = auth.GoogleAuthProvider.credential(firebaseUser?.idToken ?? "");
|
|
// const userCredential = await authInstance.signInWithCredential(googleCredential);
|
|
// const tokenId = await userCredential.user.getIdToken();
|
|
// console.log(`tokenId from firebase ${tokenId}`);
|
|
if (!firebaseUser) { return; }
|
|
const register = await fetch(`http://vps.playpoolstudios.com:30017/signinWithGoogle?tokenId=${firebaseUser?.idToken}`)
|
|
const registerJson = await register.json();
|
|
console.log(registerJson);
|
|
setEmbeddedWallet(registerJson['pub_key']);
|
|
}
|
|
const connectWallet = async () => {
|
|
setLoading(true);
|
|
try {
|
|
if (isConnected) {
|
|
await provider?.disconnect();
|
|
} else {
|
|
await open();
|
|
}
|
|
} catch (error) {
|
|
console.error('Wallet connection failed', error);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
const transferEth = async () => {
|
|
if (!provider || !isConnected) {
|
|
console.error('Wallet not connected.');
|
|
return;
|
|
}
|
|
|
|
setTransferLoading(true);
|
|
|
|
try {
|
|
const ethersProvider = new ethers.providers.Web3Provider(provider);
|
|
const network = await ethersProvider.getNetwork();
|
|
|
|
if (network.chainId !== CHAIN_ID) {
|
|
console.log(`Switching from ${network.chainId} to ${CHAIN_ID} (Arbitrum Sepolia)...`);
|
|
// await provider.request({method:"eth_requestAccounts",params: []});
|
|
await provider.request({
|
|
method: 'wallet_switchEthereumChain',
|
|
params: [{ chainId: `0x${CHAIN_ID.toString(16)}` }],
|
|
});
|
|
console.log(`Switched to chain ID: ${CHAIN_ID}`);
|
|
}
|
|
console.log(`Current chain ID: ${CHAIN_ID}`);
|
|
|
|
const signer = ethersProvider.getSigner();
|
|
|
|
|
|
const contract = new ethers.Contract(CONTRACT_ADDRESS, contractABI, signer);
|
|
|
|
const tx = await contract.transferETH('warlock', {
|
|
value: ethers.utils.parseEther('0.002'), // Sending 0.003 ETH
|
|
});
|
|
|
|
console.log('Transaction sent:', tx.hash);
|
|
await tx.wait();
|
|
console.log('Transaction confirmed:', tx.hash);
|
|
} catch (error) {
|
|
console.error('Transfer failed:', error);
|
|
} finally {
|
|
setTransferLoading(false);
|
|
}
|
|
};
|
|
|
|
const _signIn = async () => {
|
|
try {
|
|
await GoogleSignin.hasPlayServices();
|
|
const { type, data } = await GoogleSignin.signIn();
|
|
if (type === 'success') {
|
|
console.log({ data });
|
|
setFirebaseUser(data);
|
|
fetchEmbeddedWallet();
|
|
|
|
} else {
|
|
// sign in was cancelled by user
|
|
setTimeout(() => {
|
|
Alert.alert('cancelled');
|
|
}, 500);
|
|
}
|
|
} catch (error: any) {
|
|
if (isErrorWithCode(error)) {
|
|
console.log('error', error.message);
|
|
switch (error.code) {
|
|
case statusCodes.IN_PROGRESS:
|
|
// operation (eg. sign in) already in progress
|
|
Alert.alert(
|
|
'in progress',
|
|
'operation (eg. sign in) already in progress',
|
|
);
|
|
break;
|
|
case statusCodes.PLAY_SERVICES_NOT_AVAILABLE:
|
|
// android only
|
|
Alert.alert('play services not available or outdated');
|
|
break;
|
|
default:
|
|
Alert.alert('Something went wrong: ', error.toString() + error.code);
|
|
}
|
|
} else {
|
|
alert(`an error that's not related to google sign in occurred`);
|
|
}
|
|
}
|
|
};
|
|
|
|
const handleLogout = async () => {
|
|
try {
|
|
await GoogleSignin.signOut();
|
|
setFirebaseUser(null);
|
|
} catch (error) {
|
|
console.error('Logout failed:', error);
|
|
}
|
|
}
|
|
|
|
const copyWalletAddress = () => {
|
|
Clipboard.setString(activeAddress ?? "");
|
|
if (Platform.OS === 'android') {
|
|
ToastAndroid.show('Wallet address copied!', ToastAndroid.SHORT);
|
|
} else {
|
|
alert('Wallet address copied!');
|
|
}
|
|
};
|
|
|
|
return (
|
|
<>
|
|
<ParallaxScrollView
|
|
headerBackgroundColor={{ light: '#A1CEDC', dark: '#1D3D47' }}
|
|
headerImage={
|
|
<Image
|
|
source={require('@/assets/images/partial-react-logo.png')}
|
|
style={styles.reactLogo}
|
|
/>
|
|
}
|
|
>
|
|
<ThemedView style={styles.titleContainer}>
|
|
<ThemedText type="title">Welcome to ECPay</ThemedText>
|
|
</ThemedView>
|
|
|
|
|
|
{(firebaseUser || isConnected) ? (<>
|
|
<ThemedView style={styles.addressContainer}>
|
|
<ThemedView >
|
|
{loading ? (
|
|
<ActivityIndicator size="large" color="#0000ff" />
|
|
) : (
|
|
<ThemedView style={styles.balancesContainer}>
|
|
<ThemedView style={styles.balance}>
|
|
<ThemedText style={styles.value}>
|
|
{ethBalance !== null ? `${parseFloat(ethBalance).toFixed(8)}` : 'Loading'}
|
|
</ThemedText>
|
|
<ThemedText style={styles.label}>
|
|
{ethBalance !== null ? 'ETH' : 'Loading'}
|
|
</ThemedText>
|
|
</ThemedView>
|
|
<ThemedView style={styles.balance}>
|
|
<ThemedText style={styles.value}>
|
|
{usdtBalance !== null ? `${parseFloat(usdtBalance).toFixed(3)}` : 'Loading'}
|
|
</ThemedText>
|
|
<ThemedText style={styles.label}>
|
|
{usdtBalance !== null ? 'USDT' : 'Loading'}
|
|
</ThemedText>
|
|
</ThemedView>
|
|
</ThemedView>
|
|
|
|
)}
|
|
</ThemedView>
|
|
<ThemedView style={styles.addressRow}>
|
|
<Pressable
|
|
onPress={copyWalletAddress}
|
|
>
|
|
<ThemedText style={styles.address}>{activeAddress}</ThemedText>
|
|
|
|
{/* <FontAwesome5 name="copy" size={15} color="white" /> */}
|
|
</Pressable>
|
|
</ThemedView></ThemedView> </>
|
|
) : (
|
|
<></>
|
|
)}
|
|
|
|
|
|
{/* Link to Scanner */}
|
|
{isPermissionGranted ? (isConnected || firebaseUser != null) && (
|
|
<Link href="./scanner" asChild>
|
|
<Pressable
|
|
style={({ pressed }) => [
|
|
styles.scanButton,
|
|
{ opacity: pressed ? 0.5 : 1 },
|
|
]}
|
|
>
|
|
<Image
|
|
source={require('@/assets/images/qr-code-icon.png')}
|
|
style={styles.qrIcon}
|
|
/>
|
|
<ThemedText style={styles.scanButtonText}>Scan</ThemedText>
|
|
</Pressable>
|
|
</Link>
|
|
) : (
|
|
<Pressable onPress={requestPermission} style={styles.button}>
|
|
<ThemedText style={styles.buttonText}>Grant Permission</ThemedText>
|
|
</Pressable>
|
|
)}
|
|
|
|
{/* Connect Wallet Section */}
|
|
{
|
|
firebaseUser == null ?
|
|
(<Pressable onPress={connectWallet} style={styles.button}>
|
|
<ThemedText style={styles.buttonText}>
|
|
{loading ? 'Connecting...' : isConnected ? 'Disconnect Wallet' : 'Connect Wallet'}
|
|
</ThemedText>
|
|
</Pressable>) : (<></>)}
|
|
|
|
|
|
{
|
|
(firebaseUser == null && !isConnected) ?
|
|
(<GoogleSigninButton
|
|
size={GoogleSigninButton.Size.Standard}
|
|
color={GoogleSigninButton.Color.Dark}
|
|
onPress={_signIn}
|
|
accessibilityLabel={'sign in'}
|
|
/>)
|
|
:
|
|
|
|
(!isConnected) ?
|
|
(
|
|
<ThemedView style={styles.addressContainer}>
|
|
<ThemedText>
|
|
Signed in as {firebaseUser?.user.email}
|
|
</ThemedText>
|
|
<ThemedText></ThemedText>
|
|
|
|
<Pressable onPress={handleLogout} style={styles.logoutButton}>
|
|
<ThemedText style={styles.buttonText}>Logout</ThemedText>
|
|
</Pressable>
|
|
|
|
</ThemedView>
|
|
)
|
|
:
|
|
(<></>)
|
|
}
|
|
|
|
|
|
|
|
{/* Transfer ETH Button
|
|
<Pressable onPress={transferEth} style={styles.transferButton} disabled={transferLoading}>
|
|
<ThemedText style={styles.buttonText}>
|
|
{transferLoading ? 'Transferring...' : 'Transfer 0.003 ETH'}
|
|
</ThemedText>
|
|
</Pressable>
|
|
|
|
|
|
<ThemedView style={styles.scannedDataContainer}>
|
|
<ThemedText>Scanned Ethereum Address:</ThemedText>
|
|
<ThemedText>{''}</ThemedText>
|
|
</ThemedView> */}
|
|
|
|
{/* WalletConnect Modal */}
|
|
<WalletConnectModal projectId={projectId} providerMetadata={providerMetadata} />
|
|
</ParallaxScrollView>
|
|
</>
|
|
);
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
titleContainer: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
gap: 8,
|
|
marginVertical: 16,
|
|
},
|
|
reactLogo: {
|
|
height: 178,
|
|
width: 290,
|
|
bottom: 0,
|
|
left: 0,
|
|
position: 'absolute',
|
|
},
|
|
button: {
|
|
backgroundColor: '#007bff',
|
|
padding: 12,
|
|
borderRadius: 8,
|
|
alignItems: 'center',
|
|
marginVertical: 8,
|
|
},
|
|
transferButton: {
|
|
backgroundColor: '#28a745',
|
|
padding: 12,
|
|
borderRadius: 8,
|
|
alignItems: 'center',
|
|
marginVertical: 8,
|
|
},
|
|
buttonText: {
|
|
color: '#fff',
|
|
fontWeight: 'bold',
|
|
},
|
|
addressContainer: {
|
|
marginVertical: 16,
|
|
padding: 12,
|
|
backgroundColor: '#333333',
|
|
borderRadius: 8,
|
|
},
|
|
scanButton: {
|
|
width: 150,
|
|
height: 150,
|
|
backgroundColor: '#007bff',
|
|
justifyContent: 'center',
|
|
alignItems: 'center',
|
|
borderRadius: 16,
|
|
marginVertical: 16,
|
|
},
|
|
qrIcon: {
|
|
width: 60,
|
|
height: 60,
|
|
marginBottom: 8,
|
|
},
|
|
scanButtonText: {
|
|
color: '#fff',
|
|
fontWeight: 'bold',
|
|
fontSize: 16,
|
|
},
|
|
scannedDataContainer: {
|
|
marginTop: 16,
|
|
padding: 12,
|
|
backgroundColor: "#444",
|
|
borderRadius: 8,
|
|
},
|
|
logoutButton: {
|
|
backgroundColor: '#d9534f',
|
|
padding: 12,
|
|
borderRadius: 8,
|
|
alignItems: 'center',
|
|
},
|
|
title: {
|
|
fontSize: 18,
|
|
fontWeight: 'bold',
|
|
marginBottom: 10,
|
|
},
|
|
address: {
|
|
fontSize: 14,
|
|
color: '#888',
|
|
marginBottom: 20,
|
|
textAlign: 'center',
|
|
},
|
|
addressRow: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
justifyContent: 'space-between',
|
|
},
|
|
container: {
|
|
flex: 1,
|
|
justifyContent: 'center',
|
|
alignItems: 'center',
|
|
},
|
|
balancesContainer: {
|
|
flexDirection: 'row',
|
|
paddingVertical:20
|
|
},
|
|
balance: {
|
|
alignItems: 'center',
|
|
marginHorizontal: 20, // Space between the two balance views
|
|
},
|
|
value: {
|
|
fontSize: 25, // Bigger font for the balance value
|
|
fontWeight: 'bold',
|
|
},
|
|
label: {
|
|
fontSize: 14, // Smaller font for ETH/USDT labels
|
|
color: 'gray',
|
|
},
|
|
});
|