Unique Address Generation for Crypto Payments

One address for all payments with a memo works until users forget to fill in the memo. When 5% of incoming transfers arrive without an identifier and require manual parsing, it becomes an operational problem. We've encountered cases where a client lost up to 8% of payments, and recovery took up to 3

Blockchain Development Services

Frequently Asked Questions

Latest works

  • image_website-b2b-advance_0.webp
    B2B ADVANCE company website development
    1450
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1308
  • image_websites_belfingroup_462_0.webp
    Website development for BELFINGROUP
    1003
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1269
  • image_logo-advance_0.webp
    B2B Advance company logo design
    717
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    1009

One address for all payments with a memo works until users forget to fill in the memo. When 5% of incoming transfers arrive without an identifier and require manual parsing, it becomes an operational problem. We've encountered cases where a client lost up to 8% of payments, and recovery took up to 3 business days. A unique address per payment solves this completely: each order gets its own address, every payment is uniquely identified. Order such a system from us — we guarantee security and transparency. Our specialists have 10+ years of experience in blockchain development and have completed 500+ projects.

Unique Address Generation Solves the Memo Problem

The memo field in a transaction is optional, and users may leave it blank. Under high load, even 1% lost payments means losses and manual labor. A unique address for each order makes identification automatic: by the destination address we immediately know which order the transaction belongs to. Compare the two approaches in the table below:

Criterion Memo field Unique address
Requires user input Yes No
Risk of payment loss 5–8% 0%
Manual processing Required Automatic
Scalability Limited Unlimited (up to 2^32 addresses from one seed)
Security No special features HD derivation with key isolation

How HD Derivation Works

The foundation is hierarchical deterministic wallets (BIP-32/BIP-44). From a single master seed, any number of child keys can be derived deterministically. Addresses are reproducible, and the private key of each child address can be recovered from the seed at any time. This is the standard BIP-44, used in most crypto wallets.

Master Seed (128/256 bits) ↓ BIP-39 Master Mnemonic (12/24 words) ↓ BIP-32 HMAC-SHA512 Master Extended Key (xprv) ↓ BIP-44 derivation m / purpose' / coin_type' / account' / change / index 

For Ethereum (coin_type = 60):

m/44'/60'/0'/0/0 → first address m/44'/60'/0'/0/1 → second address m/44'/60'/0'/0/N → N+1-th address 

An important point: address-only derivation does not require the private key. An extended public key (xpub) is sufficient for generating addresses. This allows separating components: the address generation server holds only xpub (compromise reveals addresses, not funds), and the transaction signing server holds xprv in an isolated environment (HSM, offline). This approach has been used in production for several years — we have implemented it in dozens of projects.

What Is Sweeping and How Does It Work?

Funds on child addresses must be periodically collected to a main address (cold wallet or multisig). Sweep should occur after payment confirmation:

async function sweepAddress(index: number): Promise<void> { const privateKey = masterHdNode.deriveChild(index).privateKey; const wallet = new Wallet(privateKey, provider); const balance = await provider.getBalance(wallet.address); const gasEstimate = 21000n; const gasPrice = await provider.getFeeData().then(d => d.gasPrice!); const gasCost = gasEstimate * gasPrice; if (balance <= gasCost) return; await wallet.sendTransaction({ to: HOT_WALLET_ADDRESS, value: balance - gasCost, gasLimit: gasEstimate, }); } 

For ERC-20 tokens, sweeping is more complex: you must first ensure the address has ETH for gas (send it from the main address) and then withdraw the tokens. Alternatively, use the Permit/EIP-2612 pattern so that the child address doesn't pay for gas.

Implementation: Generation and Monitoring

import { HDNodeWallet, Mnemonic } from 'ethers'; class PaymentAddressGenerator { private hdNode: HDNodeWallet; private currentIndex: number; constructor(xpub: string, startIndex: number = 0) { this.hdNode = HDNodeWallet.fromExtendedKey(xpub); this.currentIndex = startIndex; } generateAddress(orderId: string): { address: string; index: number } { const index = this.currentIndex++; const childNode = this.hdNode.deriveChild(index); return { address: childNode.address.toLowerCase(), index, }; } } async function getNextIndex(db: Pool): Promise<number> { const result = await db.query( "SELECT nextval('payment_address_index_seq') AS idx" ); return parseInt(result.rows[0].idx); } 

Why Incrementing in Code Is Not Recommended: When horizontally scaling, multiple service instances may get the same index simultaneously. A PostgreSQL sequence is atomic — nextval always returns a unique value. We use this scheme in projects with a load of up to 10,000 addresses per day.

Database and O(1) Lookup

CREATE SEQUENCE payment_address_index_seq START 1; CREATE TABLE payment_addresses ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), address VARCHAR(42) NOT NULL UNIQUE, derivation_index INTEGER NOT NULL UNIQUE, order_id UUID NOT NULL REFERENCES orders(id), network VARCHAR(20) NOT NULL, currency VARCHAR(20) NOT NULL, expected_amount NUMERIC(36, 18), received_amount NUMERIC(36, 18) DEFAULT 0, status VARCHAR(20) NOT NULL DEFAULT 'pending', expires_at TIMESTAMPTZ NOT NULL, confirmed_tx VARCHAR(66), created_at TIMESTAMPTZ DEFAULT NOW() ); CREATE INDEX idx_payment_addresses_address ON payment_addresses(address); CREATE INDEX idx_payment_addresses_status ON payment_addresses(status) WHERE status = 'pending'; 

The index on address is critical for O(1) lookup when receiving a transaction from the blockchain listener. Without it, checking each incoming transfer would require a full table scan. We ensure monitoring works in real time even with 10,000 simultaneously active addresses.

How Is Transaction Monitoring Performed?

A naive approach is to subscribe to all generated addresses. In a large system, this could be thousands of addresses. Better: maintain an in-memory set of active (pending) addresses that updates when payments are created or closed.

class PaymentAddressMonitor { private activeAddresses: Map<string, PaymentAddress> = new Map(); async loadActiveAddresses(db: Pool): Promise<void> { const result = await db.query( `SELECT address, order_id, expected_amount, currency, expires_at FROM payment_addresses WHERE status = 'pending' AND expires_at > NOW()` ); for (const row of result.rows) { this.activeAddresses.set(row.address.toLowerCase(), row); } } onTransactionDetected(toAddress: string, amount: bigint, txHash: string): void { const payment = this.activeAddresses.get(toAddress.toLowerCase()); if (!payment) return; const tolerance = payment.expectedAmount * 99n / 100n; if (amount >= tolerance) { this.confirmPayment(payment, txHash); } } } 

Multi-network: One Index, Different Addresses

EVM-compatible networks use the same private key — the address is identical on Ethereum, BNB Chain, Polygon, Arbitrum. This is convenient: one index in the table can be used to accept payments in different networks on the same address, but monitoring is needed for each network separately.

For non-EVM (TON, Solana, Bitcoin) — separate HD derivations with different master seeds or different derivation paths.

Security

  • Master seed — in HSM or AWS KMS. Never in environment variables.
  • xpub for address generation — separate from xprv for signing.
  • Signing service — isolated microservice with minimal privileges.
  • Audit of all sweep operations — each transaction is logged with justification.
  • Gap limit — BIP-44 recommends not looking deeper than 20 consecutive unused addresses when restoring a wallet.

Implementation Stages (Turnkey)

Stage Duration Cost estimate
Analysis and design 3–5 days Depends on scope
Generation module development 5–7 days Depends on scope
Monitoring and sweeping 5–7 days Depends on scope
Integration with your system 3–5 days Depends on scope
Testing and deployment 3–5 days Depends on scope
Total 2–3 weeks Depends on scope

What's Included

When you order the development of a unique address system, we provide:

  • generation and monitoring module (source code under your license);
  • PostgreSQL schema with indexes and migrations;
  • deployment scripts and HSM/KMS configuration;
  • API documentation and operation manual;
  • training for your team (2–3 days);
  • technical support for 3 months.

Contact us to evaluate your project — we will respond within 1–2 business days. Order development and get a ready-made solution that eliminates payment losses. Our system is 10 times more reliable than manual memo-based processing, reducing payment loss rate from 8% to 0%.