Blockchain Integration Guide

Technology: blockchain · Category: tooling · Last reviewed: 2026-08-23

Source: https://tech-stack.codeamanilabs.org/guide/blockchain

Insight:

A blockchain is a replicated append-only ledger no single party owns — its one real superpower is verifiable state without a trusted operator, and everything else (the trilemma, gas, finality) is a trade-off around that. Reads are cheap RPC calls; writes are key-signed, gas-costed, and irreversible, so the signing key never touches your server — it lives in the user's wallet or a KMS/HSM. For codeAmani the honest use is USDC on a cheap L2 (Base) as a borderless settlement rail behind an M-Pesa cash-out — stablecoins are the killer app, speculation is not.

██████╗ ██╗      ██████╗  ██████╗██╗  ██╗ ██████╗██╗  ██╗ █████╗ ██╗███╗   ██╗
██╔══██╗██║     ██╔═══██╗██╔════╝██║ ██╔╝██╔════╝██║  ██║██╔══██╗██║████╗  ██║
██████╔╝██║     ██║   ██║██║     █████╔╝ ██║     ███████║███████║██║██╔██╗ ██║
██╔══██╗██║     ██║   ██║██║     ██╔═██╗ ██║     ██╔══██║██╔══██║██║██║╚██╗██║
██████╔╝███████╗╚██████╔╝╚██████╗██║  ██╗╚██████╗██║  ██║██║  ██║██║██║ ╚████║
╚═════╝ ╚══════╝ ╚═════╝  ╚═════╝╚═╝  ╚═╝ ╚═════╝╚═╝  ╚═╝╚═╝  ╚═╝╚═╝╚═╝  ╚═══╝

Blockchain Integration Guide

Focus: what a blockchain actually is (a replicated append-only ledger nobody owns), the trade-off that governs every design choice (the trilemma), and where it earns its keep for codeAmani — stablecoin settlement and remittance rails that complement M-Pesa, not replace it. EVM-anchored, read-mostly, secrets server-side.

Overview

A blockchain is a shared, append-only ledger replicated across thousands of independent computers, where new entries ("blocks") are only accepted if a majority agree they follow the rules. Each block carries the cryptographic hash of the one before it, so the chain is tamper-evident: change one historical transaction and every later block's hash breaks, and the network rejects your fork. No single party — no bank, no company, no government — can silently edit it. That single property (verifiable state without a trusted operator) is the whole point; everything else is engineering trade-offs around it.

Three honest truths frame every blockchain decision:

  1. You can't have it all — the trilemma. A chain optimises at most two of decentralisation, security, and scalability. Bitcoin and Ethereum L1 pick decentralisation + security and pay with low throughput and higher fees. High-TPS L1s buy scalability by reducing the number of validators (less decentralised). L2 rollups (Base, Arbitrum, Optimism) are today's pragmatic answer: they execute cheaply off-chain and inherit Ethereum's security by posting proofs back to L1.
  2. Reads are free and easy; writes are slow, public, and cost gas. Querying chain state (a balance, a token's supply) is a cheap RPC call. Writing (a transfer, a contract call) is signed by a private key, broadcast to the mempool, included in a block by a validator, and costs a gas fee. Until it has enough confirmations it can still be reorged — writes are eventually-final, not instantly-final.
  3. Code is law, and bugs are permanent. A deployed smart contract is immutable and usually controls real money. There is no "undo," no support line. This is why audits, well-trodden token standards (ERC-20, ERC-721), and "don't roll your own crypto" matter more here than almost anywhere else.

For codeAmani, the realistic, non-speculative use is digital dollars on a cheap fast chain: USDC/USDT (stablecoins) on an L2 settle cross-border value in seconds for cents, which is a genuine complement to M-Pesa's domestic strength. The rest of this guide is EVM-centric (Ethereum + its L2s) because that ecosystem has the deepest tooling — viem and ethers — and the widest stablecoin liquidity.

Official Documentation

Source URL What it covers
Ethereum dev docs https://ethereum.org/en/developers/docs/ Accounts, transactions, gas, EVM, consensus — the canonical mental model
viem https://viem.sh/docs/getting-started Modern TypeScript Ethereum interface (clients, reads, writes, ABIs)
ethers v6 https://docs.ethers.org/v6/ Mature JS library — providers, contracts, wallets, formatting
Solidity https://docs.soliditylang.org/ The dominant smart-contract language
Circle (USDC) https://developers.circle.com/stablecoins Stablecoin standards, supported chains, on/off-ramp APIs

How a block chain holds together

Each block bundles a batch of transactions plus the hash of the previous block. Because the hash is derived from the contents, any change anywhere ripples forward and invalidates every subsequent block — that's what makes the ledger tamper-evident rather than merely "a database with backups."

flowchart LR
    G["Block 0 · Genesis<br/>hash: 0x9f…"] --> B1["Block 1<br/>prev: 0x9f…<br/>txs · hash: 0x3a…"]
    B1 --> B2["Block 2<br/>prev: 0x3a…<br/>txs · hash: 0x7c…"]
    B2 --> B3["Block 3<br/>prev: 0x7c…<br/>txs · hash: 0x11…"]
    B3 --> B4["Block N · pending<br/>prev: 0x11…<br/>mempool txs"]

Consensus is how the network agrees on which block is next. Ethereum uses Proof of Stake: validators lock up (stake) ETH and are chosen to propose/attest blocks; misbehaviour gets their stake slashed. This replaced energy-hungry Proof of Work and is why "Ethereum is bad for the environment" is now outdated.


The transaction lifecycle (a write)

Reading is a free RPC query. Writing money or state is the part with real consequences — sign locally, broadcast, wait for inclusion, then wait for finality.

sequenceDiagram
    participant App as Your app
    participant W as Wallet / signer
    participant M as Mempool
    participant V as Validator
    participant C as Chain
    App->>W: Build tx (to, value, data)
    W->>W: Sign with private key (never leaves the wallet)
    W->>M: Broadcast signed tx
    M->>V: Validator picks txs (often highest gas first)
    V->>C: Include tx in a block
    C-->>App: 1 confirmation (could still reorg)
    C-->>App: N confirmations → final
    Note over App,C: Reads are instant & free · writes cost gas & take time

Key terms you must internalise:

Term Meaning
Account / address 0x… 20-byte identifier. EOA = controlled by a private key; contract account = controlled by code.
Private key / seed phrase The secret that authorises spends. Whoever holds it owns the funds. Never on a server, never in git, never NEXT_PUBLIC_*.
Gas Compute units a tx consumes × gas price (in gwei). The fee. Failed txs still cost gas.
Nonce Per-account counter; orders an account's txs and prevents replays.
Confirmation / finality Blocks built on top of yours. More confirmations = harder to reverse. Treat money as received only after your finality threshold.
Wei / gwei / ether Denominations. 1 ether = 10⁹ gwei = 10¹⁸ wei. Always work in the smallest unit (bigint); format only for display.

Reading the chain (viem)

viem is the modern, type-safe TypeScript interface. A Public Client reads; install and query in three lines. Source: viem getting-started + clients/public docs.

npm i viem
// lib/chain.ts
import { createPublicClient, http } from "viem";
import { base } from "viem/chains"; // Base = cheap Ethereum L2 — good default for payments

// A public client is read-only. The transport is your RPC endpoint.
export const publicClient = createPublicClient({
  chain: base,
  transport: http(process.env.RPC_URL), // server-side RPC; falls back to a public node if omitted
});

// Cheap, free reads:
const block = await publicClient.getBlockNumber();
const wei = await publicClient.getBalance({ address: "0xA0Cf…251e" });

Reading an ERC-20 (e.g. a USDC balance)

Tokens like USDC are smart contracts, not native chain balance — you read them by calling balanceOf on the contract. Pattern straight from viem's reading-contracts example.

import { erc20Abi, formatUnits } from "viem";
import { publicClient } from "./chain";

const USDC = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"; // USDC on Base

const [symbol, decimals, raw] = await Promise.all([
  publicClient.readContract({ address: USDC, abi: erc20Abi, functionName: "symbol" }),
  publicClient.readContract({ address: USDC, abi: erc20Abi, functionName: "decimals" }),
  publicClient.readContract({ address: USDC, abi: erc20Abi, functionName: "balanceOf", args: ["0xA0Cf…251e"] }),
]);

const human = formatUnits(raw, decimals); // e.g. "42.5" → never do float math on raw bigint balances

The same read in ethers v6

ethers is the mature alternative; many existing dapps use it. Source: ethers v6 getting-started.

npm i ethers
import { JsonRpcProvider, Contract, formatUnits } from "ethers";

const provider = new JsonRpcProvider(process.env.RPC_URL);
const abi = [
  "function decimals() view returns (uint8)",
  "function symbol() view returns (string)",
  "function balanceOf(address) view returns (uint)",
];
const usdc = new Contract("0x8335…2913", abi, provider);

const decimals = await usdc.decimals();
const balance = await usdc.balanceOf("0xA0Cf…251e");
const human = formatUnits(balance, decimals);

viem vs ethers, for a new codeAmani project: prefer viem — smaller bundle (matters on 2G/3G), first-class TypeScript inference, and it pairs with wagmi for React wallet hooks. Use ethers when integrating with an existing codebase or tutorial that already assumes it.


Writing the chain (and why the key stays off your server)

A write is signed by a private key. There are two safe places for that key, and your app server is never one of them:

// Frontend write with a browser wallet (viem) — simulate first, then send.
import { createWalletClient, custom, parseUnits, erc20Abi } from "viem";
import { base } from "viem/chains";
import { publicClient } from "./chain";

const walletClient = createWalletClient({ chain: base, transport: custom(window.ethereum) });
const [account] = await walletClient.getAddresses();

// 1. Simulate — catches reverts BEFORE the user pays gas.
const { request } = await publicClient.simulateContract({
  account,
  address: "0x8335…2913", // USDC
  abi: erc20Abi,
  functionName: "transfer",
  args: ["0xRecipient…", parseUnits("10", 6)], // 10 USDC (6 decimals)
});
// 2. The wallet signs & broadcasts; the key never leaves the wallet.
const hash = await walletClient.writeContract(request);
// 3. Wait for finality before treating it as paid.
const receipt = await publicClient.waitForTransactionReceipt({ hash, confirmations: 3 });

The simulate → write → wait sequence is the canonical safe pattern: simulation surfaces a revert before any gas is spent, and waitForTransactionReceipt is where you enforce your finality threshold.


Smart contracts in one breath

A smart contract is code (usually Solidity) deployed to an address that runs on the EVM exactly as written, by every validator, forever. You mostly consume existing contracts via their ABI (the JSON describing their functions) rather than writing your own. When you do write one:


Possible use cases for codeAmani (mapped to the existing stack)

Blockchain is not a replacement for the repo's rails — it's a settlement layer that plugs into them. Each row pairs a real product idea with the tech-stack guide it builds on.

flowchart TD
    subgraph OnChain["On-chain (settlement)"]
      U["USDC on Base L2"]
    end
    subgraph Rails["codeAmani rails (existing guides)"]
      M["M-Pesa / Daraja"]
      S["Stripe"]
      DB["Supabase / Neon ledger"]
      AT["Africa's Talking / WhatsApp"]
    end
    U <-->|on/off-ramp| M
    U <-->|card top-up| S
    U -->|index events| DB
    U -->|status alerts| AT
Use case What blockchain adds Builds on (repo guide)
Cross-border remittance / settlement USDC on an L2 moves value across borders in seconds for cents, then off-ramps to M-Pesa locally. Cheaper and faster than correspondent banking for diaspora → Kenya flows. MPESA_PATTERNS.md, daraja-api, stripe (card on-ramp)
Stablecoin payouts to SMEs/creators Pay suppliers or gig workers in digital dollars; they hold value against KES inflation, cash out to M-Pesa on demand. daraja-api, africas-talking
On-chain proof / provenance Anchor a hash of a document, certificate, or supply-chain event on-chain for tamper-evident verification — cheap, no token speculation. supabase (store the doc, index the tx)
Auditable payment ledger Mirror on-chain transfers into Postgres so your app reads from a fast DB, with the chain as the source of truth. Dedupe on tx hash, exactly like webhook idempotency. supabase/neon, webhooks
Wallet-based identity / access "Sign-In with Ethereum" (a signed message, no gas) as a passwordless login or token-gated access, alongside Clerk for email/social. clerk (hybrid auth)
Transparent disbursements (NGO/treasury) Publicly verifiable fund flows for grants or community payouts — anyone can audit, no trust in a single operator. supabase (off-chain metadata)

The honest framing for the East African market: stablecoins are the killer app, speculation is not. USDC settlement complements M-Pesa's last-mile reach; treat the chain as a fast, borderless clearing layer and let Daraja handle the cash-in/cash-out that users actually touch.


On/off-ramp reality (the hard part)

Moving between fiat (KES) and on-chain dollars is a regulated, partner-dependent step, not an API you self-host:


Security checklist


codeAmani notes

Official docs: