logo

Adding Support for a New EVM-Compatible Blockchain (Polygon, BSC, Others)

A collection of colorful icons displayed on a black background, showcasing various themes and designs.

The first time you add a new EVM chain, it feels like a quick win.

You swap an RPC URL, update a chainId, and watch transactions land on a cheaper network. Then the bug reports arrive with a familiar rhythm. Balances load slowly on one chain and instantly on another. A transaction that was “sent” never appears on the explorer. Fee estimates look reasonable, yet the transaction sits pending until the user retries three times and accidentally queues duplicates.

EVM compatibility gets you a shared instruction set. It does not guarantee shared operational behavior. Adding a chain is really an exercise in defining what your app assumes about the network, and then making those assumptions explicit.

What “supporting a new chain” changes in your app

“Support” sounds like a configuration flag. In practice, it touches security boundaries, transaction construction, and UX flows. The chain can be EVM-compatible and still behave differently at exactly the points your users feel.

Chain identity: chainId as a security boundary

chainId is not metadata. It is part of transaction signing rules, and it is the foundation of replay protection across EVM networks. EIP-155 introduced chainId into the signing payload, so a valid transaction on one chain cannot be replayed on another chain that shares transaction formats.

This is why chain support begins with one unglamorous rule: treat chainId as a security input, not a UI label. If your app can end up signing with the wrong chainId, you will eventually produce a signature that fails validation, or you’ll produce a signature that the user never intended for that chain. The fix is less about better retries and more about reducing ambiguity in how the app decides “which network am I on” at the moment of signing.

RPC behavior: The same JSON-RPC surface with different realities

Two chains can expose the same methods and still differ in performance characteristics that matter. Log queries can behave differently. Some networks enforce stricter rate limits. Some nodes respond inconsistently under load, especially around bursty patterns like token balance screens or NFT galleries.

From an engineering perspective, the key shift is to treat RPC as a dependency with a contract: timeouts, fallbacks, and a clear error taxonomy. A chain integration that succeeds in staging can fail under real traffic simply because request volume and concurrency change.

Fees and transaction types: Legacy gasPrice vs EIP-1559 fields

Fee behavior is where “EVM-compatible” most often misleads teams. Ethereum mainnet uses EIP-1559’s dynamic fee market, which introduces maxFeePerGas and maxPriorityFeePerGas and relies on a base fee mechanism.

Some EVM chains follow that model closely. Others still behave like legacy Ethereum, where gasPrice is the main fee input. BNB Smart Chain, for example, has historically been treated as a legacy-fee network in many application stacks, and it also introduced its own fee-burning mechanism through BEP-95 rather than adopting Ethereum’s EIP-1559 model in the same way.

Your integration needs a decision point: which fee fields does this chain expect, and which fields does your signer produce by default? If your code blindly sends EIP-1559 fields to a chain that interprets fees differently, failures can look random. If it blindly sends legacy fields everywhere, users on EIP-1559 chains can end up overpaying or getting stuck during base fee spikes.

The minimum chain configuration you need to stop guessing

A new chain integration goes off the rails when “configuration” is spread across code paths. One screen uses a default provider. Another uses a hard-coded URL. A third relies on the wallet’s current network and hopes it matches your backend. You want a single definition of the chain that every layer can consume.

RPC URLs, fallbacks, and timeouts that reflect production traffic

A single RPC URL is a single point of failure. Even with reputable providers, outages and throttling happen, and they are often method-specific. You don’t need a complicated routing layer on day one, yet you do want a structure that supports it later.

At minimum, a chain definition should include primary and secondary RPC endpoints, plus a timeout policy that you can change without touching feature code. The timeout is part of product behavior. A 30-second hang on a balance screen is not “network reality.” It is a missing decision.

A useful pattern is to separate read paths from write paths. Reads can spread across providers and tolerate caching. Writes should prefer the wallet’s provider when user signatures are required, because the wallet already has the account context.

Block explorer links and address formatting rules

Explorers are part of the UX even when you don’t build them. Users rely on them for trust. If your app links to the wrong explorer or shows a transaction hash that resolves to a different chain, you get support tickets that feel like fraud reports.

Chain configuration should include explorer base URLs for transactions and addresses, and it should define what “copyable identifiers” look like. Addresses remain 20-byte values across EVM chains, yet the meaning users attach to them depends on context. Showing a Polygon address in a BSC view is a subtle error that creates real confusion.

Wallet connectivity on a new chain: UX path your code must follow

Chain support often fails in the handoff between your app and the user’s wallet. Wallet UX is governed by conventions that have become standards, and your app should meet them where they are instead of inventing a parallel flow.

EIP-1193 provider behavior and network-change events

Most browser wallets expose an injected provider interface that follows EIP-1193, which standardizes a minimal provider API and event-driven behavior for wallet interoperability.

That matters for chain support because chain switching is not just a configuration toggle. Your app must respond to provider events that indicate the user changed networks, disconnected accounts, or switched accounts. If you miss those signals, you can keep showing balances from one chain while sending transactions to another.

A practical internal rule helps: treat provider state as authoritative for signing, and treat your app state as a cache that must be invalidated on provider changes. This prevents the common mismatch where the UI still believes it’s on Chain A while the wallet is already on Chain B.

Adding a chain to wallets with wallet_addEthereumChain

If you want the user to add a new chain to their wallet directly from your app, EIP-3085 defines wallet_addEthereumChain, a wallet-namespaced RPC method that requests the wallet to track a given chain and show it in the wallet UI.

The important nuance is that wallets can reject the request, and wallets may impose requirements on the fields you provide. A chain add flow should be built as a polite suggestion, not a guaranteed operation. It should also include validation of chain data because the RPC URL you propose is a privacy and security surface. EIP-3085’s security considerations explicitly discuss chain IDs and RPC URLs as sensitive inputs.

Shipping safely: Validation steps that prevent silent failures

A chain integration often “works” in the sense that a transaction can be sent. The real goal is that it behaves predictably under stress, and that failures are explainable.

Transaction signing sanity checks keyed to chainId

Signing libraries will happily sign a transaction object that contains inconsistent fields. Your module should be stricter than the library. It should validate chainId presence, fee field coherence, and nonce assumptions before signing.

EIP-155 exists because chain identity must be bound into the signature. If you treat chainId as a UI choice instead of a signing input, you create a category of bugs that look like RPC flakiness.

Confirmations, reorg sensitivity, and receipt interpretation

Different chains exhibit different reorg characteristics and finality expectations. Even when block times are fast, “fast” does not mean “final.” A good UX avoids promising finality too early, and it treats confirmations as a progression rather than a binary state.

Receipt interpretation also requires care. A successful receipt indicates execution success, yet user expectations include more than execution. They expect indexers to pick up events, explorers to show the transaction, and balances to update. That last step is often delayed by caching and indexing layers, especially on high-throughput networks.

Observability: Which metrics explain “stuck” and “dropped”

A chain integration becomes manageable when you can see its failure modes. Track error rates by RPC method. Track 429s separately from 5xx responses. Track nonce errors and replacement transaction patterns. These are the signals that distinguish “provider throttling” from “our code is building invalid transactions.”

If you only track “requests per minute” and “transactions sent,” you will be blind at the exact moment users lose trust.

Limitations and edge cases that show up after launch

Provider limits, archival reads, and log range surprises

Many applications start with basic reads: balances, allowances, and recent transfers. Then the product asks for history. Token transfer history often leads to eth_getLogs queries over broad ranges. That can be expensive, slow, and sometimes rate-limited aggressively, depending on the provider and chain.

This is also where archival requirements appear. If your product needs to query historical state at older block heights, archive access becomes relevant, and the operational cost can change meaningfully. Ethereum ecosystem docs describe archive nodes as storing the full historical state, with large storage requirements that vary by client.

A practical integration rule is to decide early whether your app will rely on raw RPC log scans for history, or whether it will rely on an indexer. Many teams start with RPC scans and later migrate. The migration is easier when you treat history as a distinct subsystem, not a “slightly bigger balance query.”

Chain-specific quirks: Fee markets, mempools, and method support

Even within EVM compatibility, chain policies differ. Fee markets differ. Mempool behavior differs. Some chains accept certain transaction types more consistently than others, and some environments rely on legacy fee fields longer.

BNB Smart Chain is a good example of why “fee behavior” should be explicit. BEP-95 describes a mechanism where a portion of gas fees is burned, which affects how the ecosystem discusses fees and incentives. Separately, developer discussions and chain docs around EIP-1559-style base fee behavior on BSC illustrate that assumptions from the Ethereum mainnet do not always transfer cleanly.

Your module should focus on one thing: produce valid transactions for the chosen chain, then report failures in a way that leads to fixes. Avoid building UX that implies every chain behaves like the Ethereum mainnet.

Self-hosted nodes and operational drag

Self-hosting is tempting when provider limits become painful or when compliance requirements tighten. It also turns your team into a node operator. Running an Ethereum node involves both an execution client and a consensus client, and it introduces storage and monitoring responsibilities.

You do not need to self-host to support a new chain. You do need to design your integration so that self-hosting can be added later without rewriting the app. That is mostly about isolation: one chain config layer, one RPC routing layer, and one signing layer.

Conclusion

Adding support for a new EVM-compatible chain is less about swapping endpoints and more about making network assumptions explicit. chainId belongs in the security model because replay protection binds it into signatures. Wallet connectivity belongs in the UX model because EIP-1193 provider behavior and EIP-3085 chain-add flows shape how users move between networks. Fees belong in your transaction model because legacy and EIP-1559-style fields do not translate cleanly across chains.

When the integration is designed as a small system – one chain definition, one RPC strategy, one signing policy – new networks become incremental work rather than a recurring source of production mysteries.