dApp (Decentralized Application) Development

We design and develop full-cycle blockchain solutions: from smart contract architecture to launching DeFi protocols, NFT marketplaces and crypto exchanges. Security audits, tokenomics, integration with existing infrastructure.
Showing 1 of 1 servicesAll 1306 services
dApp (Decentralized Application) Development
Complex
from 2 weeks to 3 months
FAQ
Blockchain Development Services
Blockchain Development Stages
Latest works
  • image_website-b2b-advance_0.png
    B2B ADVANCE company website development
    1214
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1161
  • image_websites_belfingroup_462_0.webp
    Website development for BELFINGROUP
    852
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1041
  • image_logo-advance_0.png
    B2B Advance company logo design
    561
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    823

dApp (Decentralized Application) Development

A dApp differs from a regular web application not because "it uses blockchain" — but because critical business logic is executed on-chain, and the user interacts with it directly through their wallet, without an intermediary. This is a fundamentally different architecture: there's no backend server that "owns" user data, no database with balances — only smart contracts and events. Everything else is a project decision about how much off-chain infrastructure you're willing to maintain.

Architectural Decisions at the Start

Level of Decentralization

The first honest question — what should be on-chain, and what shouldn't? Every byte in a smart contract costs gas. Storing everything on-chain is expensive and often pointless.

Fully on-chain: logic + data in the contract. Suitable for financial primitives (AMM, lending, staking). No backend, works through any RPC.

Hybrid: logic on-chain, UI + indexing off-chain. 90% of dApps. The contract is the source of truth for financial operations, off-chain backend handles fast search, notifications, analytics.

Light dApp: smart contract only for payments/ownership, main functionality is a regular web service. Often the correct choice for the first version of a product.

Stack

Standard stack for 2024-2025: React 18 + TypeScript + Vite, Wagmi v2 + Viem for blockchain interaction, RainbowKit or ConnectKit for wallet connection, TanStack Query for caching on-chain data. For SSR requirements — Next.js, but carefully: server components + wagmi require careful setup.

State management: Zustand or Jotai work well for dApps (less boilerplate than Redux, combine well with reactive wagmi hooks). Recoil — if the project already uses it.

Wallet Connection and Authentication

Multi-wallet Support

Users arrive with MetaMask, Coinbase Wallet, WalletConnect, Ledger, Safe. Wagmi v2 + WalletConnect v2 covers 95% of use cases out of the box. Custom integration is rarely needed — only for corporate wallets or specific use cases.

const config = createConfig({
    chains: [mainnet, polygon, arbitrum],
    transports: {
        [mainnet.id]: http(process.env.VITE_ALCHEMY_MAINNET_URL),
        [polygon.id]: http(process.env.VITE_ALCHEMY_POLYGON_URL),
        [arbitrum.id]: http(process.env.VITE_ALCHEMY_ARBITRUM_URL),
    },
    connectors: [
        injected(),
        coinbaseWallet({ appName: 'MyDApp' }),
        walletConnect({ projectId: WC_PROJECT_ID }),
    ],
});

SIWE (Sign-In with Ethereum)

For dApps with off-chain components (profiles, settings, notifications), authentication without a password is needed. SIWE (EIP-4361): user signs a text message with nonce, backend verifies the signature, issues a JWT. This is not a transaction — the signature is free and instant.

const message = new SiweMessage({
    domain: window.location.host,
    address: account.address,
    statement: 'Sign in with Ethereum to MyDApp.',
    uri: window.location.origin,
    version: '1',
    chainId: chain.id,
    nonce: await getNonce(), // from server, for replay protection
});

On-chain Data Fetching

Multicall and Request Batching

A naive approach — a separate RPC call for each balanceOf, allowance, userInfo. With 20 tokens — 20 requests, ~2 second delay. Multicall3 (deployed on all major networks at address 0xcA11bde05977b3631167028862bE2a173976CA11) lets you pack N calls into one:

const results = await client.multicall({
    contracts: tokens.map(token => ({
        address: token.address,
        abi: erc20Abi,
        functionName: 'balanceOf',
        args: [userAddress],
    })),
});

Wagmi automatically batches useReadContracts through Multicall3. But it's important to understand the limits: very large batches can exceed node gas limits.

The Graph vs. Custom Indexer

For reading historical data (events, transactions, aggregates) — you can't rely on eth_getLogs with a wide block range: nodes limit requests. Two options:

The Graph: GraphQL API over indexed events. Subgraph is written in AssemblyScript, deployed to Subgraph Studio. Good for DeFi data (TVL, volumes, positions). Indexing delay — several blocks.

Alchemy/Moralis API: managed indexing without writing a subgraph. Faster to start, more expensive to scale, less flexibility.

Custom indexer: PostgreSQL + TypeScript service listening to events via WebSocket. Full control, but infrastructure support. Recommended with specific data requirements or high loads.

Transaction UX

Transactions are the main source of friction in dApps. The user shouldn't have to guess what's happening.

Transaction States

Full lifecycle: idle → signing (waiting for signature in wallet) → pending (transaction in mempool) → confirming (N of M confirmations) → success/error. Each state requires UI feedback.

const { writeContractAsync, isPending } = useWriteContract();
const { isLoading: isConfirming, isSuccess } = useWaitForTransactionReceipt({
    hash: txHash,
});

Gas Estimation and EIP-1559

Wagmi/Viem uses EIP-1559 (maxFeePerGas + maxPriorityFeePerGas) by default on supporting networks. For UX: show estimated fee in USD before confirmation, using eth_estimateGas + current gas price + ETH/USD oracle (Chainlink or CoinGecko API).

Too low gasLimit — transaction fails with out-of-gas. Too high — user sees a scary number. Adding 20% buffer to estimated gas — standard practice.

Approve Flow

ERC-20 requires approve before transferFrom. Two-step process (approve → action) — source of confusion. Solutions:

  • Permit (EIP-2612): one signature instead of approve transaction, if token supports it
  • Unlimited approve: once per contract (good UX, bad security — not recommended)
  • Exact approve: approve exactly the amount of the current operation — correct, but two transactions each time

Multichain and Network

Chain Switching

User may be connected to the wrong chain. Auto-request to switch:

const { switchChain } = useSwitchChain();

if (chain?.id !== targetChainId) {
    await switchChain({ chainId: targetChainId });
}

For new networks (not in MetaMask by default) — use wallet_addEthereumChain RPC method to add.

RPC Resilience

Single RPC is a single point of failure. For production: multiple providers with fallback (Alchemy primary, Infura secondary, public RPC tertiary). Viem supports fallback transport out of the box.

Frontend Security

  • No private key on frontend — obvious, but worth stating explicitly
  • Verify contract addresses from env variables, not hardcoded in code
  • Content Security Policy against XSS — especially critical, as XSS in dApp can lead to fund theft via fake transactions
  • Check chainId in every transaction — protection from replay attacks on another chain
  • ENS resolution with reverse lookup check (address → ENS → address matches)

Timeline Guidelines

MVP dApp (one chain, basic operations, wallet connect): 2-3 weeks. Full-featured product with multichain, analytics, The Graph indexing and production-ready UX: 2-3 months.