A front-end needs the ABI, the address and a signer. Reads go through a provider; writes need a wallet signature and should surface the pending state and the final receipt.
const c = new ethers.Contract(address, abi, signer); const tx = await c.stake(amount); const receipt = await tx.wait();
Check the chain id before every write, decode custom errors for readable messages, and never trust client-side validation as security.