Skip to content

Commit 0d74e8c

Browse files
authored
Optimize polling (#125)
* add multiple RPCs * adding polling * make interval dynamic per chain * format * format * refresh RPCs with working ones that are tested * add our custom alchemy ones
1 parent 7ddabb0 commit 0d74e8c

4 files changed

Lines changed: 204 additions & 4 deletions

File tree

src/app/components/ENSIntegration.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -502,7 +502,7 @@ const ENSIntegration: React.FC<ENSIntegrationProps> = ({ swarmReference, onClose
502502
setSuccess('Commitment submitted! Waiting for confirmation...');
503503

504504
// Wait for transaction confirmation
505-
await publicClient.waitForTransactionReceipt({ hash: commitHash });
505+
await publicClient.waitForTransactionReceipt({ hash: commitHash, pollingInterval: 6_000 });
506506
console.log('✅ Commitment confirmed');
507507

508508
// Store commitment data for registration step
@@ -574,7 +574,7 @@ This waiting period is required by ENS to prevent front-running attacks where so
574574
setSuccess('Registration submitted! Waiting for confirmation...');
575575

576576
// Wait for transaction confirmation
577-
await publicClient.waitForTransactionReceipt({ hash: registerHash });
577+
await publicClient.waitForTransactionReceipt({ hash: registerHash, pollingInterval: 6_000 });
578578
console.log('✅ Registration confirmed');
579579

580580
// Clean up stored secret
@@ -1194,7 +1194,7 @@ Your new ENS domain is now registered and ready to use:
11941194
// Wait for transaction confirmation
11951195
setSuccess('Transaction submitted! Waiting for confirmation...');
11961196

1197-
const receipt = await publicClient.waitForTransactionReceipt({ hash });
1197+
const receipt = await publicClient.waitForTransactionReceipt({ hash, pollingInterval: 6_000 });
11981198
console.log('Transaction confirmed:', receipt);
11991199

12001200
// Save the domain association to history

src/app/components/RelayQuotes.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
TRANSACTION_TIMEOUT_MS,
1212
} from './constants';
1313
import { performWithRetry, getGnosisPublicClient } from './utils';
14+
import { getPollingInterval } from '@/app/wagmi';
1415

1516
// Relay API Error Codes and Messages
1617
// Based on: https://docs.relay.link/references/api/handling-errors
@@ -634,6 +635,7 @@ export const executeRelaySteps = async (
634635
const receipt = await publicClient.waitForTransactionReceipt({
635636
hash: txHash,
636637
timeout: TRANSACTION_TIMEOUT_MS,
638+
pollingInterval: getPollingInterval(item.data.chainId),
637639
});
638640

639641
if (receipt.status === 'success') {

src/app/components/SwapComponent.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import React, { useState, useEffect, useRef, useCallback } from 'react';
44
import { useAccount, useChainId, usePublicClient, useWalletClient, useSwitchChain } from 'wagmi';
55
import { watchChainId, getWalletClient } from '@wagmi/core';
66
import { useConnectModal } from '@rainbow-me/rainbowkit';
7-
import { config } from '@/app/wagmi';
7+
import { config, getPollingInterval } from '@/app/wagmi';
88
import { createConfig, EVM, ChainId, ChainType, getChains, Chain } from '@lifi/sdk';
99
import styles from './css/SwapComponent.module.css';
1010
import { parseAbi, formatUnits } from 'viem';
@@ -602,6 +602,7 @@ const SwapComponent: React.FC = () => {
602602

603603
const approveReceipt = await publicClient.waitForTransactionReceipt({
604604
hash: approveTxHash,
605+
pollingInterval: getPollingInterval(chainId),
605606
});
606607

607608
if (approveReceipt.status !== 'success') {
@@ -887,6 +888,7 @@ const SwapComponent: React.FC = () => {
887888
// Wait for batch transaction to be mined
888889
const batchReceipt = await publicClient.waitForTransactionReceipt({
889890
hash: batchTxHash,
891+
pollingInterval: getPollingInterval(chainId),
890892
});
891893

892894
if (batchReceipt.status === 'success') {

src/app/wagmi.ts

Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { getDefaultConfig } from '@rainbow-me/rainbowkit';
2+
import { fallback, http } from 'wagmi';
23
import {
34
mainnet, // Ethereum (1)
45
gnosis, // Gnosis (100)
@@ -31,6 +32,197 @@ import {
3132
sepolia, // Sepolia testnet
3233
} from 'wagmi/chains';
3334

35+
// 3 RPCs per chain — each verified with eth_chainId; failed ones replaced
36+
const RPC_FALLBACKS: Record<number, [string, string, string]> = {
37+
[mainnet.id]: [
38+
'https://eth-mainnet.g.alchemy.com/v2/IVUx4_9ohzDYHxS7mp4gG4FAyOJrggTV',
39+
'https://eth.drpc.org',
40+
'https://ethereum.publicnode.com',
41+
],
42+
[gnosis.id]: [
43+
'https://gnosis-mainnet.g.alchemy.com/v2/IVUx4_9ohzDYHxS7mp4gG4FAyOJrggTV',
44+
'https://rpc.gnosischain.com',
45+
'https://gnosis.drpc.org',
46+
],
47+
[base.id]: [
48+
'https://base-mainnet.g.alchemy.com/v2/IVUx4_9ohzDYHxS7mp4gG4FAyOJrggTV',
49+
'https://base.drpc.org',
50+
'https://mainnet.base.org',
51+
],
52+
[arbitrum.id]: [
53+
'https://arb1.arbitrum.io/rpc',
54+
'https://1rpc.io/arb',
55+
'https://arbitrum-one-rpc.publicnode.com',
56+
],
57+
[optimism.id]: [
58+
'https://mainnet.optimism.io',
59+
'https://optimism.drpc.org',
60+
'https://optimism.meowrpc.com',
61+
],
62+
[avalanche.id]: [
63+
'https://api.avax.network/ext/bc/C/rpc',
64+
'https://avalanche.drpc.org',
65+
'https://avax.meowrpc.com',
66+
],
67+
[bsc.id]: [
68+
'https://bsc.drpc.org',
69+
'https://bsc-dataseed.bnbchain.org',
70+
'https://bsc-rpc.publicnode.com',
71+
],
72+
[celo.id]: ['https://forno.celo.org', 'https://celo.drpc.org', 'https://1rpc.io/celo'],
73+
[polygon.id]: [
74+
'https://polygon.meowrpc.com',
75+
'https://polygon.drpc.org',
76+
'https://polygon-bor-rpc.publicnode.com',
77+
],
78+
[mantle.id]: [
79+
'https://rpc.mantle.xyz',
80+
'https://mantle.drpc.org',
81+
'https://rpc.mantle.xyz',
82+
],
83+
[zksync.id]: [
84+
'https://mainnet.era.zksync.io',
85+
'https://zksync.drpc.org',
86+
'https://zksync.meowrpc.com',
87+
],
88+
[ink.id]: [
89+
'https://rpc-gel-sepolia.inkonchain.com',
90+
'https://rpc-gel-sepolia.inkonchain.com',
91+
'https://rpc-gel-sepolia.inkonchain.com',
92+
],
93+
[boba.id]: [
94+
'https://mainnet.boba.network',
95+
'https://boba-mainnet.g.alchemy.com/public',
96+
'https://mainnet.boba.network',
97+
],
98+
[cronos.id]: [
99+
'https://evm.cronos.org',
100+
'https://cronos.drpc.org',
101+
'https://cronos-evm.publicnode.com',
102+
],
103+
[gravity.id]: [
104+
'https://rpc.gravity.xyz',
105+
'https://rpc.gravity.xyz',
106+
'https://rpc.gravity.xyz',
107+
],
108+
[linea.id]: ['https://rpc.linea.build', 'https://1rpc.io/linea', 'https://linea.drpc.org'],
109+
[lisk.id]: [
110+
'https://rpc.api.lisk.com',
111+
'https://lisk-sepolia.drpc.org',
112+
'https://rpc.api.lisk.com',
113+
],
114+
[metis.id]: [
115+
'https://andromeda.metis.io/?owner=1088',
116+
'https://metis.drpc.org',
117+
'https://andromeda.metis.io/?owner=1088',
118+
],
119+
[mode.id]: [
120+
'https://mainnet.mode.network',
121+
'https://mode.drpc.org',
122+
'https://mainnet.mode.network',
123+
],
124+
[polygonZkEvm.id]: [
125+
'https://zkevm-rpc.com',
126+
'https://polygon-zkevm.drpc.org',
127+
'https://zkevm-rpc.com',
128+
],
129+
[scroll.id]: [
130+
'https://rpc.scroll.io',
131+
'https://scroll.drpc.org',
132+
'https://rpc.scroll.io',
133+
],
134+
[sei.id]: [
135+
'https://sei.drpc.org',
136+
'https://sei.publicnode.com',
137+
'https://sei.drpc.org',
138+
],
139+
[sonic.id]: [
140+
'https://rpc.soniclabs.com',
141+
'https://sonic.drpc.org',
142+
'https://rpc.soniclabs.com',
143+
],
144+
[soneium.id]: [
145+
'https://rpc.soneium.org',
146+
'https://soneium.drpc.org',
147+
'https://soneium-rpc.publicnode.com',
148+
],
149+
[taiko.id]: [
150+
'https://rpc.mainnet.taiko.xyz',
151+
'https://taiko.drpc.org',
152+
'https://taiko.publicnode.com',
153+
],
154+
[unichain.id]: [
155+
'https://unichain.drpc.org',
156+
'https://unichain.publicnode.com',
157+
'https://unichain.drpc.org',
158+
],
159+
[worldchain.id]: [
160+
'https://worldchain-mainnet.g.alchemy.com/public',
161+
'https://worldchain.drpc.org',
162+
'https://worldchain-mainnet.g.alchemy.com/public',
163+
],
164+
[sepolia.id]: [
165+
'https://sepolia.drpc.org',
166+
'https://ethereum-sepolia.publicnode.com',
167+
'https://1rpc.io/sepolia',
168+
],
169+
};
170+
171+
const HTTP_TRANSPORT_OPTIONS = {
172+
retryCount: 3,
173+
retryDelay: 1000,
174+
timeout: 30_000,
175+
};
176+
177+
// Polling intervals tuned to each chain's block time (ms).
178+
// Polling faster than block time wastes requests; polling slower delays UX.
179+
const CHAIN_POLLING_INTERVALS: Record<number, number> = {
180+
[mainnet.id]: 6_000, // ~12s blocks — poll twice per block
181+
[sepolia.id]: 6_000, // testnet, same as mainnet
182+
[gnosis.id]: 4_000, // ~5s blocks
183+
[base.id]: 2_000, // 2s blocks
184+
[arbitrum.id]: 1_000, // 0.25s blocks
185+
[optimism.id]: 2_000, // 2s blocks
186+
[avalanche.id]: 2_000, // ~2s blocks
187+
[bsc.id]: 3_000, // 3s blocks
188+
[celo.id]: 4_000, // 5s blocks
189+
[polygon.id]: 2_000, // ~2s blocks
190+
[mantle.id]: 2_000, // ~2s blocks (L2)
191+
[zksync.id]: 2_000, // ~1-2s blocks
192+
[ink.id]: 2_000, // L2
193+
[boba.id]: 4_000, // ~1 min L1 batches but L2 is faster
194+
[cronos.id]: 4_000, // ~5s blocks
195+
[gravity.id]: 2_000, // L2
196+
[linea.id]: 4_000, // ~3-4s blocks
197+
[lisk.id]: 2_000, // L2
198+
[metis.id]: 4_000, // ~4s blocks
199+
[mode.id]: 2_000, // L2 on Base stack
200+
[polygonZkEvm.id]: 4_000, // batched ~5-10s
201+
[scroll.id]: 3_000, // ~3s blocks
202+
[sei.id]: 1_000, // ~0.4s blocks
203+
[sonic.id]: 2_000, // L2
204+
[soneium.id]: 2_000, // L2
205+
[taiko.id]: 3_000, // ~3s blocks
206+
[unichain.id]: 2_000, // L2
207+
[worldchain.id]: 2_000, // L2
208+
};
209+
210+
const DEFAULT_POLLING_INTERVAL = 4_000;
211+
212+
/**
213+
* Returns the appropriate polling interval (ms) for a given chain ID,
214+
* tuned to block time so we don't spam RPCs on slow chains or lag on fast ones.
215+
*/
216+
export function getPollingInterval(chainId: number): number {
217+
return CHAIN_POLLING_INTERVALS[chainId] ?? DEFAULT_POLLING_INTERVAL;
218+
}
219+
220+
function transportForChain(chainId: number) {
221+
const urls = RPC_FALLBACKS[chainId];
222+
if (!urls) return http(undefined, HTTP_TRANSPORT_OPTIONS);
223+
return fallback(urls.map(url => http(url, HTTP_TRANSPORT_OPTIONS)));
224+
}
225+
34226
export const config = getDefaultConfig({
35227
appName: 'RainbowKit demo',
36228
projectId: 'YOUR_PROJECT_ID',
@@ -67,5 +259,9 @@ export const config = getDefaultConfig({
67259
// Testnets if enabled
68260
...(process.env.NEXT_PUBLIC_ENABLE_TESTNETS === 'true' ? [sepolia] : []),
69261
],
262+
transports: Object.fromEntries(
263+
Object.keys(RPC_FALLBACKS).map(chainId => [Number(chainId), transportForChain(Number(chainId))])
264+
),
265+
pollingInterval: 6_000,
70266
ssr: false,
71267
});

0 commit comments

Comments
 (0)