Embedded wallets done

This commit is contained in:
Sewmina 2024-12-31 10:29:02 +05:30
parent 0289ec4600
commit 3cfc3c5792
6 changed files with 303 additions and 76 deletions

View File

@ -1,5 +1,5 @@
import React, { useEffect, useState } from 'react';
import { Image, StyleSheet, Pressable, Platform, View, Alert } from 'react-native';
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';
@ -12,9 +12,10 @@ import { CHAIN_ID, CONTRACT_ADDRESS, projectId, providerMetadata } from './share
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 from '@react-native-firebase/auth';
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);
@ -25,6 +26,37 @@ export default function HomeScreen() {
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);
@ -40,8 +72,8 @@ export default function HomeScreen() {
if (userInfo != null) {
console.log("Setting firebase user silently");
setFirebaseUser(userInfo.data);
console.log(firebaseUser);
fetchEmbeddedWallet();
}
} catch (error) {
console.error('Failed to check Google sign-in status:', error);
@ -54,22 +86,21 @@ export default function HomeScreen() {
}, [])
useEffect(() => {
const fetchEmbeddedWallet = async () => {
if (firebaseUser == null) { console.log('firebase user is null'); return; }
const googleCredential = auth.GoogleAuthProvider.credential(firebaseUser.idToken);
const userCredential = await auth().signInWithCredential(googleCredential);
const tokenId = await userCredential.user.getIdToken();
console.log(`tokenId from firebase ${tokenId}`);
const register = await fetch(`http://vps.playpoolstudios.com:30017/signinWithGoogle?tokenId=${tokenId}`)
const registerJson = await register.json();
console.log(registerJson);
setEmbeddedWallet(registerJson['publicKey']);
}
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 {
@ -134,6 +165,8 @@ export default function HomeScreen() {
if (type === 'success') {
console.log({ data });
setFirebaseUser(data);
fetchEmbeddedWallet();
} else {
// sign in was cancelled by user
setTimeout(() => {
@ -173,6 +206,15 @@ export default function HomeScreen() {
}
}
const copyWalletAddress = () => {
Clipboard.setString(activeAddress ?? "");
if (Platform.OS === 'android') {
ToastAndroid.show('Wallet address copied!', ToastAndroid.SHORT);
} else {
alert('Wallet address copied!');
}
};
return (
<>
<ParallaxScrollView
@ -188,54 +230,50 @@ export default function HomeScreen() {
<ThemedText type="title">Welcome to ECPay</ThemedText>
</ThemedView>
{/* Connect Wallet Section */}
{
firebaseUser == null ?
(<Pressable onPress={connectWallet} style={styles.button}>
<ThemedText style={styles.buttonText}>
{loading ? 'Connecting...' : isConnected ? 'Disconnect Wallet' : 'Connect Wallet'}
</ThemedText>
</Pressable>) : (<></>)}
{/* Display Wallet Address if Connected */}
{isConnected && (
<ThemedView style={styles.addressContainer}>
<ThemedText>Connected Wallet:</ThemedText>
<ThemedText>{address}</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> </>
) : (
<></>
)}
{
(firebaseUser == null && !isConnected) ?
(<GoogleSigninButton
size={GoogleSigninButton.Size.Standard}
color={GoogleSigninButton.Color.Dark}
onPress={_signIn}
accessibilityLabel={'sign in'}
/>)
:
(!isConnected) ?
(
<ThemedView>
<ThemedText>
Signed in as {firebaseUser?.user.email}
</ThemedText>
<ThemedText>
{embeddedWallet}
</ThemedText>
<Pressable onPress={handleLogout} style={styles.logoutButton}>
<ThemedText style={styles.buttonText}>Logout</ThemedText>
</Pressable>
</ThemedView>
)
:
(<></>)
}
{/* Link to Scanner */}
{isPermissionGranted ? (isConnected || firebaseUser != null) && (
{/* Link to Scanner */}
{isPermissionGranted ? (isConnected || firebaseUser != null) && (
<Link href="./scanner" asChild>
<Pressable
style={({ pressed }) => [
@ -256,18 +294,58 @@ export default function HomeScreen() {
</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>
<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> */}
<ThemedView style={styles.scannedDataContainer}>
<ThemedText>Scanned Ethereum Address:</ThemedText>
<ThemedText>{''}</ThemedText>
</ThemedView> */}
{/* WalletConnect Modal */}
<WalletConnectModal projectId={projectId} providerMetadata={providerMetadata} />
@ -345,4 +423,41 @@ const styles = StyleSheet.create({
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',
},
});

View File

@ -8,6 +8,7 @@ import { CHAIN_ID, CONTRACT_ADDRESS, TOKEN_CONTRACT_ADDRESS } from "./shared";
import { useWalletConnectModal } from "@walletconnect/modal-react-native";
import { contractABI } from "./ABI/transfer_contract";
import { erc20ABI } from "./ABI/erc20_contract";
import { GoogleSignin, User } from "@react-native-google-signin/google-signin";
export default function Home() {
const qrLock = useRef(false);
@ -17,6 +18,8 @@ export default function Home() {
const [modalVisible, setModalVisible] = useState(false);
const [scannedAddress, setScannedAddress] = useState("0xE09865aaCd816729C19E4BeA4caFf247704De9fE");
const [dollarValue, setDollarValue] = useState("400");
const [firebaseUser, setFirebaseUser] = useState<User | null>();
const [isLoading, setIsLoading]= useState(false);
useEffect(() => {
const subscription = AppState.addEventListener("change", (nextAppState) => {
@ -26,6 +29,21 @@ export default function Home() {
appState.current = nextAppState;
});
const checkSignInStatus = async () => {
try {
const userInfo = await GoogleSignin.signInSilently();
if (userInfo != null) {
console.log("Setting firebase user silently");
setFirebaseUser(userInfo.data);
}
} catch (error) {
console.error('Failed to check Google sign-in status:', error);
} finally {
}
};
checkSignInStatus();
transferEth();
return () => {
@ -37,9 +55,21 @@ export default function Home() {
const isValidEthAddress = (address:string) => /^0x[a-fA-F0-9]{40}$/.test(address);
const transferEthRemtoe = async () => {
setIsLoading(true);
const response =await fetch(`http://vps.playpoolstudios.com:30117/sendTransaction?tokenId=${firebaseUser?.idToken}&receiver=${scannedAddress}&amount=${dollarValue}`);
console.log(await response.text());
setIsLoading(false);
navigation.goBack();
}
const transferEth = async () => {
if (!provider || !isConnected) {
console.error("Wallet not connected.");
if(firebaseUser!=null){
transferEthRemtoe();
}
return;
}
@ -119,6 +149,17 @@ export default function Home() {
/>
<Overlay />
{/* Loading Modal */}
{isLoading && (
<Modal animationType="fade" transparent={true} visible={isLoading}>
<View style={styles.loadingModalContainer}>
<View style={styles.loadingContent}>
<Text style={styles.loadingText}>Loading...</Text>
</View>
</View>
</Modal>
)}
<Modal
animationType="slide"
transparent={true}
@ -197,5 +238,27 @@ const styles = StyleSheet.create({
},
spacer: {
height: 10,
}, loadingModalContainer: {
flex: 1,
justifyContent: "center",
alignItems: "center",
backgroundColor: "rgba(0,0,0,0.5)",
},
loadingContent: {
width: "70%",
backgroundColor: "white",
padding: 20,
borderRadius: 10,
alignItems: "center",
elevation: 5,
shadowColor: "#000",
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.25,
shadowRadius: 3.84,
},
loadingText: {
fontSize: 18,
fontWeight: "bold",
color: "#333",
},
});

View File

@ -1,5 +1,5 @@
export const CHAIN_ID = 421614; // Arbitrum Sepolia
export const CONTRACT_ADDRESS = '0xD66858fB654b69a1F454F084700dC403B1636184';
export const CONTRACT_ADDRESS = '0x5C38736f3E8d2A4F4Ca742fE0B6c4e22D3dB8917';
export const TOKEN_CONTRACT_ADDRESS = '0x976E7ED682f781F39Fd79BF2966bB54A9CBbDF5F';
export const RECEIPENT_ADDRESS = "0xE09865aaCd816729C19E4BeA4caFf247704De9fE";

View File

@ -0,0 +1,17 @@
import { initializeApp } from '@react-native-firebase/app';
import auth from '@react-native-firebase/auth';
const firebaseConfig = {
apiKey: "AIzaSyCpATgMXv9Pu3coBwljh0uv9I9ciFkBJIA",
authDomain: "sologin-55bbd.firebaseapp.com",
projectId: "sologin-55bbd",
storageBucket: "sologin-55bbd.firebasestorage.app",
messagingSenderId: "652710461284",
appId: "1:652710461284:web:cb86f9afb033a5bcf54471",
measurementId: "G-Q6H1J4E92L",
};
const app = initializeApp(firebaseConfig); // Initialize app.
const authInstance = auth(app); // Access auth instance.
export { app, authInstance };

32
package-lock.json generated
View File

@ -10,6 +10,7 @@
"dependencies": {
"@expo/vector-icons": "^14.0.2",
"@react-native-async-storage/async-storage": "1.23.1",
"@react-native-clipboard/clipboard": "^1.15.0",
"@react-native-community/netinfo": "11.4.1",
"@react-native-firebase/app": "^21.6.1",
"@react-native-firebase/auth": "^21.6.1",
@ -51,7 +52,8 @@
"react-native-screens": "~4.4.0",
"react-native-svg": "15.8.0",
"react-native-web": "~0.19.13",
"react-native-webview": "13.12.5"
"react-native-webview": "13.12.5",
"undefined": "@expo/vector-icons/FontAwesome5"
},
"devDependencies": {
"@babel/core": "^7.25.2",
@ -65,6 +67,7 @@
"typescript": "^5.3.3"
}
},
"@expo/vector-icons/FontAwesome5": {},
"node_modules/@0no-co/graphql.web": {
"version": "1.0.12",
"resolved": "https://registry.npmjs.org/@0no-co/graphql.web/-/graphql.web-1.0.12.tgz",
@ -5354,6 +5357,29 @@
"react-native": "^0.0.0-0 || >=0.60 <1.0"
}
},
"node_modules/@react-native-clipboard/clipboard": {
"version": "1.15.0",
"resolved": "https://registry.npmjs.org/@react-native-clipboard/clipboard/-/clipboard-1.15.0.tgz",
"integrity": "sha512-YDMC3E956jn9zE11uKGcQDKS1SO9q72iNHxZyrKY5y9XYwZcA9vo3Xk74+zRnf7cM48drDO0s9lyAPUlOvyhrw==",
"license": "MIT",
"workspaces": [
"example"
],
"peerDependencies": {
"react": ">= 16.9.0",
"react-native": ">= 0.61.5",
"react-native-macos": ">= 0.61.0",
"react-native-windows": ">= 0.61.0"
},
"peerDependenciesMeta": {
"react-native-macos": {
"optional": true
},
"react-native-windows": {
"optional": true
}
}
},
"node_modules/@react-native-community/cli": {
"version": "15.1.3",
"resolved": "https://registry.npmjs.org/@react-native-community/cli/-/cli-15.1.3.tgz",
@ -22462,6 +22488,10 @@
"integrity": "sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==",
"license": "MIT"
},
"node_modules/undefined": {
"resolved": "@expo/vector-icons/FontAwesome5",
"link": true
},
"node_modules/undici": {
"version": "6.21.0",
"resolved": "https://registry.npmjs.org/undici/-/undici-6.21.0.tgz",

View File

@ -17,6 +17,7 @@
"dependencies": {
"@expo/vector-icons": "^14.0.2",
"@react-native-async-storage/async-storage": "1.23.1",
"@react-native-clipboard/clipboard": "^1.15.0",
"@react-native-community/netinfo": "11.4.1",
"@react-native-firebase/app": "^21.6.1",
"@react-native-firebase/auth": "^21.6.1",
@ -58,7 +59,8 @@
"react-native-screens": "~4.4.0",
"react-native-svg": "15.8.0",
"react-native-web": "~0.19.13",
"react-native-webview": "13.12.5"
"react-native-webview": "13.12.5",
"undefined": "@expo/vector-icons/FontAwesome5"
},
"devDependencies": {
"@babel/core": "^7.25.2",