Files
crypto_clash/crypto_clash_frontend/app/page.tsx
averel10 f10da70c48 feat: automate config.json copying and update dependencies
- Added a script (copy_config.js) to copy config.json from the root directory to the frontend's public directory before starting the development server, building, or starting the application.
- Updated package.json scripts to include the config copying step.
- Updated dependencies in package.json, including adding web3 and ensuring tailwindcss is included in devDependencies.
- Modified tsconfig.json to include a path for the config file.
- Added a new config.json file in the public directory with API_URL, CONTRACT_ADDRESS, and ABI details for the smart contract.
2025-11-11 21:48:56 +01:00

250 lines
8.7 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client";
import { useEffect, useState } from "react";
import Web3 from "web3";
interface Config {
API_URL: string;
CONTRACT_ADDRESS: string;
ABI: any[];
}
export default function Home() {
const [config, setConfig] = useState<Config | null>(null);
const [web3, setWeb3] = useState<Web3 | null>(null);
const [contract, setContract] = useState<any>(null);
const [message, setMessage] = useState<string>("");
const [newMessage, setNewMessage] = useState<string>("");
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string>("");
const [account, setAccount] = useState<string>("");
// Initialize Web3 and contract
useEffect(() => {
const loadConfig = async () => {
try {
const response = await fetch("/crypto_clash/config.json");
const data = await response.json();
setConfig(data);
// Initialize Web3
const web3Instance = new Web3(data.API_URL);
setWeb3(web3Instance);
// Create contract instance
const contractInstance = new web3Instance.eth.Contract(
data.ABI,
data.CONTRACT_ADDRESS
);
setContract(contractInstance);
// Try to get connected account (if MetaMask or similar is available)
if (typeof window !== "undefined" && (window as any).ethereum) {
try {
const accounts = await (window as any).ethereum.request({
method: "eth_requestAccounts",
});
setAccount(accounts[0]);
// Get the network ID from the RPC endpoint
const networkId = await web3Instance.eth.net.getId();
const currentChainId = await (window as any).ethereum.request({
method: "eth_chainId",
});
// If on different network, notify user (they may need to switch networks manually)
console.log(
`RPC Network ID: ${networkId}, MetaMask Chain ID: ${currentChainId}`
);
} catch (err) {
console.log("MetaMask not available or user denied access");
}
}
// Load initial message
await readMessage(contractInstance);
} catch (err) {
setError(`Failed to load config: ${err}`);
console.error(err);
}
};
loadConfig();
}, []);
// Read message from contract
const readMessage = async (contractInstance: any) => {
try {
setLoading(true);
setError("");
const result = await contractInstance.methods.message().call();
setMessage(result);
} catch (err) {
setError(`Failed to read message: ${err}`);
console.error(err);
} finally {
setLoading(false);
}
};
// Update message on contract
const updateMessage = async () => {
if (!newMessage.trim()) {
setError("Please enter a message");
return;
}
try {
setLoading(true);
setError("");
if (!web3 || !contract) {
throw new Error("Web3 or contract not initialized");
}
// Check if MetaMask is available
if (!account && typeof window !== "undefined" && !(window as any).ethereum) {
throw new Error(
"MetaMask not available. Please install MetaMask to update the message."
);
}
// Create transaction
const tx = contract.methods.update(newMessage);
const gas = await tx.estimateGas({ from: account });
console.log(await web3.eth.net.getId());
// Send transaction via MetaMask
const result = await (window as any).ethereum.request({
method: "eth_sendTransaction",
params: [
{
from: account,
to: config?.CONTRACT_ADDRESS,
data: tx.encodeABI(),
gas: web3.utils.toHex(gas),
chainId: web3.utils.toHex(await web3.eth.net.getId()),
},
],
});
setError(`Transaction sent: ${result}`);
setNewMessage("");
// Wait a bit and refresh message from the RPC endpoint
setTimeout(() => {
if (contract) {
readMessage(contract);
}
}, 2000);
} catch (err) {
setError(`Failed to update message: ${err}`);
console.error(err);
} finally {
setLoading(false);
}
};
return (
<div className="flex min-h-screen items-center justify-center bg-gradient-to-br from-blue-50 to-indigo-100 dark:from-slate-900 dark:to-slate-800 font-sans">
<main className="w-full max-w-2xl mx-auto py-12 px-6">
<div className="bg-white dark:bg-slate-800 rounded-lg shadow-lg p-8">
<h1 className="text-4xl font-bold text-center mb-2 text-slate-900 dark:text-white">
Smart Contract Interaction
</h1>
<p className="text-center text-slate-600 dark:text-slate-300 mb-8">
Read and update messages on the blockchain
</p>
{/* Status Section */}
<div className="mb-8 p-4 bg-slate-100 dark:bg-slate-700 rounded-lg">
<p className="text-sm text-slate-600 dark:text-slate-300">
<span className="font-semibold">Connected Account:</span>{" "}
{account ? `${account.slice(0, 6)}...${account.slice(-4)}` : "Not connected"}
</p>
<p className="text-sm text-slate-600 dark:text-slate-300 mt-2">
<span className="font-semibold">Contract Address:</span>{" "}
{config?.CONTRACT_ADDRESS}
</p>
</div>
{/* Current Message Display */}
<div className="mb-8 p-6 bg-indigo-50 dark:bg-slate-700 rounded-lg border-2 border-indigo-200 dark:border-slate-600">
<h2 className="text-lg font-semibold text-slate-900 dark:text-white mb-2">
Current Message:
</h2>
{loading && !message ? (
<p className="text-slate-600 dark:text-slate-300 italic">Loading...</p>
) : message ? (
<p className="text-xl text-indigo-900 dark:text-indigo-200 break-words">
{message}
</p>
) : (
<p className="text-slate-500 dark:text-slate-400 italic">
No message yet
</p>
)}
</div>
{/* Refresh Button */}
<button
onClick={() => readMessage(contract)}
disabled={loading || !contract}
className="w-full mb-6 py-3 px-4 bg-blue-500 hover:bg-blue-600 disabled:bg-slate-400 text-white font-semibold rounded-lg transition-colors"
>
{loading ? "Loading..." : "Refresh Message"}
</button>
{/* Update Message Form */}
<div className="space-y-4">
<h2 className="text-lg font-semibold text-slate-900 dark:text-white">
Update Message:
</h2>
<input
type="text"
value={newMessage}
onChange={(e) => setNewMessage(e.target.value)}
placeholder="Enter new message..."
className="w-full px-4 py-3 border border-slate-300 dark:border-slate-600 rounded-lg bg-white dark:bg-slate-700 text-slate-900 dark:text-white placeholder-slate-400 dark:placeholder-slate-500 focus:outline-none focus:ring-2 focus:ring-indigo-500"
/>
<button
onClick={updateMessage}
disabled={loading || !account || !contract}
className="w-full py-3 px-4 bg-green-500 hover:bg-green-600 disabled:bg-slate-400 text-white font-semibold rounded-lg transition-colors"
>
{loading ? "Updating..." : "Update Message"}
</button>
</div>
{/* Error/Status Messages */}
{error && (
<div
className={`mt-6 p-4 rounded-lg ${
error.includes("Transaction sent")
? "bg-green-50 dark:bg-green-900 text-green-800 dark:text-green-200"
: "bg-red-50 dark:bg-red-900 text-red-800 dark:text-red-200"
}`}
>
<p className="text-sm break-words">{error}</p>
</div>
)}
{/* Info Section */}
<div className="mt-8 p-4 bg-yellow-50 dark:bg-yellow-900 rounded-lg text-sm text-yellow-800 dark:text-yellow-200">
<p className="font-semibold mb-2"> Note:</p>
<ul className="list-disc list-inside space-y-1">
<li>To update messages, you need MetaMask or a compatible Web3 wallet</li>
<li>Make sure your MetaMask is connected to the correct test network</li>
<li>Reading messages is free and doesn't require a wallet</li>
<li>Updates are written to the blockchain and may take time to confirm</li>
</ul>
</div>
</div>
</main>
</div>
);
}