Initial commit
This commit is contained in:
81
app/pages/home/HomePage.tsx
Normal file
81
app/pages/home/HomePage.tsx
Normal file
@@ -0,0 +1,81 @@
|
||||
import { useState } from "react";
|
||||
import { toast } from "react-toastify";
|
||||
import { formatEther, isHash, type Hash } from "viem";
|
||||
import { useTransaction } from "wagmi";
|
||||
|
||||
|
||||
export function HomePage() {
|
||||
const [inputHash, setInputHash] = useState('');
|
||||
const [confirmedHash, setConfirmedHash] = useState<Hash | undefined>(undefined);
|
||||
|
||||
const { data: transaction, isPending, error } = useTransaction({
|
||||
hash: confirmedHash,
|
||||
query: {
|
||||
retry: 1
|
||||
}
|
||||
})
|
||||
|
||||
function onSubmit(e) {
|
||||
e.preventDefault();
|
||||
if (isHash(inputHash)) {
|
||||
setConfirmedHash(inputHash);
|
||||
} else {
|
||||
toast.error('Enter a valid transaction hash!');
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-10">
|
||||
<div className="bg-white p-5 rounded-xl border border-slate-200 shadow-sm flex flex-col gap-6">
|
||||
<h1 className="text-lg font-bold">Transaction Explorer</h1>
|
||||
<p>Enter a transaction hash to get more details...</p>
|
||||
|
||||
<form className="flex flex-row gap-5 w-full" onSubmit={onSubmit}>
|
||||
<input type="text" value={inputHash} onChange={(e) => {
|
||||
setInputHash(e.target.value);
|
||||
if (confirmedHash) {
|
||||
setConfirmedHash(undefined);
|
||||
}
|
||||
}} className="w-full border border-gray-300 p-2 rounded-lg" placeholder="Enter hash..." />
|
||||
<button
|
||||
type="submit"
|
||||
disabled={!inputHash}
|
||||
className="bg-black text-white p-2 px-6 rounded-xl cursor-pointer disabled:opacity-50"
|
||||
>
|
||||
Run
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{!!confirmedHash && (
|
||||
<div className="bg-white p-0 rounded-xl overflow-hidden border border-slate-200 shadown-sm flex flex-col gap-6">
|
||||
<div className="bg-slate-100 px-6 py-4">
|
||||
<h2 className="text-lg font-bold">Details</h2>
|
||||
</div>
|
||||
|
||||
<div className="p-6 pt-0">
|
||||
{error && <p>An error occurred. Please enter a valid hash.</p>}
|
||||
{isPending && <p>Loading details...</p>}
|
||||
|
||||
{!!transaction && (
|
||||
<div className="grid grid-cols-6 w-full gap-y-6">
|
||||
|
||||
<div className="opacity-50">Hash</div>
|
||||
<div className="col-span-5 break-all">{transaction.hash}</div>
|
||||
|
||||
<div className="opacity-50">From</div>
|
||||
<div className="col-span-5">{transaction.from}</div>
|
||||
|
||||
<div className="opacity-50">To</div>
|
||||
<div className="col-span-5">{transaction.to || 'N/A'}</div>
|
||||
|
||||
<div className="opacity-50">Value</div>
|
||||
<div className="col-span-5">{formatEther(transaction.value)} ETH</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
79
app/pages/status/StatusPage.tsx
Normal file
79
app/pages/status/StatusPage.tsx
Normal file
@@ -0,0 +1,79 @@
|
||||
import { useEffect } from "react";
|
||||
import { formatEther, type Address } from "viem";
|
||||
import { useBalance, useBlockNumber, useConnect, useConnection, useConnectors, useDisconnect } from "wagmi";
|
||||
|
||||
function WalletDetails({ address }: { address: Address }) {
|
||||
const { data: blockNumber } = useBlockNumber({ watch: true });
|
||||
const { data: balance, isPending, error, refetch: refetchBalance } = useBalance({ address });
|
||||
const { mutate: disconnect } = useDisconnect();
|
||||
|
||||
useEffect(() => {
|
||||
refetchBalance();
|
||||
}, [blockNumber]);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-6">
|
||||
<h1 className="text-lg font-bold">You are connected</h1>
|
||||
|
||||
<p className="break-all">Your address is: {address}</p>
|
||||
|
||||
{isPending && <p>Loading balance...</p>}
|
||||
{error && <p>An error occurred.</p>}
|
||||
|
||||
{blockNumber && <p>Block number: {blockNumber.toLocaleString()}</p>}
|
||||
{balance && <p>Balance: {formatEther(balance.value)} ETH</p>}
|
||||
|
||||
<div className="flex flex-row">
|
||||
<button
|
||||
className="bg-black text-white py-2 px-6 rounded-xl cursor-pointer"
|
||||
onClick={() => disconnect()}
|
||||
>
|
||||
Disconnect
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
|
||||
function ConnectWallet() {
|
||||
const connectors = useConnectors();
|
||||
const { mutate: connect } = useConnect();
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-6">
|
||||
<h1 className="text-lg font-bold">Connect your wallet</h1>
|
||||
|
||||
<p>Choose an option from the list below:</p>
|
||||
<div className="flex flex-row gap-6">
|
||||
{connectors.map(connector => (
|
||||
<button
|
||||
key={connector.id}
|
||||
onClick={() => connect({ connector })}
|
||||
className="bg-black text-white px-6 py-2 rounded-lg cursor-pointer"
|
||||
>
|
||||
Connect {connector.name}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
export function StatusPage() {
|
||||
const { address, isConnected } = useConnection();
|
||||
|
||||
return (
|
||||
<div className="bg-white p-5 border border-slate-200 shadow-sm rounded-xl flex flex-col gap-6">
|
||||
{isConnected && address ? (
|
||||
<WalletDetails address={address} />
|
||||
) : (
|
||||
<ConnectWallet />
|
||||
)}
|
||||
|
||||
</div>
|
||||
);
|
||||
}
|
||||
85
app/pages/transfer/TransferPage.tsx
Normal file
85
app/pages/transfer/TransferPage.tsx
Normal file
@@ -0,0 +1,85 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { useNavigate } from "react-router";
|
||||
import { toast } from "react-toastify";
|
||||
import { isAddress, parseEther } from "viem";
|
||||
import { useConnection, useSendTransaction, useWaitForTransactionReceipt } from "wagmi";
|
||||
|
||||
|
||||
export function TransferPage() {
|
||||
const { isConnected } = useConnection();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const [inputAddress, setInputAddress] = useState('');
|
||||
const [inputValue, setInputValue] = useState('');
|
||||
|
||||
const { data: transactionHash, isPending: isSendingTransaction, error, mutate: sendTransaction } = useSendTransaction();
|
||||
const { data: receipt, isPending, isFetching } = useWaitForTransactionReceipt({
|
||||
hash: transactionHash,
|
||||
query: {
|
||||
// not stricly needed, because if transactionHash is not defined,
|
||||
// in current versions of wagmi it defaults to disabled
|
||||
// (but, it's good to know so you can control dependent queries)
|
||||
enabled: !!transactionHash
|
||||
}
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (!isConnected) {
|
||||
navigate('/status');
|
||||
}
|
||||
}, [isConnected, navigate]);
|
||||
|
||||
function onSubmit(e) {
|
||||
e.preventDefault();
|
||||
|
||||
if (!isAddress(inputAddress)) {
|
||||
return toast.error('Invalid address.');
|
||||
}
|
||||
|
||||
if (!inputValue || isNaN(Number(inputValue)) || Number(inputValue) <= 0) {
|
||||
return toast.error('Invalid value.');
|
||||
}
|
||||
|
||||
sendTransaction({
|
||||
to: inputAddress,
|
||||
value: parseEther(inputValue)
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="bg-white border border-slate-200 rounded-xl p-5 flex flex-col gap-6 shadow-sm">
|
||||
<h1 className="text-lg font-bold">Transfer ETH</h1>
|
||||
|
||||
<form onSubmit={onSubmit} className="flex flex-col gap-6">
|
||||
<input
|
||||
type="text"
|
||||
className="border border-gray-300 p-2 rounded-lg"
|
||||
placeholder="Enter address."
|
||||
value={inputAddress}
|
||||
onChange={e => setInputAddress(e.target.value)}
|
||||
/>
|
||||
<input
|
||||
type="text"
|
||||
className="border border-gray-300 p-2 rounded-lg"
|
||||
placeholder="Enter value."
|
||||
value={inputValue}
|
||||
onChange={e => setInputValue(e.target.value)}
|
||||
/>
|
||||
|
||||
<div className="flex flex-row">
|
||||
<button
|
||||
type="submit"
|
||||
disabled={!inputAddress || !inputValue}
|
||||
className="bg-black text-white px-5 py-2 cursor-pointer rounded-xl disabled:opacity-50"
|
||||
>
|
||||
{isSendingTransaction ? 'Sending...' : 'Send'}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{error && <p>An error occurred. Please check the address and the value.</p>}
|
||||
{(isPending && isFetching) && <p>Confirming...</p>}
|
||||
{receipt && <p>Status: {receipt.status}</p>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user