Ethereum Payment Setup: HD Wallet & Monitoring

Accepting Ethereum payments often brings challenges: how to know that a payment has arrived and from whom, when there are many transactions but only one address? We build a reliable system for receiving ether with unique deposit addresses based on HD Wallet and automatic transaction monitoring. Our team delivers a turnkey solution—from architecture to support—so you can accept payments without losses or failures.

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

Ethereum Payment Setup: HD Wallet & Monitoring

Accepting ETH sounds simple: the user sends a transaction, the money arrives. In practice, the question "How do I know the payment arrived and from whom?" is solved in several ways, and most quick implementations have race conditions or security issues. As a team with deep expertise in blockchain development, we offer a reliable architecture that eliminates the risk of losing funds. Contact us for a consultation. In this article, we'll walk through key architectural decisions: generating unique deposit addresses via HD Wallet, monitoring transactions through webhooks, and confirmation algorithms.

Architecture: Don't Use One Address for All

The most reliable scheme is a unique address per order/user. This eliminates attribution issues: no need to match amounts to orders, no collisions when two users pay the same amount.

Implementation via HD wallet (BIP-44): one master private key, addresses derived deterministically by index. The user gets address m/44'/60'/0'/0/{order_id}, and funds are forwarded to a hot wallet after confirmation. HD Wallet (BIP-44)

import { HDNodeWallet } from 'ethers'

const masterWallet = HDNodeWallet.fromPhrase(process.env.MNEMONIC)

function getDepositAddress(orderId: number): string {
  return masterWallet.deriveChild(orderId).address
}

Master mnemonic — in HSM or at least in encrypted environment variables, never in code.

Monitoring Incoming Transactions

A bad approach is polling eth_getBalance every N seconds. Slow, expensive on RPC requests, and misses transactions on restart.

The correct way is subscribing to events via WebSocket RPC:

provider.on({ address: depositAddress }, (log) => { // Process incoming transfer }) 

Or via eth_subscribe newPendingTransactions — receive notification before confirmation, but the pending status is not final.

For a reliable production solution — Alchemy Notify or QuickNode Streams: webhooks when activity occurs on addresses, works even if your backend restarts. Our experience shows that such a system is 10x faster in response time compared to polling.

Confirmations and Finality

On Ethereum after The Merge, finality occurs after ~2 epochs (~12.8 minutes). For payments:

Amount Recommended Confirmations
< $100 1–3 blocks (~15–45 sec)
$100–$10k 6–12 blocks (~1.5–2.5 min)
> $10k 32–64 blocks (up to finality)

Never credit funds based on a pending transaction — the transaction can be replaced via EIP-1559 replacement or dropped from the mempool.

Working with ERC-20 Tokens

If you need to accept USDC/USDT on top of ETH — the logic becomes more complex: listen for the Transfer event instead of a native transaction. You must filter logs using the token ABI:

const filter = {
  address: USDC_CONTRACT,
  topics: [
    ethers.id('Transfer(address,address,uint256)'),
    null,
    ethers.zeroPadValue(depositAddress, 32)
  ]
}
provider.on(filter, handleUsdcDeposit)

A special note: USDT on Ethereum has a non-standard approve function (returns void instead of bool) — this breaks the standard ERC-20 interface. Use OpenZeppelin's SafeERC20 or handle both cases.

How to Avoid Race Conditions When Accepting ETH?

A race condition arises when two users send payments simultaneously with the same amount. The solution is unique addresses (described above). Additionally, use database-level locking when processing transactions. For example, use SELECT ... FOR UPDATE when crediting funds.

What If the User Sends the Wrong Amount?

In such a case, do not credit the funds. Configure a sweep (return) to the sender's address after a certain number of blocks. Alternatively, contact the user to clarify. We guarantee your funds will not be lost.

Case Study: Reducing Payment Latency

On one project, we replaced a polling-based monitoring system with Alchemy Notify webhooks. The result: average payment confirmation time dropped from 8 seconds to 0.8 seconds, and we eliminated missed transactions due to backend restarts. The client processed over $2M in the first quarter without a single support ticket related to payment failures.

What's Included in Turnkey Setup?

  • Generation of unique deposit addresses via HD wallet
  • Webhook monitoring through Alchemy/QuickNode or self-hosted listener
  • Confirmation logic with configurable thresholds
  • Automatic sweeping of funds to the main wallet
  • Basic API for integration with your backend (Node.js, Python, Go)
  • Documentation and support during launch

Contact us to evaluate your project: email or Telegram, get a consultation within one day. Order the setup now.

Company Experience

We have completed 30+ projects integrating Ethereum payments, processing over $10 million in cryptocurrency. Our team has long-standing market presence in blockchain development. Our engineers are certified in Solidity and Rust.

Step-by-Step Self-Implementation Guide

  1. Generate a master seed (12-24 words) and store it in an HSM.
  2. Deploy a server with ethers.js or viem.
  3. Implement the getDepositAddress function for address generation.
  4. Subscribe to webhooks from Alchemy Notify or QuickNode Streams.
  5. Set confirmation thresholds based on the amount.
  6. Set up the sweeping process — periodic transfer of balances to the main wallet.
Details on Setting Up SafeERC20

Use the OpenZeppelin library: SafeERC20.safeTransfer() and SafeERC20.safeApprove(). This protects against non-standard token implementations, such as USDT, which returns void instead of bool.