Understanding the Frontend-to-Blockchain Flow: A Complete Guide

As a Web3 developer, one of the most confusing aspects when starting out is understanding how your frontend actually communicates with the blockchain. Let me break down the entire flow from smart contracts to user interaction.

The Complete Development Flow

1. Smart Contract Development & Deployment

Your Starting Point:

  • You have an application idea (decentralized, of course!)
  • You write smart contracts with all your application's core logic
  • Choose the right blockchain based on your requirements:
    • Ethereum: Most secure, highest adoption
    • Polygon: Low fees, fast transactions
    • BSC (Binance Smart Chain): Lower costs, high throughput
    • Avalanche: Fast finality, customizable subnets

Deployment Outputs:

  • Contract Address
  • Contract ABI (Application Binary Interface)

The Frontend Integration Challenge

Now you have a frontend and want users to interact with your deployed contracts. Here's where it gets interesting.

2. The Node Problem (And Its Solution)

The Challenge: Every blockchain has its own RPC (Remote Procedure Call) methods to read and write state. All blockchain data lives in nodes.

Option A: Run Your Own Node

  • Maintain infrastructure
  • Sync blockchain data (hundreds of GBs)
  • Handle uptime and reliability
  • Verdict: Not practical for most developers

Option B: Use RPC Providers ✔️

  • Alchemy, Infura, and others run thousands of nodes across all major chains
  • You get instant access via API
  • Sign up → Get RPC URL → Start building

3. Frontend Libraries: The Bridge to Blockchain

What Do Viem and Ethers.js Actually Do?

These libraries provide abstractions over raw JSON-RPC calls, making blockchain interaction developer-friendly.

Core Components You Need:

A. Client (Connection to Blockchain)

// Viem example
import { createPublicClient, http } from 'viem'
import { mainnet } from 'viem/chains'

const client = createPublicClient({
  chain: mainnet,
  transport: http('https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY')
})

What it does: Creates a connection to your chosen blockchain through the provider's RPC endpoint.

B. Wallet (For Signing Transactions)

import { createWalletClient, custom } from 'viem'

const walletClient = createWalletClient({
  chain: mainnet,
  transport: custom(window.ethereum) // MetaMask or other wallet
})

Why you need it: Any state-changing transaction requires gas fees and must be signed by the user's wallet.

C. Contract Instance (Your Application Interface)

import { getContract } from 'viem'

const contract = getContract({
  address: '0xYourContractAddress',
  abi: yourContractABI,
  publicClient: client,
  walletClient: walletClient
})

// Now call your functions
const result = await contract.read.name()
await contract.write.transfer(['0xRecipient', amount])

4. Under The Hood: How Libraries Work

The Magic Behind createClient()

Libraries like Viem implement all JSON-RPC requests under the hood. Here's what's actually happening:

// Simplified version of what Viem does internally
async function rpcRequest(rpcUrl, method, params = []) {
  const res = await fetch(rpcUrl, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      jsonrpc: "2.0",
      id: 1,
      method,
      params
    })
  });
  
  const data = await res.json();
  if (data.error) throw new Error(data.error.message);
  return data.result;
}

function createClient({ rpcUrl }) {
  return {
    request: (method, params = []) => rpcRequest(rpcUrl, method, params),
    
    getBlockNumber: async function() {
      const hex = await this.request("eth_blockNumber");
      return parseInt(hex, 16);
    },
    
    getBalance: async function(address) {
      const balanceHex = await this.request("eth_getBalance", [address, "latest"]);
      return parseInt(balanceHex, 16);
    }
  };
}

// Usage
const client = createClient({ 
  rpcUrl: "https://mainnet.infura.io/v3/YOUR_KEY" 
});

console.log(await client.getBlockNumber());
console.log(await client.getBalance("0xabc..."));

5. Contract Function Calls: The Complete Journey

Example: Calling a Simple View Function

function name() public view returns (string);

What Happens When You Call It:

Step 1: ABI Encoding

// Function selector = first 4 bytes of keccak256("name()")
const selector = keccak256(Buffer.from("name()")).slice(0, 4);
const data = "0x" + Buffer.from(selector).toString("hex");

Step 2: JSON-RPC Request

const result = await client.request("eth_call", [{
  to: "0xYourContractAddress",
  data
}, "latest"]);

What the library does:

  1. Encodes function call to ABI format
  2. Wraps it in JSON-RPC request
  3. Sends HTTP request to provider

6. Provider & EVM Execution

The Request Flow:

Your Frontend 
    ↓ (HTTP Request)
Provider (Alchemy/Infura)
    ↓ (Forwards to)
Blockchain Node
    ↓ (Executes via)
EVM (Ethereum Virtual Machine)

Read Calls (eth_call, eth_getBalance, etc.)

  1. Provider receives request
  2. EVM executes call locally (no gas, no state change)
  3. Returns result to RPC client
  4. Viem decodes response → JavaScript object
  5. You update your UI 

Write Calls (eth_sendTransaction, eth_sendRawTransaction)

  1. User signs transaction with wallet
  2. Provider receives signed transaction
  3. Node broadcasts to network
  4. Consensus process begins:
    • Validators/miners verify transaction
    • Transaction included in block
    • Block propagated across network
    • State updated across all nodes
  5. Transaction confirmed
  6. Result returned → Viem decodes → UI updates 

Visual Flow Diagram

┌─────────────────────────────────────────────────────────────┐
│                    YOUR DEVELOPMENT FLOW                    │
└─────────────────────────────────────────────────────────────┘

1. Write Smart Contracts
         ↓
2. Choose Blockchain (Ethereum/Polygon/BSC/Avalanche)
         ↓
3. Deploy Contracts → Get (Address + ABI)
         ↓
4. Build Frontend
         ↓
5. Integrate Web3 Library (Viem/Ethers.js)
         ↓
┌────────────────────────────────────────────────────────────┐
│  createClient() → Pass provider RPC URL                    │
│  createWallet() → Connect user's wallet                    │
│  getContract()  → Pass (address, ABI, clients)             │
└────────────────────────────────────────────────────────────┘
         ↓
6. User Interacts with Frontend
         ↓
┌────────────────────────────────────────────────────────────┐
│              WHAT HAPPENS BEHIND THE SCENES                │
├────────────────────────────────────────────────────────────┤
│  Library encodes ABI → JSON-RPC request                    │
│           ↓                                                │
│  HTTP request to Provider (Alchemy/Infura)                 │
│           ↓                                                │
│  Provider forwards to Blockchain Node                      │
│           ↓                                                │
│  EVM executes (read) or broadcasts (write)                 │
│           ↓                                                │
│  [If write: Consensus → Verification → State Update]       │
│           ↓                                                │
│  Result returned → Library decodes → UI updates            │
└────────────────────────────────────────────────────────────┘

Key Takeaways

  1. RPC Providers (Alchemy, Infura) eliminate the need to run your own nodes
  2. Web3 Libraries (Viem, Ethers.js) abstract complex RPC calls into simple functions
  3. Every transaction goes through: Encoding → RPC Request → Provider → Node → EVM
  4. Read operations are instant and free
  5. Write operations require gas, signatures, and go through consensus

Quick Reference: Essential Functions

Need Viem Function What It Does
Connect to chain  createPublicClient()     Establishes RPC connection
User wallet  createWalletClient()    Enables transaction signing
Contract interaction  getContract()    Creates contract instance
Read data  contract.read.functionName()    Calls view/pure functions
Write data  contract.write.functionName()     Sends transactions



Comments