Designing dApp Architecture: From Smart Contracts to UX

Building a dApp often starts with choosing a framework, but the real issue lies in the disconnect between smart contracts and the interface. We design the architecture holistically—from contract logic to UX flows—ensuring frontendability and reducing RPC load. Our team delivers the project turnkey with security audit and ongoing support.

Blockchain Development Services

Frequently Asked Questions

Latest works

  • Development of a web application for FEEDME
    Development of a web application for FEEDME
    1335
  • Development of an online store for the company FURNORO
    Development of an online store for the company FURNORO
    1293
  • B2B Advance company logo design
    B2B Advance company logo design
    738
  • Development of a web application for Enviok
    Development of a web application for Enviok
    1031
  • AIDER company logo development
    AIDER company logo development
    978
  • CRM development for Chasseurs
    CRM development for Chasseurs
    1087

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.