Ethers.js Integration: Where Frontend with Blockchain Breaks
Connecting a wallet to the interface looks like a few Ethers.js calls: request access, read address, show balance, send transaction. It breaks not during testnet checks but when a real user arrives, the network changes, or confirmation is delayed for minutes.
The first point of failure is a transaction signature hang. The wallet opened a confirmation window, the user closed it or the extension lost connection, and the interface remained in a "sending" state: without timeout, cancellation, or proper retry.
The second is a mismatch between balance and network state: the interface shows a stored value, while the transaction has already passed, has not passed yet, or went to another network. The user sees one set of numbers, the node responds with another.
The third group is repeated sends and lost operations. A double click on the button, repeated signature after page reload, connection break between request and response — and the network gets two identical transactions or none, although the user is sure otherwise.
Without an operation log and a unique transaction identifier, reconstructing the picture after the fact is impossible.
All three failures live in the frontend-blockchain integration layer, not in contract logic: their cost is lost operations, duplicate charges, and undermined trust in the product.
We break down how this is closed — from signature state management to network reconciliation and replay protection.
What Changes in Numbers: Response, Failure Rate, Release Time
We measure the effect before and after rewriting the frontend to the current branch of Ethers.js.
Interface response to a typical action — reading balance, calculating fee, checking allowance — is reduced from two seconds to fractions of a second: requests go through a provider pool with retries, heavy reads are cached, and signature state is updated by events instead of constant polling.
The share of failed transactions drops severalfold. The cause of failures is usually one: the signature goes out with a stale nonce or underpriced fee, and the user sees a wallet error instead of confirmation.
We synchronize the nonce right before signing, calculate the fee from the latest block, and show an understandable reason instead of a technical string — the number of manual incident investigations drops from dozens per month to single digits.
Connection resilience to the network is a separate value. Switching between nodes by health state, a cache of the last known state, and resending the same hash keep the interface working when the provider fails: the user loses neither entered data nor an unfinished signature.
Release time for updates is also measurable.
Types and contract descriptions are generated automatically, signature handling is moved to a separate layer, and integration checks are run on a local copy of the network — an interface edit goes to release in hours, not weeks of coordination between frontend and contracts.
Connection Modes: Hosted Provider, Own Node, Hybrid
Network access mode determines how the interface behaves when a node responds with delay or returns an error.
The problem shows not in tests but on live traffic: the number of concurrent sessions grows, and some requests for reading balances and subscribing to events start dropping. Below are three schemes that are actually chosen between.
| Mode | When Suitable | Limitations |
|---|---|---|
| External network provider | Quick start, no own infrastructure, moderate request flow | Shared rate limits, some methods unavailable, uptime outside your control |
| Own node | High load, privacy requirements for requests, need archival data and rare methods | Need hardware capacity, network synchronization, and constant supervision |
| Mixed connection scheme | Public product with load that is hard to predict | More complex configuration and monitoring: two channels instead of one |
Hybrid is the default scheme for a loaded product: the primary channel is held by an own node, the backup switches to an external provider when the first degrades.
Switching is configured in the common connection module, so changing mode does not touch components on ethers.js, event subscriptions, and transaction signing.
This gives a path without rewriting the frontend: start on an external provider, move to an own node when request flow outgrows shared limits.
Work Order: Audit, Scheme, Setup, Verification, Handover
We start with a review of the current code: without it, you cannot see where the signature is duplicated on repeated click, and where balance is read bypassing checks. Then — interaction scheme, environments, scenarios, and handover.
-
Frontend audit. We look at how the application accesses the network: where providers are created, what happens on account change, connection loss, and rejected signature. Separately mark checks that live only in the browser.
-
Network interaction scheme. We map which calls go through the user's wallet, which through our node, where contract events are read. The scheme becomes a common language for frontend, backend, and your team.
-
Confirmation and failure model. We agree how many confirmations we wait for before "completed" status, what we show on transaction replacement, and how we phrase errors instead of raw node responses.
-
Environment setup. We separate configurations for local run, testnet, and mainnet: contract addresses, endpoints, keys, environment variables. Local run gives reproducibility without real transactions.
-
Client connection. We configure ethers.js for different wallets, add network switching, retries on interruption, and graceful disconnect without losing entered data.
-
User scenario verification. We run connection, signing, sending, cancellation, retry, and history reading — with automated tests on testnet and manually on supported wallets.
-
Handover to team. We deliver code with tests, the interaction scheme, environment instructions, and the procedure for when the provider is unavailable.
Handover Artifacts: Configurations, Schemes, Policies
After delivery, the repository and documentation contain not only integration code on ethers.js, but also everything your team needs for independent work. We assemble the kit so a new engineer can bring up the environment and release a change without correspondence with us.
- Environment configurations — sets of variables for local stand, testnet, and production network: contract addresses, network identifiers, node connection. Changing environment does not require code edits.
- Data interaction scheme — which calls go through the provider, where event subscriptions are, what is cached, and how block lag is handled after reconnect.
- Error handling policy — failure classes: rejected signature, insufficient funds for fee, connection loss, transaction revert. For each — retry, interface text, logging point.
- Key management policy — where secrets are stored, who has access, what does not go into client code.
- Fault tolerance scheme — primary and backup route to the network, interface behavior when the node is unavailable.
- Handover documentation — local run, test scenarios, procedure for updating contract addresses.
- Acceptance checklist — checks by which your team accepts the work.
The kit removes the main handover risk: while knowledge remains only with the contractor, the team cannot release a single change on its own.
Case: User Account with Wallet Connection and Payment
Where Payments Broke
The user account with wallet connection was assembled as a set of independent places: connection in the header, payment on a separate page, transaction history in a third component.
Each created its own connection to the network, listened to events only while open, and learned about network change from failure.
A user with an unsuitable network saw successful signature and an empty screen — only about a third of payments reached network confirmation, the rest hung in unknown status.
What We Changed and What Metrics Showed
Connection was moved into a single client on Ethers.js with shared state: address, network, signature, and transaction status are stored in one place and survive page reload.
Before payment, the network is checked in advance, with a suggestion to switch, sending goes by idempotent key, and repeated status check on return to the account pulls the transaction result even if the tab was closed at the confirmation stage.
The share of successfully completed payments grew from a third to 90%, and "paid — status did not update" requests fell from dozens per month to single digits.
Duplicates were closed separately: repeated click and reload return the same transaction rather than creating a second one. Transaction history in the account now reconciles with network data, and the purchase scenario no longer depends on when the user minimized the browser.
Ethers.js Integration or Direct Node Calls — What to Choose
Short answer: for most tasks — wallet connection, state reading, sending signed operations — Ethers.js is enough. The library handles serialization, signature work, node error parsing, and compatibility with new network versions for us. Direct node calls are justified only when a requirement appears that the library does not have.
Library integration wins on timelines: an engineer describes the contract and connection in a matter of days, and some future changes come with library updates themselves.
An own interaction layer is needed where the library starts to get in the way — when routing between multiple networks, caching state between screens, or custom retry and rollback logic when the node returns an error.
| Option | When Suitable | What We Pay in Maintenance |
|---|---|---|
| Library integration | Typical product: wallet, reading, signing, sending | Nonstandard scenarios have to be worked around; third-party changes need tracking |
| Direct node calls | Custom signature scheme, strict request control, multichain | All logic and failure parsing are on us, higher requirements for engineers |
| Library and own interaction layer | Product grows, scenarios and networks become many | Higher initial effort, but fewer edits later |
The difference shows not at start but over distance. We start with library integration, and allocate an own interaction layer for a specific need — this way connection takes days, not weeks.
Technical Questions Asked Before Start
Before start, teams ask the same questions — not about library choice, but about how the system behaves when something goes wrong. Below are four concerns that come up most often, and how we close each.
What to do if the user rejected signature?
A rejected signature is not a failure but a standard scenario, so the handler must distinguish wallet refusal from network refusal. On rejection, the interface returns to initial state, the form remains filled, the transaction does not go out and does not enter the retry queue. Otherwise a person sees an error where nothing broke.
How to know that a transaction is actually confirmed?
After sending, the signature is recorded, but nothing is guaranteed until inclusion in a block. We listen for the confirmation event and show an intermediate status, and on chain reorganization the interface rolls back to "not completed" state, not "success". This way the user is not left with a false result on screen.
What happens if network rules change?
A network rule change, contract update, or move to another network breaks some calls.
Therefore we move addresses and parameters into configuration, at startup verify the network and contract interface version, and show a mismatch with a clear message instead of a technical error in the console.
Can already working code be connected?
Yes: we embed integration into the existing application structure, preserving routing, state, and styles, without rewriting from scratch.
Support for ready code starts with a review of data layers so connection does not affect neighboring screens, and ends with handover of instructions to your team.
If any of these scenarios looks different for you — describe the task, we will return with an estimate and work plan.Let's Discuss Your Task and Return with Estimate and Plan
For the task estimate for integration to be accurate rather than "roughly", minimum input data is needed.
From it, scope is immediately visible: how many contract calls, which events to read, where transaction signature is required, and where a network request is enough.
- contract addresses and their descriptions indicating networks — mainnet and testnet; from them we count the number of calls and list of events;
- user scenarios: where the wallet is connected, at what moment the transaction is signed, how we show waiting and result;
- supported wallets and connection methods — browser extension, mobile, hardware;
- access: network node or provider key, test environment, access to client-side repository;
- data reading requirements — whether event indexing and user transaction history are needed on our side;
- error policy: rejected signature, transaction revert, network change, connection loss with node;
- your requirements for checks and release of updates: which environments and how often.
After discussing the task, we assemble a work plan: fix requirements, design the network access layer on ethers.js, implement, cover with checks on testnet.
A separate stage is project handover — source code, environment configurations, and documentation for connecting new contracts, so your team can develop integration itself. Work starts after the plan and scope are agreed.
Describe the task in free form and attach what you have — we will return with an estimate and work plan.







