Designing dApp Architecture: From Smart Contracts to UX

Designing dApp Architecture Recently, a DeFi protocol team approached us: their frontend made 20 separate RPC calls on every page load, resulting in a 5–7 second delay. The root cause was that the smart contracts weren't designed with frontendability in mind—missing view functions and sparse even

Blockchain Development Services

Frequently Asked Questions

Latest works

  • image_website-b2b-advance_0.webp
    B2B ADVANCE company website development
    1441
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1301
  • image_websites_belfingroup_462_0.webp
    Website development for BELFINGROUP
    998
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1267
  • image_logo-advance_0.webp
    B2B Advance company logo design
    713
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    1003

Designing dApp Architecture

Recently, a DeFi protocol team approached us: their frontend made 20 separate RPC calls on every page load, resulting in a 5–7 second delay. The root cause was that the smart contracts weren't designed with frontendability in mind—missing view functions and sparse events. We redesigned the architecture, integrated Multicall3, and reduced requests to a single one, cutting load time to 400 ms. Designing a dApp starts not with choosing a framework, but with analyzing how users will interact with the app. A common mistake is developing the frontend in isolation from the contracts. We ensure every layer—from smart contracts to UX flows—works as a unified whole.

How We Design Secure dApp Architecture

The first step is selecting standards and patterns for smart contracts. We use Solidity 0.8.x with overflow protection, explicit require and revert with messages, and proven libraries (OpenZeppelin). Each contract is audited with static analyzer Slither and fuzzer Echidna. This reduces risk of reentrancy, flash loan attacks, and other vulnerabilities. Typical gas savings after optimization are 30–50% compared to naive implementations.

Why Frontendability Is Critical for DeFi

Contracts must be friendly for the frontend. That means:

  • Rich Events for every meaningful action (transfer, balance change, parameter update).
  • View functions for gasless reads (e.g., totalAssets, balanceOf).
  • Compatibility with Multicall3 — so the frontend can fetch dozens of values in one RPC request.

Data Fetching Architecture

We use a multi-level data loading system:

  • Realtime: WebSocket to RPC or Alchemy SDK.
  • Recent history: The Graph (subgraph).
  • Historical analytics: Self-hosted PostgreSQL indexer.
  • Prices: CoinGecko API + Chainlink on-chain.

According to The Graph documentation, subgraphs can process up to 1000 events per second. For one client, we configured an index that processed 3 million events in 2 minutes.

How We Implement Transaction Flow UX

User experience during transaction submission is a frequent source of errors. We design four states:

  • IDLE: button ready.
  • WAITING_WALLET: user confirms in wallet.
  • CONFIRMING: transaction in mempool.
  • SUCCESS or ERROR: final state.

Example with wagmi:

function DepositButton({ amount }: { amount: bigint }) { const { data: hash, writeContract, isPending } = useWriteContract(); const { isLoading: isConfirming, isSuccess } = useWaitForTransactionReceipt({ hash }); const status = isPending ? "WAITING_WALLET" : isConfirming ? "CONFIRMING" : isSuccess ? "SUCCESS" : "IDLE"; return ( <button onClick={() => writeContract({ address: POOL, abi, functionName: "deposit", args: [amount] })} disabled={status !== "IDLE"} > {status === "WAITING_WALLET" && "Confirm in wallet..."} {status === "CONFIRMING" && "Confirming..."} {status === "SUCCESS" && "Deposited!"} {status === "IDLE" && "Deposit"} </button> ); } 

State Management and Batching

For data reads, we use TanStack Query with refetchInterval (30 seconds for sensitive data) and Multicall3 to batch requests:

import { multicall } from "viem/actions"; const results = await multicall(client, { contracts: [ { address: TOKEN_A, abi: ERC20_ABI, functionName: "balanceOf", args: [user] }, { address: TOKEN_B, abi: ERC20_ABI, functionName: "balanceOf", args: [user] }, { address: POOL, abi: POOL_ABI, functionName: "totalAssets" }, ], }); 

Wallet Connection

We configure RainbowKit with support for main networks and wallets:

import { getDefaultConfig, RainbowKitProvider } from "@rainbow-me/rainbowkit"; import { arbitrum, mainnet, base } from "wagmi/chains"; const config = getDefaultConfig({ appName: "MyDApp", projectId: WALLETCONNECT_PROJECT_ID, chains: [mainnet, arbitrum, base], wallets: [ { groupName: "Popular", wallets: [metaMaskWallet, coinbaseWallet, walletConnectWallet] }, ], }); 

Architecture Approach Comparison

Parameter Monolithic Architecture Modular (Ours)
Layer coupling High, changes affect everything Low, each layer independent
Testability Hard, need to mock whole system Easy, can test contracts in isolation
Reusability Low High (contracts, indexer, UI components)
Time to implement changes Long Short, up to 2 days per layer

Indexing Method Comparison

Criterion The Graph Self-hosted Indexer
Sync speed Up to 1000 events/sec Depends on hardware
Query flexibility Limited (GraphQL) Full (SQL)
Cost Free (subscription) Infrastructure
Data aggregation Medium High

Project Work Process

  1. Analysis — studying business logic, user scenarios.
  2. Design — contract architecture, event specification, data flow diagrams.
  3. Implementation — writing contracts (Solidity/Rust), frontend (React + wagmi + RainbowKit), indexers.
  4. Testing — unit tests (Foundry), fuzzing, integration tests (Tenderly, Hardhat).
  5. Deployment — deploying with verify on Etherscan, setting up Tenderly Dashboard.
  6. Support — transaction monitoring, contract upgrades via proxy, gas optimization.

Timelines and What's Included

Timelines: from 2 to 6 weeks depending on complexity. Cost is calculated individually — contact us to get an estimate. Included:

  • Architectural documentation (diagrams, specifications).
  • Source code of contracts with comments.
  • Frontend stack with configuration (wagmi, RainbowKit).
  • Access to Tenderly project and alerts.
  • Team training on the codebase.

Typical dApp Design Mistakes

  • Lack of Multicall3 — frontend makes 10–20 separate requests, increasing load time by 3–5x.
  • Weak Events — without details on balance transfers, analytics is hard to build.
  • Ignoring transaction UX — user doesn't see intermediate states and clicks the button again, causing duplicate transactions.
  • Choosing the wrong indexer — The Graph is fast for millions of events, but for custom aggregation a self-hosted solution is better.

Our team's experience — 5+ years in Web3, 10+ implemented projects (DeFi, NFT, gaming). We guarantee clean code and adherence to best practices. Get a consultation on your dApp architecture — contact us via email or Telegram. Contact us to discuss your project — we'll prepare architectural documentation within 2 days.