metamask login connect

<!doctype html>
<html lang="en">
	<head>
		<meta charset="utf-8" />
		<meta name="viewport" content="width=device-width,initial-scale=1" />
		<title>Web3 Contract Example</title>

		<!-- Pin web3 to a specific version -->
		<script src="https://cdn.jsdelivr.net/npm/web3@1.8.1/dist/web3.min.js"></script>

		<style>
			body { font-family: Arial, sans-serif; padding: 1rem; max-width: 720px; margin: auto; }
			button { margin-top: 0.5rem; }
			.row { margin: 0.5rem 0; }
			.warn { color: #a00; font-weight: bold; }
		</style>
	</head>
	<body>

		<h2>Web3 Contract Demo</h2>

		<div id="status" class="row">
			Status: <span id="statusText">idle</span>
		</div>

		<div class="row">
			<button id="connectBtn">Connect Wallet</button>
			<span id="account" style="margin-left:1rem"></span>
		</div>

		<div class="row">
			<strong>Network:</strong>
			<span id="network">—</span>
			<span id="networkWarn" class="warn" style="margin-left:1rem; display:none">Wrong network</span>
		</div>

		<div class="row">
			<strong>Current value:</strong>
			<span id="currentValue">—</span>
			<button id="refreshBtn">Refresh</button>
		</div>

		<div class="row">
			<input id="newValue" type="number" placeholder="New value" />
			<button id="setBtn">Set Value</button>
		</div>

		<div class="row">
			<small>Tip: Place a `contractABI.json` file in the same folder (optional). Otherwise replace the `ABI` and `contractAddress` constants in the source.</small>
		</div>

		<script>
			// === Configuration ===
			// Replace with your deployed address if you know it.
			const contractAddress = "0xYourContractAddressHere";

			// Optionally set the expected chainId (Hex string like '0x1' for mainnet or '0x5' for Goerli).
			// Set to null to skip network mismatch warnings.
			const expectedChainId = null; // e.g. '0x5'

			// Default ABI placeholder; we will try to load ./contractABI.json if present.
			let ABI = [ /* ... ABI array ... */ ];

			let web3;
			let contract;
			let currentAccount;
			let currentChainId;

			function setStatus(text) { document.getElementById('statusText').textContent = text; }

			// Try to load an ABI file from the same directory (contractABI.json)
			async function tryLoadLocalAbi() {
			try {
			const resp = await fetch('./contractABI.json', { cache: 'no-store' });
			if (!resp.ok) return;
			const parsed = await resp.json();
			if (Array.isArray(parsed)) {
			ABI = parsed;
			console.info('Loaded ABI from contractABI.json');
			} else if (parsed.abi && Array.isArray(parsed.abi)) {
			ABI = parsed.abi;
			console.info('Loaded ABI from contractABI.json (wrapped format)');
			}
			} catch (err) {
			// ignore - local ABI optional
			}
			}

			// Decode revert reason from error.data (if present)
			function decodeRevertReason(data) {
			try {
			if (!data) return null;
			// standard Error(string) selector: 0x08c379a0
			if (data.startsWith('0x08c379a0')) {
			// drop selector and length prefix: solidity ABI encode => 4 + offset(32) + strLen(32) + strBytes
			const hex = data.slice(10); // remove 0x08c379a0
			// Derive the string hex from the remaining payload
			// Skip first 64 chars (offset + strLen) -> start at char 128 (0-based)
			const strHex = hex.slice(128);
			const strBytes = strHex.slice(0, strHex.length - (strHex.length % 2));
			const str = decodeURIComponent(strBytes.match(/.{1,2}/g).map(byte => '%' + byte).join(''));
			return str;
			}
			return null;
			} catch (e) {
			return null;
			}
			}

			async function connectWallet() {
			if (!window.ethereum) {
			setStatus('No Ethereum provider (install MetaMask).');
			return;
			}
			try {
			setStatus('Initializing...');
			await tryLoadLocalAbi();
			web3 = new Web3(window.ethereum);

			// Request accounts (user will be prompted if not previously allowed)
			const accounts = await window.ethereum.request({ method: 'eth_requestAccounts' });
			currentAccount = accounts && accounts[0];
			document.getElementById('account').textContent = currentAccount || '';

			// Read chainId
			currentChainId = await window.ethereum.request({ method: 'eth_chainId' });
			document.getElementById('network').textContent = currentChainId || 'unknown';
			if (expectedChainId && currentChainId !== expectedChainId) {
			document.getElementById('networkWarn').style.display = 'inline';
			} else {
			document.getElementById('networkWarn').style.display = 'none';
			}

			contract = new web3.eth.Contract(ABI, contractAddress);
			setStatus('Connected');
			await readValue();
			} catch (err) {
			console.error(err);
			setStatus('Connection failed');
			}
			}

			async function readValue() {
			if (!contract) { setStatus('Not connected'); return; }
			try {
			setStatus('Reading value...');
			const value = await contract.methods.getValue().call();
			document.getElementById('currentValue').textContent = value;
			setStatus('Read complete');
			} catch (err) {
			console.error(err);
			const reason = decodeRevertReason(err?.data?.original || err?.data || err?.error?.data);
			setStatus('Read failed' + (reason ? `: ${reason}` : ''));
			}
			}

			function validateInput(v) {
			if (v === '') return { ok: false, msg: 'Value required' };
			// numeric check
			const n = Number(v);
			if (!Number.isFinite(n)) return { ok: false, msg: 'Not a valid number' };
			// check safe integer range if integer expected
			if (!Number.isSafeInteger(n)) {
			// allow decimals if the contract expects them, else warn
			return { ok: true, value: n, warn: 'Value is outside safe integer range; consider using a string/BN' };
			}
			return { ok: true, value: n };
			}

			async function setValue() {
			if (!contract || !currentAccount) { setStatus('Not connected'); return; }
			const input = document.getElementById('newValue');
			const v = input.value;
			const check = validateInput(v);
			if (!check.ok) {
			setStatus(check.msg);
			return;
			}
			try {
			setStatus('Estimating gas...');
			// Use BN/string if necessary - here we pass the value as-is; adapt if the contract expects specific types
			const method = contract.methods.setValue(check.value);
			// estimate gas
			const gasEstimate = await method.estimateGas({ from: currentAccount });
			setStatus('Sending transaction...');
			const receipt = await method.send({ from: currentAccount, gas: Math.floor(gasEstimate * 1.2) });
			console.log('Tx receipt', receipt);
			setStatus('Transaction confirmed');
			await readValue();
			} catch (err) {
			console.error(err);
			// Try to show revert reason
			let reason = null;
			// common places providers put data
			reason = decodeRevertReason(err?.data?.original || err?.data || err?.error?.data || err?.receipt?.revertReason);
			if (!reason && err?.message) reason = err.message;
			setStatus('Tx failed or rejected' + (reason ? `: ${reason}` : ''));
			}
			}

			// React to account / chain changes
			if (window.ethereum) {
			window.ethereum.on('accountsChanged', (accounts) => {
			currentAccount = accounts[0] || null;
			document.getElementById('account').textContent = currentAccount || '';
			if (!currentAccount) setStatus('Disconnected');
			});
			window.ethereum.on('chainChanged', (chainId) => {
			// update UI and optionally warn about mismatch
			currentChainId = chainId;
			document.getElementById('network').textContent = currentChainId || 'unknown';
			if (expectedChainId && currentChainId !== expectedChainId) {
			document.getElementById('networkWarn').style.display = 'inline';
			} else {
			document.getElementById('networkWarn').style.display = 'none';
			}
			// Recommend reload to reinitialize provider state (keeps things simple).
			// Some apps handle chain re-init without reload.
			window.location.reload();
			});
			}

			document.getElementById('connectBtn').addEventListener('click', connectWallet);
			document.getElementById('refreshBtn').addEventListener('click', readValue);
			document.getElementById('setBtn').addEventListener('click', setValue);

			// Optionally auto-connect if user already approved - prefer eth_accounts
			window.addEventListener('DOMContentLoaded', async () => {
			if (!window.ethereum) return;
			try {
			await tryLoadLocalAbi();
			const accounts = await window.ethereum.request({ method: 'eth_accounts' });
			if (accounts && accounts.length > 0) {
			await connectWallet();
			}
			} catch (e) {
			// ignore
			}
			});
		</script>
	</body>
</html>
Web3 Contract Example

Web3 Contract Demo

Status: idle
Network: —
Current value: —
Tip: Place a `contractABI.json` file in the same folder (optional). Otherwise replace the `ABI` and `contractAddress` constants in the source.

Note: this is a work in progress and part of practicing a meta mask course currently undergoing.