Menghubungkan frontend dengan blockchain: ethers.js dan viem, integrasi wallet pengguna dengan MetaMask dan WalletConnect, serta merakit dApp sederhana yang benar-benar berfungsi

Setelah di episode 7 kalian memastikan kontrak teruji, pada episode ini kita membangun jembatan ke pengguna: dApp. Kontrak yang bagus tidak ada artinya tanpa antarmuka yang memungkinkan pengguna berinteraksi dengannya. Di sinilah profesi blockchain developer kembali ke akar fullstack — kalian harus menghubungkan frontend, wallet, dan on-chain dengan mulus.
Kita fokus pada dua library standar: ethers.js (mapan, paling banyak dipakai) dan viem (modern, type-safe, favorit baru). Di akhir episode, kita merakit dApp sederhana yang benar-benar berfungsi.
Perhatikan dua jalur: baca (frontend memanggil RPC untuk view function — gratis) dan tulis (frontend meminta wallet menandatangani transaksi — pengguna membayar gas). Frontend tidak pernah memegang private key; wallet pengguna yang menandatangani.
Install dan hubungkan ke wallet browser:
npm install ethersimport { ethers } from "ethers";
async function connect() {
// Web3Provider membungkus wallet browser (MetaMask)
const provider = new ethers.BrowserProvider(window.ethereum);
const signer = await provider.getSigner();
const address = await signer.getAddress();
return { provider, signer, address };
}
async function readBalance(contractAddress, abi, signer) {
const contract = new ethers.Contract(contractAddress, abi, signer);
return await contract.balanceOf(await signer.getAddress());
}
async function sendTransfer(contractAddress, abi, to, amount, signer) {
const contract = new ethers.Contract(contractAddress, abi, signer);
const tx = await contract.transfer(to, amount);
// Tunggu konfirmasi (bisa dipilih 1 block untuk UX cepat)
const receipt = await tx.wait(1);
return receipt;
}BrowserProvider menyuntikkan koneksi ke wallet; getSigner() mengambil akun yang dipilih pengguna. Semua fungsi view bisa dibaca dengan provider biasa, sementara transaksi butuh signer.
viem berorientasi type-safe dan terpisah antara read/write action dengan jelas:
npm install viemimport { createWalletClient, createPublicClient, custom, http } from "viem";
import { mainnet } from "viem/chains";
const publicClient = createPublicClient({
chain: mainnet,
transport: http("https://ethereum-rpc.publicnode.com"),
});
const walletClient = createWalletClient({
chain: mainnet,
transport: custom(window.ethereum),
});
// Baca saldo (view function, tanpa gas)
async function readBalance(tokenAbi, contractAddress, account) {
return await publicClient.readContract({
address: contractAddress,
abi: tokenAbi,
functionName: "balanceOf",
args: [account],
});
}
// Kirim transaksi (butuh tanda tangan wallet)
async function transfer(tokenAbi, contractAddress, to, amount, account) {
const txHash = await walletClient.writeContract({
address: contractAddress,
abi: tokenAbi,
functionName: "transfer",
args: [to, amount],
account,
});
const receipt = await publicClient.waitForTransactionReceipt({ hash: txHash });
return receipt;
}viem memisahkan publicClient (membaca, tanpa gas) dari walletClient (menulis, butuh tanda tangan) — model yang lebih eksplisit dan menghasilkan error yang lebih jelas saat ada kesalahan.
Note
ethers.js dan viem sama-sama valid. ethers.js memiliki ekosistem tooling paling luas; viem unggul dalam type safety dan pengalaman developer modern (RainbowKit, wagmi, dan ekosistem Shadcn UI berbasis viem). Pilih sesuai kebutuhan tim — skill-nya transferable.
dApp 2026 tidak lagi menulis integrasi wallet manual. Ekosistem library sudah matang:
| Library | Keunggulan |
|---|---|
| wagmi | Hooks React, dukungan viem, built-in wallet modal |
| RainbowKit | UI wallet connect yang siap pakai |
| Web3Modal (WalletConnect) | Dukungan ribuan wallet mobile & desktop |
Contoh integrasi cepat dengan wagmi + RainbowKit:
npm install wagmi @rainbow-me/rainbowkit viemimport { createConfig, http } from "wagmi";
import { mainnet } from "wagmi/chains";
import { getDefaultConfig } from "@rainbow-me/rainbowkit";
export const config = createConfig(
getDefaultConfig({
appName: "My dApp",
projectId: "YOUR_WALLETCONNECT_ID",
chains: [mainnet],
transports: { [mainnet.id]: http() },
})
);WalletConnect Project ID membuat dApp kalian bisa diakses dari wallet mobile apa pun — bukan hanya ekstensi browser. Di 2026, dukungan mobile adalah keharusan, bukan bonus.
Mari gabungkan semuanya: satu halaman yang membaca saldo token dan mengirim transfer.
"use client";
import { useAccount, useReadContract, useWriteContract } from "wagmi";
import { parseEther } from "viem";
const ABI = [
"function balanceOf(address) public view returns (uint256)",
"function transfer(address to, uint256 amount) public returns (bool)",
];
export default function Home() {
const { address, isConnected } = useAccount();
const { data: balance } = useReadContract({
address: "0xTOKEN",
abi: ABI,
functionName: "balanceOf",
args: address ? [address] : undefined,
});
const { writeContract } = useWriteContract();
if (!isConnected) return <w3m-button />;
return (
<div>
<p>Alamat: {address}</p>
<p>Saldo: {balance?.toString() ?? "0"} MTK</p>
<button
onClick={() =>
writeContract({
address: "0xTOKEN",
abi: ABI,
functionName: "transfer",
args: ["0xRECIPIENT", parseEther("1")],
})
}
>
Kirim 1 MTK
</button>
</div>
);
}Perhatikan pola: useReadContract otomatis update saat saldo berubah, useWriteContract menangani seluruh alur transaksi termasuk feedback error. Inilah pengalaman development 2026 — bukan lagi boilerplate manual.
Warning
Jangan pernah meletakkan private key di frontend — dApp browser berjalan di sisi pengguna, semua yang kalian tulis bisa dibaca. Frontend hanya menyediakan UI + koneksi; tanda tangan dan gas selalu milik pengguna.
Inti yang harus dibawa pulang:
Di episode 9 selanjutnya kita masuk ke ranah paling besar di ekosistem — DeFi fundamentals: DEX, AMM, lending, staking, dan liquidity pools, plus analisis nyata protokol DeFi. Mari lanjut ke episode 9!