diff --git a/session-2/src/App.tsx b/session-2/src/App.tsx index 73b90a2..aa43582 100644 --- a/session-2/src/App.tsx +++ b/session-2/src/App.tsx @@ -1,4 +1,3 @@ -import React from 'react'; import './App.css'; import WalletContextProvider from "./components/WalletContextProvider"; import {AppBar} from "./components/AppBar/AppBar"; diff --git a/session-2/src/components/BalanceCard/BalanceCard.tsx b/session-2/src/components/BalanceCard/BalanceCard.tsx index 716f683..280695e 100644 --- a/session-2/src/components/BalanceCard/BalanceCard.tsx +++ b/session-2/src/components/BalanceCard/BalanceCard.tsx @@ -1,60 +1,65 @@ -import { FC, useState, useEffect, useCallback } from 'react'; -import { useWallet } from '@solana/wallet-adapter-react'; -import { Connection, LAMPORTS_PER_SOL, PublicKey } from '@solana/web3.js'; +import { FC, useState, useEffect, useCallback } from "react"; +import { useWallet } from "@solana/wallet-adapter-react"; +import { Connection, LAMPORTS_PER_SOL } from "@solana/web3.js"; import { SOLANA_TESTNET_URL } from "../../const"; -import './BalanceCard.css'; +import "./BalanceCard.css"; +// Create a connection to the cluster const connection = new Connection(SOLANA_TESTNET_URL, "confirmed"); export const BalanceCard: FC = () => { - const { publicKey, connected } = useWallet(); - const [balance, setBalance] = useState(null); - const [loading, setLoading] = useState(true); - const [error, setError] = useState(null); + const { publicKey, connected } = useWallet(); + const [balance, setBalance] = useState(null); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); - const fetchBalance = useCallback(async () => { - if (!publicKey) { - console.log("No public key"); - return; - } - setLoading(true); - setError(null); - try { - const balanceInLamports = await connection.getBalance(publicKey); - const balanceInSOL = balanceInLamports / LAMPORTS_PER_SOL; - console.log("Public Key:", publicKey.toBase58()); - console.log("Balance in SOL:", balanceInSOL); - setBalance(balanceInSOL); - } catch (err) { - console.error("Error fetching balance:", err); - setError("Failed to fetch balance"); - } - setLoading(false); - }, [publicKey]); + const fetchBalance = useCallback(async () => { + if (!publicKey) { + console.log("No public key"); + return; + } + setLoading(true); + setError(null); + try { + // Fetch the balance of the connected wallet + const balanceInLamports = await connection.getBalance(publicKey); - useEffect(() => { - if (connected) { - fetchBalance(); - } - }, [connected, fetchBalance]); + // Convert the balance to SOL + const balanceInSOL = balanceInLamports / LAMPORTS_PER_SOL; - return ( -
-
-

Wallet Balance

-
-
- {connected ? ( -

{loading ? "Loading..." : `${balance ?? 0} SOL`}

- ) : ( -

Please Connect Wallet

- )} - {connected && ( - - )} -
-
- ); -}; \ No newline at end of file + console.log("Public Key:", publicKey.toBase58()); + console.log("Balance in SOL:", balanceInSOL); + setBalance(balanceInSOL); + } catch (err) { + console.error("Error fetching balance:", err); + setError("Failed to fetch balance"); + } + setLoading(false); + }, [publicKey]); + + useEffect(() => { + if (connected) { + fetchBalance(); + } + }, [connected, fetchBalance]); + + return ( +
+
+

Wallet Balance

+
+
+ {connected ? ( +

{loading ? "Loading..." : `${balance ?? 0} SOL`}

+ ) : ( +

Please Connect Wallet

+ )} + {connected && ( + + )} +
+
+ ); +}; diff --git a/session-2/src/components/History/History.tsx b/session-2/src/components/History/History.tsx index 1d5eb39..7e4a1ac 100644 --- a/session-2/src/components/History/History.tsx +++ b/session-2/src/components/History/History.tsx @@ -1,122 +1,146 @@ -import { FC, useState, useEffect, useCallback } from 'react'; -import { useWallet } from '@solana/wallet-adapter-react'; -import { ConfirmedSignatureInfo, Connection, PublicKey } from '@solana/web3.js'; +import { FC, useState, useEffect, useCallback } from "react"; +import { useWallet } from "@solana/wallet-adapter-react"; +import { ConfirmedSignatureInfo, Connection } from "@solana/web3.js"; import { SOLANA_TESTNET_URL, SOLANA_DEVNET_URL } from "../../const"; // @ts-ignore -import * as timeago from 'timeago.js'; -import './History.css'; +import * as timeago from "timeago.js"; +import "./History.css"; const connection = new Connection(SOLANA_TESTNET_URL, "confirmed"); const shortenSignature = (signature: string) => { - return `${signature.slice(0, 7)}.........${signature.slice(-7)}`; + return `${signature.slice(0, 7)}.........${signature.slice(-7)}`; }; interface Transaction { - signature: string; - slot: number; - blockTime: number; - success: boolean; + signature: string; + slot: number; + blockTime: number; + success: boolean; } export const History: FC = () => { - const { publicKey, connected } = useWallet(); - const [transactions, setTransactions] = useState([]); - const [loading, setLoading] = useState(true); + const { publicKey, connected } = useWallet(); + const [transactions, setTransactions] = useState([]); + const [loading, setLoading] = useState(true); - const fetchTransactionHistory = useCallback(async () => { - if (!publicKey) return; - setLoading(true); - try { - const confirmedSignatures = await connection.getSignaturesForAddress(publicKey, { limit: 10 }); - const confirmedTransactions = await Promise.all( - confirmedSignatures.map(async (signatureInfo: ConfirmedSignatureInfo) => { - const transaction = await connection.getTransaction(signatureInfo.signature); - return { - signature: signatureInfo.signature, - slot: transaction!.slot, - blockTime: transaction!.blockTime!, - success: transaction!.meta!.err === null, - }; - }) + const fetchTransactionHistory = useCallback(async () => { + if (!publicKey) return; + setLoading(true); + try { + // Fetch the transaction signatures for the connected wallet + const confirmedSignatures = await connection.getSignaturesForAddress( + publicKey, + { limit: 10 } + ); + + // Fetch the transaction details for each signature + const confirmedTransactions = await Promise.all( + confirmedSignatures.map( + async (signatureInfo: ConfirmedSignatureInfo) => { + const transaction = await connection.getTransaction( + signatureInfo.signature ); - setTransactions(confirmedTransactions); - } catch (error) { - console.error('Error fetching transactions:', error); - } - setLoading(false); - }, [publicKey]); + return { + signature: signatureInfo.signature, + slot: transaction!.slot, + blockTime: transaction!.blockTime!, + success: transaction!.meta!.err === null, + }; + } + ) + ); + setTransactions(confirmedTransactions); + } catch (error) { + console.error("Error fetching transactions:", error); + } + setLoading(false); + }, [publicKey]); - useEffect(() => { - if (connected) { - fetchTransactionHistory(); - } else { - setTransactions([]); - setLoading(false); - } - }, [connected, fetchTransactionHistory]); + useEffect(() => { + if (connected) { + fetchTransactionHistory(); + } else { + setTransactions([]); + setLoading(false); + } + }, [connected, fetchTransactionHistory]); - const copyToClipboard = (signature: string) => { - navigator.clipboard.writeText(signature) - .then(() => { - // alert("Signature copied to clipboard!"); - }) - .catch(err => { - console.error('Failed to copy signature: ', err); - }); - }; + const copyToClipboard = (signature: string) => { + navigator.clipboard + .writeText(signature) + .then(() => { + // alert("Signature copied to clipboard!"); + }) + .catch((err) => { + console.error("Failed to copy signature: ", err); + }); + }; - return ( -
-
-

Transaction History

- {connected && ( - - )} -
-
- {loading ? ( -

Loading...

- ) : ( - - - - - - - - - - - - {transactions.length === 0 ? ( - - - - ) : ( - transactions.map((transaction, index) => ( - - - - - - - - )) - )} - -
Transaction SignatureBlockTimestampAgoResult
No transactions found
copyToClipboard(transaction.signature)} className="pointer-cursor"> - - {shortenSignature(transaction.signature)} - {transaction.slot}{new Date(transaction.blockTime * 1000).toString().replace('GMT+0530 (India Standard Time)', '')}{timeago.format(new Date(transaction.blockTime * 1000))} - - {transaction.success ? 'Success' : 'Failed'} - -
- )} -
-
- ); -}; \ No newline at end of file + return ( +
+
+

Transaction History

+ {connected && ( + + )} +
+
+ {loading ? ( +

Loading...

+ ) : ( + + + + + + + + + + + + {transactions.length === 0 ? ( + + + + ) : ( + transactions.map((transaction, index) => ( + + + + + + + + )) + )} + +
Transaction SignatureBlockTimestampAgoResult
+ No transactions found +
copyToClipboard(transaction.signature)} + className="pointer-cursor" + > + + {shortenSignature(transaction.signature)} + {transaction.slot} + {new Date(transaction.blockTime * 1000) + .toString() + .replace("GMT+0530 (India Standard Time)", "")} + + {timeago.format(new Date(transaction.blockTime * 1000))} + + + {transaction.success ? "Success" : "Failed"} + +
+ )} +
+
+ ); +}; diff --git a/session-2/src/components/SendSol/SendSol.tsx b/session-2/src/components/SendSol/SendSol.tsx index 4521bc0..06927b4 100644 --- a/session-2/src/components/SendSol/SendSol.tsx +++ b/session-2/src/components/SendSol/SendSol.tsx @@ -1,108 +1,135 @@ -import { FC, useState, useCallback } from 'react'; -import { useWallet } from '@solana/wallet-adapter-react'; -import { Connection, PublicKey, Transaction, SystemProgram, LAMPORTS_PER_SOL } from '@solana/web3.js'; +import { FC, useState, useCallback } from "react"; +import { useWallet } from "@solana/wallet-adapter-react"; +import { + Connection, + PublicKey, + Transaction, + SystemProgram, + LAMPORTS_PER_SOL, +} from "@solana/web3.js"; import { SOLANA_TESTNET_URL } from "../../const"; -import './SendSol.css'; +import "./SendSol.css"; const connection = new Connection(SOLANA_TESTNET_URL, "confirmed"); export const SendSol: FC = () => { - const { publicKey, sendTransaction, connected } = useWallet(); - const [recipientAddress, setRecipientAddress] = useState(''); - const [amount, setAmount] = useState(''); - const [loading, setLoading] = useState(false); - const [errors, setErrors] = useState<{ address?: string; amount?: string }>({}); - - const validateAddress = (address: string): boolean => { - try { - new PublicKey(address); - return true; - } catch { - return false; - } - }; - - const handleSendTransaction = useCallback(async () => { - const newErrors: { address?: string; amount?: string } = {}; - - if (!recipientAddress || !validateAddress(recipientAddress)) { - newErrors.address = 'Invalid wallet address'; - } - - const lamports = Math.round(parseFloat(amount) * LAMPORTS_PER_SOL); - - if (!amount || isNaN(lamports) || lamports <= 0) { - newErrors.amount = 'Invalid amount'; - } - - setErrors(newErrors); - - if (Object.keys(newErrors).length > 0) return; - - if (!publicKey) return; - - const recipientPublicKey = new PublicKey(recipientAddress); - - setLoading(true); - - try { - const balance = await connection.getBalance(publicKey); - if (balance < lamports) { - setErrors({ amount: 'Insufficient balance' }); - return; - } - - const transaction = new Transaction().add( - SystemProgram.transfer({ - fromPubkey: publicKey, - toPubkey: recipientPublicKey, - lamports: lamports, - }) - ); - - const signature = await sendTransaction(transaction, connection); - await connection.confirmTransaction(signature, 'confirmed'); - alert(`Transaction successful! Signature: ${signature}`); - setRecipientAddress(''); - setAmount(''); - } catch (error) { - console.error('Error sending transaction:', error); - alert('Transaction failed!'); - } finally { - setLoading(false); - } - }, [publicKey, recipientAddress, amount, sendTransaction]); - - return ( -
-
-

Quick Transfer

-
-
- - setRecipientAddress(e.target.value)} - /> - {errors.address && {errors.address}} - - setAmount(e.target.value)} - /> - {errors.amount && {errors.amount}} - -
-
- ); + const { publicKey, sendTransaction, connected } = useWallet(); + const [recipientAddress, setRecipientAddress] = useState(""); + const [amount, setAmount] = useState(""); + const [loading, setLoading] = useState(false); + const [errors, setErrors] = useState<{ address?: string; amount?: string }>( + {} + ); + + const validateAddress = (address: string): boolean => { + try { + new PublicKey(address); + return true; + } catch { + return false; + } + }; + + const handleSendTransaction = useCallback(async () => { + const newErrors: { address?: string; amount?: string } = {}; + + if (!recipientAddress || !validateAddress(recipientAddress)) { + newErrors.address = "Invalid wallet address"; + } + + const lamports = Math.round(parseFloat(amount) * LAMPORTS_PER_SOL); + + if (!amount || isNaN(lamports) || lamports <= 0) { + newErrors.amount = "Invalid amount"; + } + + setErrors(newErrors); + + if (Object.keys(newErrors).length > 0) return; + + if (!publicKey) return; + + const recipientPublicKey = new PublicKey(recipientAddress); + + setLoading(true); + + try { + const balance = await connection.getBalance(publicKey); + if (balance < lamports) { + setErrors({ amount: "Insufficient balance" }); + return; + } + + // creating a new instance of transaction + const transaction = new Transaction(); + + // creating the transaction instructions + const transactionInstructions = SystemProgram.transfer({ + fromPubkey: publicKey, + toPubkey: recipientPublicKey, + lamports: lamports, + }); + + // adding the transaction instructions to the transaction + transaction.add(transactionInstructions); + + // signing and sending the transaction + const signature = await sendTransaction(transaction, connection); + await connection.confirmTransaction(signature, "confirmed"); + + alert(`Transaction successful! Signature: ${signature}`); + setRecipientAddress(""); + setAmount(""); + } catch (error) { + console.error("Error sending transaction:", error); + alert("Transaction failed!"); + } finally { + setLoading(false); + } + }, [publicKey, recipientAddress, amount, sendTransaction]); + + return ( +
+
+

Quick Transfer

+
+
+ + setRecipientAddress(e.target.value)} + /> + {errors.address && ( + {errors.address} + )} + + setAmount(e.target.value)} + /> + {errors.amount && ( + {errors.amount} + )} + +
+
+ ); }; diff --git a/session-2/src/components/WalletContextProvider.tsx b/session-2/src/components/WalletContextProvider.tsx index 9457ba0..088b9e0 100644 --- a/session-2/src/components/WalletContextProvider.tsx +++ b/session-2/src/components/WalletContextProvider.tsx @@ -1,23 +1,31 @@ -import { FC, ReactNode } from 'react'; -import { ConnectionProvider, WalletProvider } from '@solana/wallet-adapter-react' +import { FC, ReactNode } from "react"; +import { + ConnectionProvider, + WalletProvider, +} from "@solana/wallet-adapter-react"; import { WalletModalProvider } from "@solana/wallet-adapter-react-ui"; -import * as web3 from '@solana/web3.js' -import * as walletAdapterWallets from '@solana/wallet-adapter-wallets'; -require('@solana/wallet-adapter-react-ui/styles.css'); +import * as web3 from "@solana/web3.js"; +import * as walletAdapterWallets from "@solana/wallet-adapter-wallets"; +require("@solana/wallet-adapter-react-ui/styles.css"); const WalletContextProvider: FC<{ children: ReactNode }> = ({ children }) => { - const endpoint = web3.clusterApiUrl('testnet') - const wallets = [new walletAdapterWallets.PhantomWalletAdapter()] + // endpoint of the cluster to connect to + const endpoint = web3.clusterApiUrl("testnet"); - return ( - - - - { children } - - - - ) -} + // list of wallets to be used + const wallets = [ + new walletAdapterWallets.PhantomWalletAdapter(), + new walletAdapterWallets.CoinbaseWalletAdapter(), + new walletAdapterWallets.SolflareWalletAdapter(), + ]; -export default WalletContextProvider \ No newline at end of file + return ( + + + {children} + + + ); +}; + +export default WalletContextProvider;