Crypto Payment Confirmation System Development
Imagine a client pays an order in USDT on Polygon, but a minute later the network reorganizes — the transaction disappears. You already shipped the order, but the money never arrived. A reliable payment confirmation system is not just a hash check; it's a finite state machine with explicit state transitions and protection against all edge cases. Our implementation uses separate monitors for each network, a tolerance window to handle amount fluctuations, and idempotency at the txHash level. For example, on Ethereum PoS we require 12 confirmations (average 12-second block time), providing reliability comparable to bank clearing but 10 times faster.
We build such systems from scratch or integrate them into existing infrastructure. We rely on the EIP-1559 and Ethereum JSON-RPC API specifications for correct transaction processing. Operational cost savings on payment processing can reach $2,000 per month. The system typically pays for itself in 3–4 months. Turnkey delivery in 2–4 weeks. Contact us to discuss your scenario.
What Problem Are We Solving?
A naive implementation: receive hash → check amount → credit. It breaks at the first reorg, double spend, or when the user sends payment an hour after session expiry. Main pain points:
-
Reorg: A block is abandoned, transaction disappears. Without status rollback, you credit non-existent funds.
-
Floating point: Conversion via wei introduces rounding errors; user pays 47.50 USDT, but the system sees 47.499999.
-
Exchange fees: Transferred amount is 1–2% less than expected.
-
Session timeouts: Payment arrives after the time limit, and the address is no longer valid.
Each of these problems is resolved within a unified finite state model.
How Does the System Protect Against Reorg?
Reorg is a chain reorganization where a previously accepted block is replaced by another. On Ethereum PoS this is unlikely (1–2 block depth), on Polygon it's more common. Our approach: on every confirmation check, we fetch a fresh transaction receipt. If the receipt disappears, the status rolls back to DETECTED, the counter resets, and the monitor begins re-searching.
async function processConfirmations(paymentId: string) {
const payment = await db.findPayment(paymentId);
const currentBlock = await provider.getBlockNumber();
const receipt = await provider.getTransactionReceipt(payment.txHash);
if (!receipt) {
await db.updatePayment(paymentId, {
status: 'DETECTED',
confirmations: 0,
reorgDetected: true,
});
return;
}
const confirmations = currentBlock - receipt.blockNumber + 1;
const isConfirmed = confirmations >= payment.requiredConfirmations;
await db.updatePayment(paymentId, {
confirmations,
status: isConfirmed ? 'CONFIRMED' : 'CONFIRMING',
confirmedAt: isConfirmed ? new Date() : null,
});
}
Payments in CONFIRMING status are rechecked every N blocks — we never trust stale data.
What If the User Sends Less or More?
Due to fees and floating point, the transaction amount rarely matches the expected amount exactly. A sensible tolerance window solves this. Verification code:
function isAmountSufficient(
received: bigint,
expected: bigint,
toleranceBps: number = 50
): 'exact' | 'underpaid' | 'overpaid' {
const tolerance = expected * BigInt(toleranceBps) / 10000n;
const min = expected - tolerance;
const max = expected + expected / 10n;
if (received >= min && received <= max) return 'exact';
if (received < min) return 'underpaid';
return 'overpaid';
}
On underpaid, the system notifies the operator; on overpaid (up to 10%), it accepts the payment and credits the surplus to the user's balance or generates a refund.
Payment State Machine
Each payment passes through strictly defined states:
PENDING → DETECTED → CONFIRMING → CONFIRMED → SETTLED
↓ ↓
EXPIRED UNDERPAID / OVERPAID
↓
REFUNDED
| State |
Description |
| PENDING |
Address issued, waiting for transaction |
| DETECTED |
Transaction in mempool (0 confirmations) |
| CONFIRMING |
1+ confirmations, not yet final |
| CONFIRMED |
Confirmation threshold reached, amount correct |
| SETTLED |
Business logic executed (order created, subscription activated) |
| EXPIRED |
Timer elapsed, no transaction received |
| UNDERPAID |
Transaction received but amount less than expected |
Blockchain Monitor Architecture
Monolithic monitoring of all networks in a single process is a bad idea. We use a separate worker per network with an independent retry mechanism. Implementation for EVM networks:
Basic monitor code (EVM)
interface ChainMonitor {
network: string;
start(): Promise<void>;
stop(): void;
onTransaction(handler: (tx: IncomingTransaction) => Promise<void>): void;
}
class EvmChainMonitor implements ChainMonitor {
private provider: ethers.JsonRpcProvider;
private watchedAddresses = new Set<string>();
async start() {
const activePayments = await db.query(
"SELECT address FROM payments WHERE status IN ('PENDING', 'DETECTING', 'CONFIRMING')"
);
activePayments.rows.forEach(p => this.watchedAddresses.add(p.address));
this.provider.on('block', async (blockNumber) => {
await this.processBlock(blockNumber);
});
}
private async processBlock(blockNumber: number) {
const block = await this.provider.getBlock(blockNumber, true);
for (const tx of block.transactions) {
if (tx.to && this.watchedAddresses.has(tx.to.toLowerCase())) {
await this.handleNativeTransfer(tx, blockNumber);
}
}
await this.scanErc20Transfers(blockNumber);
}
}
Confirmation Requirements for Different Networks
| Network |
Recommended confirmations |
Average block time |
| Ethereum (L1) |
12 |
~12 s |
| Polygon (PoS) |
64 |
~60 s |
| BNB Chain |
15 |
~3 s |
| Arbitrum |
12 |
~0.5 s |
| Base |
12 |
~2 s |
Idempotency and Duplicate Protection
One txHash must be credited exactly once. We use INSERT with ON CONFLICT DO NOTHING: if the same hash already processed, it returns an empty result.
INSERT INTO payment_transactions (payment_id, tx_hash, amount, block_number)
VALUES ($1, $2, $3, $4)
ON CONFLICT (tx_hash) DO NOTHING
RETURNING id;
Notifications and Webhooks
After transition to CONFIRMED — immediate notification to external systems via a queue (Bull/BullMQ) with exponential backoff. Direct HTTP call in the block handler would lose events on failures.
async function dispatchPaymentConfirmed(payment: Payment) {
await eventBus.emit('payment.confirmed', {
paymentId: payment.id,
orderId: payment.orderId,
amount: payment.receivedAmount,
txHash: payment.txHash,
});
if (payment.webhookUrl) {
await webhookQueue.add('payment-webhook', {
url: payment.webhookUrl,
payload: { event: 'payment.confirmed', data: payment },
}, {
attempts: 5,
backoff: { type: 'exponential', delay: 2000 },
});
}
}
Process and Deliverables
-
Analysis — we dissect your business requirements, number of networks, tokens, refund scenarios.
-
State machine design — refine transitions, tolerance, confirmation thresholds.
-
Implementation — code monitors, handlers, webhooks, integration tests.
-
Testing — cover edge cases: reorg, underpaid, timeout, double-spend.
-
Deployment and monitoring — deploy in your infrastructure, set up alerts.
What is included in the result:
- Source code repository with launch instructions.
- API and architecture documentation.
- Database migrations.
- Load tests and simulation scripts.
- Support for 2 weeks after launch (extended support on request).
Order the development of a system for your project — we will prepare a detailed estimate within 1 day.
Timeline and Guarantees
Typical delivery time is 2 to 4 weeks, depending on the number of networks and business logic complexity. Pricing is calculated individually, but we guarantee transparent cost breakdown. We have been working with blockchain projects for over 5 years and have implemented dozens of such systems. We guarantee stable operation under a load of up to 10,000 transactions per hour.
Get a consultation: write to us, and we will evaluate your project for free.
Blockchain Infrastructure Deployment: Nodes, RPC, Indexing
Subgraph fell at 3:47 AM. By morning users saw outdated balances, transactions "hung" in the UI, support received 47 tickets in an hour. Cause: the handler in the subgraph failed on a transaction with a non-standard event log — and the entire index stopped. We have encountered such situations dozens of times. Our experience shows: blockchain infrastructure does not forgive gaps in observability. Guaranteeing uptime without multi-layered monitoring and fault-tolerant architecture is impossible. Over 8 years working with Ethereum, Polygon, and Solana, we have developed an approach that allows predictable deployment of infrastructure of any scale — from a single node to a multichain grid with dozens of subgraphs.
RPC Layer Architecture
Every dApp interaction with the blockchain goes through RPC — the JSON-RPC API provided by a node. Three options:
Managed providers — Alchemy, QuickNode, Infura, Ankr. Minimal operational costs, SLA, built-in monitoring. Limits: rate limits (Alchemy Free: 300 RU/sec), vendor lock, potential downtime during provider incidents. For most projects — the right choice at the start.
Self-owned nodes — full control, no rate limits, no third-party dependence. Cost: archive Ethereum node requires 2.5–3TB SSD, a strong server, and DevOps support. Sync from scratch on Ethereum via Geth/Nethermind — 3–7 days. Justified under high load or latency requirements.
Hybrid — self-owned node as primary, managed provider as fallback. Standard for protocols with high TVL. Proper load balancing can reduce costs by 20–30% compared to pure managed setup. Under high monthly request volume, hybrid saves significantly.
| Provider |
Strength |
Limitation |
| Alchemy |
Supernode, Enhanced APIs, webhooks |
Expensive on high-volume |
| QuickNode |
Low latency, multi-chain |
More expensive than Alchemy on basic plan |
| Infura |
Historical reliability |
Rate limits on free, one major incident halted half of DeFi |
| Ankr |
Cheap, 40+ chains |
Less stable |
How to Set Up an RPC Layer Without a Single Point of Failure?
At least two providers, DNS round-robin with health check every 5 seconds, automatic fallback when latency >500 ms. In practice, this gives 99.99% availability during any provider failure. For protocols with high TVL, we recommend a custom HA-proxy (nginx or Envoy) in front of two managed providers.
Why Is a Hybrid RPC Scheme More Cost-Effective Than Pure Managed?
At high request volumes, managed providers can be very expensive; a hybrid using a self-owned node as primary and a managed fallback cuts costs significantly without losing SLA.
Ethereum Node Clients
Execution clients: Geth (most used), Nethermind (C#, fast sync), Besu (Java, enterprise), Erigon (fastest sync, efficient archive mode ~2TB instead of 3TB).
Consensus clients (post-Merge): Lighthouse (Rust), Prysm (Go), Teku (Java), Nimbus (Nim). Each node after The Merge requires a pair of execution + consensus clients.
For DevOps: eth-docker — Docker Compose configurations for all client combinations. Setting up monitoring via Grafana + Prometheus is mandatory; a standard dashboard is available in each client's repository.
The Graph: Event Indexing
The Graph Protocol — decentralized indexing. A subgraph describes which events from which contracts to index and how to transform them into a GraphQL schema.
Subgraph structure:
-
subgraph.yaml — manifest: contract addresses, startBlock, events to handle
-
schema.graphql — GraphQL schema of entities
-
src/mapping.ts — AssemblyScript event handlers
dataSources:
- kind: ethereum
name: UniswapV3Pool
network: mainnet
source:
address: "0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640"
abi: UniswapV3Pool
startBlock: 12370624
mapping:
eventHandlers:
- event: Swap(indexed address,indexed address,int256,int256,uint160,uint128,int24)
handler: handleSwap
AssemblyScript handlers — not TypeScript. No nullable types, no closures, no many standard APIs. An error in the handler stops the subgraph indexing on that transaction. Important: add try-catch for operations that can fail (e.g., store.get() for an entity that may not exist).
How to Avoid Subgraph Indexing Stops?
Graph Node logs are monitored in real-time; on hasIndexingErrors = true an alert fires and an automatic node restart (via systemd or Kubernetes). Typical downtime on error — 150–300 seconds to recover. Additionally, for production we set up a watchdog that restarts Graph Node if subgraph lag exceeds 50 blocks.
Choosing Between Hosted Service and Decentralized Network
Graph Hosted Service (free, centralized) is deprecated in favor of Subgraph Studio + Graph Network. For production: deploy on Graph Network with GRT curation signal — the subgraph gets indexers proportional to curation.
Alternatives to The Graph: Ponder (TypeScript, self-hosted, easier to debug), Envio (ultra-fast indexer, supports EVM + non-EVM), Subsquid (TypeScript, own network), Moralis Streams (managed, webhook-based). Our experience shows: for high-load projects with unique logic, Ponder or Envio are more effective — they give full control over the process and do not require GRT tokenomics.
Webhooks and Real-Time Notifications
Alchemy Webhooks and QuickNode Streams allow receiving events in real-time via HTTP webhook or WebSocket. For monitoring addresses, new transactions, mints — this is faster than polling RPC.
Tenderly — platform for monitoring and alerts. You can set up an alert for a specific contract event, balance change, function call with certain parameters. Transaction simulation via Tenderly API is invaluable for debugging.
Monitoring and Observability
Minimum monitoring stack for a protocol:
On-chain: OpenZeppelin Defender Sentinel — watches contract events, triggers webhook or Autotask when conditions are met. Forta Network — community-maintained bots detect anomalies (large withdrawals, flash loans, governance attacks).
Infrastructure: Grafana + Prometheus for nodes, Datadog or Grafana Cloud for managed metrics. Alerts on: node is 10+ blocks behind, RPC latency >500ms, subgraph lag >100 blocks.
Uptime: Better Uptime or PagerDuty on RPC endpoint and subgraph health endpoint (The Graph provides _meta { hasIndexingErrors, block { number } }).
Why Is Monitoring Without Tenderly Insufficient?
Tenderly provides transaction simulation and detailed traces — critical for debugging subgraph and smart contract errors. Forta focuses on network anomalies, not your infrastructure. The combination of Tenderly plus a custom Grafana dashboard covers 90% of incident scenarios.
Multichain Infrastructure
A protocol on 5 chains = 5 separate RPC endpoints, 5 subgraphs, 5 monitoring configs. Manageable but requires deployment automation.
For subgraph multi-network deployment: graph deploy --network mainnet, graph deploy --network arbitrum-one etc. with a unified codebase and network-specific addresses in separate config files.
Chainlink CCIP and LayerZero for cross-chain messaging require monitoring of both chains and transactions on intermediate relayers. A reorg on the source chain after a confirmed mint on the target chain is a classic bridge problem. Solution: wait for finality (on Ethereum ~15 minutes after Merge for economic finality) before confirming on the target chain.
Infrastructure Setup Process
- Audit current stack — determine chains, request volume, latency and availability requirements.
- Architecture design — select providers, load balancing, redundancy.
- Subgraph development — manifest → schema → handlers → testing on local Graph Node → deploy to testnet → mainnet.
- Monitoring configuration — Tenderly alerts, Grafana dashboard, PagerDuty integration.
- Documentation and runbook — what to do when: subgraph falls behind, RPC downtime, node desync.
- Handover to operations — team training, access transfer, first month support.
What's Included
- Deployment of managed or self-hosted Ethereum, Polygon, BNB Chain nodes
- RPC layer setup with primary/fallback and load balancing
- Subgraph development and deployment for your protocol
- Monitoring connection (Tenderly, Grafana, alerts)
- Runbook and operations documentation
- Team training (up to 4 hours online)
- 30-day support after delivery
Timeline
| Task |
Duration |
| RPC and basic monitoring setup |
1–2 weeks |
| Subgraph for one protocol |
2–4 weeks |
| Self-hosted node with monitoring |
2–3 weeks |
| Full infrastructure (multi-chain, monitoring, runbooks) |
6–10 weeks |
All projects are managed in a GitHub/GitLab repository with CI/CD; configuration code stays with you. Order infrastructure deployment — we'll show how to cut costs by 20–30% without losing reliability. Get a consultation — we'll demonstrate how we deployed infrastructure for a protocol with large TVL on Ethereum and Arbitrum. Contact us.