Fatskills
Practice. Master. Repeat.
Study Guide: Blockchain and Web3 Development: Decentralized Applications dApps Web3js Ethersjs Interacting with Contracts from Frontend
Source: https://www.fatskills.com/cryptocurrency-bitcoin-blockchain-and-more/chapter/blockchain-and-web3-development-blockchain-and-web3-development-decentralized-applications-dapps-web3js-ethersjs-interacting-with-contracts-from-frontend

Blockchain and Web3 Development: Decentralized Applications dApps Web3js Ethersjs Interacting with Contracts from Frontend

By Fatskills Exam Guides Team — the exam nerds behind 28,500+ quizzes and 2.1M practice questions across 500+ global exams.

⏱️ ~6 min read

What This Is

Web3.js and Ethers.js are JavaScript libraries that let a web page talk directly to the Ethereum Virtual Machine (EVM). They expose the blockchain as a set of read‑only calls (call) and state‑changing transactions (sendTransaction) so a front‑end can, for example, let a user swap tokens on Uniswap, mint an NFT, or cast a vote in a DAO without ever leaving the page.


Key Terms & Code Snippets

  • Web3.js – The original Ethereum JavaScript API (maintained by the community). It creates a Web3 instance, loads a provider (e.g., MetaMask), and builds contract objects.

js const web3 = new Web3(window.ethereum); const token = new web3.eth.Contract(ERC20_ABI, tokenAddress);


  • Ethers.js – A newer, lighter‑weight alternative that emphasizes immutable objects and TypeScript support.

js const provider = new ethers.providers.Web3Provider(window.ethereum); const token = new ethers.Contract(tokenAddress, ERC20_ABI, provider);


  • Provider – The bridge between JavaScript and the blockchain (e.g., Infura, Alchemy, or the injected window.ethereum). It supplies block data, gas prices, and the ability to sign transactions.

  • Signer – An object that holds a private key (or uses MetaMask) and can sign and send transactions.

js const signer = provider.getSigner(); // MetaMask user const tokenWithSigner = token.connect(signer);


  • ABI (Application Binary Interface) – A JSON description of a contract’s functions, events, and types. The front‑end uses the ABI to encode calls and decode responses.

  • call vs sendTransactioncall runs locally on a node, consumes no gas, and never changes state. sendTransaction creates a signed transaction, pays gas, and updates the ledger.

js // read balance (free) const bal = await token.balanceOf(address); // call // transfer tokens (costs gas) const tx = await tokenWithSigner.transfer(to, amount); // sendTransaction


  • Event Listening – Contracts emit events (e.g., Transfer(address,address,uint256)). Front‑ends subscribe to them to update UI in real time.

js token.on('Transfer', (from, to, value, event) => {
console.log(`${from} → ${to}: ${value}`); });


  • Gas EstimationestimateGas asks the node how much gas a transaction will need; useful for UI “estimated fee” displays.

js const gas = await tokenWithSigner.estimateGas.transfer(to, amount);


  • MetaMask / WalletConnect – Browser extensions or mobile bridges that inject a provider (window.ethereum). They handle user approvals, nonce management, and chain switching.

  • EIP‑1559 Transaction – New fee model (maxFeePerGas, maxPriorityFeePerGas). Ethers.js automatically builds these fields when the network supports them.

js const tx = await tokenWithSigner.transfer(to, amount, {
maxPriorityFeePerGas: ethers.utils.parseUnits('2', 'gwei') });


  • Contract Factory – In Ethers.js, a ContractFactory compiles bytecode and deploys a new contract instance.

js const factory = new ethers.ContractFactory(ABI, BYTECODE, signer); const deployed = await factory.deploy(arg1, arg2); await deployed.deployed(); // wait for mining


Step‑by‑Step / Process Flow

  1. Write & compile the Solidity contract
    bash
    npx hardhat compile # produces .json with ABI & bytecode
  2. Deploy to a testnet (e.g., Goerli) using a script
    js
    const factory = new ethers.ContractFactory(ABI, BYTECODE, signer);
    const contract = await factory.deploy(initialSupply);
    await contract.deployed();
    console.log('Deployed at', contract.address);
  3. Expose the ABI & address to the front‑end (store in src/constants.js or fetch from a subgraph).

  4. Initialize the provider & signer in the UI
    js
    await window.ethereum.request({ method: 'eth_requestAccounts' });
    const provider = new ethers.providers.Web3Provider(window.ethereum);
    const signer = provider.getSigner();

  5. Create a contract instance and call functions
    js
    const token = new ethers.Contract(tokenAddress, ERC20_ABI, provider);
    const tokenWithSigner = token.connect(signer);
    const tx = await tokenWithSigner.transfer(recipient, ethers.utils.parseUnits('10', 18));
    await tx.wait(); // wait for confirmation
  6. Listen for events to update UI
    js
    token.on('Transfer', (from, to, amount) => refreshBalances());

Common Mistakes

  • Mistake: Using await token.transfer(...) without a signer.
    Correction: Connect the contract to a signer (token.connect(signer)) so the transaction can be signed and broadcast. Without a signer the call is a read‑only call, and no state changes occur.

  • Mistake: Forgetting to await tx.wait() before reading the updated state.
    Correction: The transaction is mined asynchronously; await tx.wait() guarantees the receipt is on‑chain, preventing UI race conditions.

  • Mistake: Hard‑coding the provider URL (e.g., https://mainnet.infura.io/v3/...) in production.
    Correction: Detect window.ethereum first; fall back to a read‑only RPC only for read‑only pages. This avoids forcing users to trust a single node and respects wallet‑driven signing.

  • Mistake: Ignoring chainId mismatches (e.g., UI thinks it’s on Polygon while MetaMask is on Ethereum).
    Correction: Compare await provider.getNetwork() with the expected chainId and prompt the user to switch networks via wallet_switchEthereumChain.

  • Mistake: Not handling reverted transactions (try/catch only around await token.transfer).
    Correction: Wrap the whole await tx.wait() in a try block; the revert reason is only available after the receipt is mined.


Blockchain Developer Interview / Practical Insights

  1. call vs delegatecall – Interviewers love to ask why delegatecall is dangerous in upgradeable proxies. The answer: it runs the callee’s code in the caller’s storage context, enabling state‑corruption if the callee is malicious.

  2. ERC‑20 vs ERC‑777 – Be ready to explain that ERC‑777 adds hooks (tokensReceived) and can reject transfers, while ERC‑20 is simpler but lacks these safety callbacks.

  3. EIP‑1559 fee fields – Know the difference between maxFeePerGas (total ceiling) and maxPriorityFeePerGas (miner tip). Show how Ethers.js fills them automatically when the network supports the new fee market.

  4. Optimistic vs ZK Rollups – Optimistic rollups assume transactions are valid and challenge fraud later; ZK rollups provide a validity proof on‑chain instantly. Discuss latency, data availability, and when each is preferred for a dApp.


Quick Check Questions

  1. Scenario: A dApp reads a token balance with await token.balanceOf(addr) and then immediately shows a “Transfer” button.
    Answer: The UI must still request the user’s signature before sending a transaction; reading a balance is free (call), but transferring requires a signed sendTransaction.

  2. Scenario: A contract uses tx.origin to restrict admin functions.
    Answer: Dangerous because a malicious contract can trick a user into calling it, making tx.origin equal the user’s address and bypassing the restriction. Use msg.sender instead.

  3. Scenario: You see a transaction receipt with status: 0.
    Answer: The transaction reverted; the UI should surface the revert reason (if available) and not assume state changed.


Last‑Minute Cram Sheet (10 one‑liners)

  1. ⚠️ Never store a private key in the front‑end; always rely on a wallet (MetaMask, WalletConnect).
  2. Ethers.js objects are immutable – calling contract.connect(signer) returns a new instance; the original stays read‑only.
  3. Gas‑optimisation tip: Use uint256 for all numeric storage; Solidity packs them efficiently when placed together.
  4. Compiler version: Most production contracts target pragma solidity ^0.8.17; the 0.8.x series includes built‑in overflow checks.
  5. ERC‑20 events: Transfer and Approval are mandatory; UI can listen to them for real‑time balance updates.
  6. EIP‑1193 defines the standard window.ethereum provider API (e.g., eth_requestAccounts).
  7. MetaMask automatically adds 0x1 (Ethereum Mainnet) to the provider; you must request a chain switch for testnets.
  8. estimateGas returns a big number; always add a small buffer (e.g., 10%) to avoid out‑of‑gas errors.
  9. Replay protection: EIP‑155 adds chainId to the transaction signature; never reuse a signed tx on another chain.
  10. Event filtering: Use contract.queryFilter('Transfer', fromBlock, toBlock) for historical data; live listeners only catch new events.


ADVERTISEMENT