Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

App config: support multiple RPC URLs #2498

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 17 additions & 2 deletions configs/app/chain.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import type { RollupType } from 'types/client/rollup';
import type { NetworkVerificationType, NetworkVerificationTypeEnvs } from 'types/networks';

import { getEnvValue } from './utils';
import { urlValidator } from 'ui/shared/forms/validators/url';

import { getEnvValue, parseEnvJson } from './utils';

const DEFAULT_CURRENCY_DECIMALS = 18;

Expand All @@ -17,6 +19,19 @@ const verificationType: NetworkVerificationType = (() => {
return getEnvValue('NEXT_PUBLIC_NETWORK_VERIFICATION_TYPE') as NetworkVerificationTypeEnvs || 'mining';
})();

const rpcUrls = (() => {
const envValue = getEnvValue('NEXT_PUBLIC_NETWORK_RPC_URL');
const isUrl = urlValidator(envValue);

if (envValue && isUrl === true) {
return [ envValue ];
}

const parsedValue = parseEnvJson<Array<string>>(envValue);

return Array.isArray(parsedValue) ? parsedValue : [];
})();

const chain = Object.freeze({
id: getEnvValue('NEXT_PUBLIC_NETWORK_ID'),
name: getEnvValue('NEXT_PUBLIC_NETWORK_NAME'),
Expand All @@ -32,7 +47,7 @@ const chain = Object.freeze({
},
hasMultipleGasCurrencies: getEnvValue('NEXT_PUBLIC_NETWORK_MULTIPLE_GAS_CURRENCIES') === 'true',
tokenStandard: getEnvValue('NEXT_PUBLIC_NETWORK_TOKEN_STANDARD_NAME') || 'ERC',
rpcUrl: getEnvValue('NEXT_PUBLIC_NETWORK_RPC_URL'),
rpcUrls,
isTestnet: getEnvValue('NEXT_PUBLIC_IS_TESTNET') === 'true',
verificationType,
});
Expand Down
2 changes: 1 addition & 1 deletion configs/app/features/blockchainInteraction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const config: Feature<{ walletConnect: { projectId: string } }> = (() => {
chain.currency.name &&
chain.currency.symbol &&
chain.currency.decimals &&
chain.rpcUrl &&
chain.rpcUrls.length > 0 &&
walletConnectProjectId
) {
return Object.freeze({
Expand Down
2 changes: 1 addition & 1 deletion configs/app/features/marketplace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ const config: Feature<(
rating: { airtableApiKey: string; airtableBaseId: string } | undefined;
graphLinksUrl: string | undefined;
}> = (() => {
if (enabled === 'true' && chain.rpcUrl && submitFormUrl) {
if (enabled === 'true' && chain.rpcUrls.length > 0 && submitFormUrl) {
const props = {
submitFormUrl,
categoriesUrl,
Expand Down
16 changes: 15 additions & 1 deletion deploy/tools/envs-validator/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -587,7 +587,21 @@ const schema = yup
NEXT_PUBLIC_NETWORK_NAME: yup.string().required(),
NEXT_PUBLIC_NETWORK_SHORT_NAME: yup.string(),
NEXT_PUBLIC_NETWORK_ID: yup.number().positive().integer().required(),
NEXT_PUBLIC_NETWORK_RPC_URL: yup.string().test(urlTest),
NEXT_PUBLIC_NETWORK_RPC_URL: yup
.mixed()
.test(
'shape',
'Invalid schema were provided for NEXT_PUBLIC_NETWORK_RPC_URL, it should be either array of URLs or URL string',
(data) => {
const isUrlSchema = yup.string().test(urlTest);
const isArrayOfUrlsSchema = yup
.array()
.transform(replaceQuotes)
.json()
.of(yup.string().test(urlTest));

return isUrlSchema.isValidSync(data) || isArrayOfUrlsSchema.isValidSync(data);
}),
NEXT_PUBLIC_NETWORK_CURRENCY_NAME: yup.string(),
NEXT_PUBLIC_NETWORK_CURRENCY_WEI_NAME: yup.string(),
NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL: yup.string(),
Expand Down
3 changes: 2 additions & 1 deletion deploy/tools/envs-validator/test/.env.alt
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ NEXT_PUBLIC_HOMEPAGE_STATS=[]
NEXT_PUBLIC_VIEWS_ADDRESS_FORMAT=['base16','bech32']
NEXT_PUBLIC_VIEWS_ADDRESS_BECH_32_PREFIX=foo
NEXT_PUBLIC_RE_CAPTCHA_APP_SITE_KEY=xxx
NEXT_PUBLIC_RE_CAPTCHA_V3_APP_SITE_KEY=deprecated
NEXT_PUBLIC_RE_CAPTCHA_V3_APP_SITE_KEY=deprecated
NEXT_PUBLIC_NETWORK_RPC_URL=['https://example.com','https://example2.com']
2 changes: 1 addition & 1 deletion docs/ENVS.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ Please be aware that all environment variables prefixed with `NEXT_PUBLIC_` will
| NEXT_PUBLIC_NETWORK_NAME | `string` | Displayed name of the network | Required | - | `Gnosis Chain` | v1.0.x+ |
| NEXT_PUBLIC_NETWORK_SHORT_NAME | `string` | Used for SEO attributes (e.g, page description) | - | - | `OoG` | v1.0.x+ |
| NEXT_PUBLIC_NETWORK_ID | `number` | Chain id, see [https://chainlist.org](https://chainlist.org) for the reference | Required | - | `99` | v1.0.x+ |
| NEXT_PUBLIC_NETWORK_RPC_URL | `string` | Chain public RPC server url, see [https://chainlist.org](https://chainlist.org) for the reference | - | - | `https://core.poa.network` | v1.0.x+ |
| NEXT_PUBLIC_NETWORK_RPC_URL | `string \| Array<string>` | Chain public RPC server url, see [https://chainlist.org](https://chainlist.org) for the reference. Can contain a single string value, or an array of urls. | - | - | `https://core.poa.network` | v1.0.x+ |
| NEXT_PUBLIC_NETWORK_CURRENCY_NAME | `string` | Network currency name | - | - | `Ether` | v1.0.x+ |
| NEXT_PUBLIC_NETWORK_CURRENCY_WEI_NAME | `string` | Name of network currency subdenomination | - | `wei` | `duck` | v1.23.0+ |
| NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL | `string` | Network currency symbol | - | - | `ETH` | v1.0.x+ |
Expand Down
2 changes: 1 addition & 1 deletion lib/web3/currentChain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const currentChain = {
},
rpcUrls: {
'default': {
http: [ config.chain.rpcUrl ?? '' ],
http: config.chain.rpcUrls,
},
},
blockExplorers: {
Expand Down
2 changes: 1 addition & 1 deletion lib/web3/useAddOrSwitchChain.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export default function useAddOrSwitchChain() {
symbol: config.chain.currency.symbol,
decimals: config.chain.currency.decimals,
},
rpcUrls: [ config.chain.rpcUrl ],
rpcUrls: config.chain.rpcUrls,
blockExplorerUrls: [ config.app.baseUrl ],
} ] as never;
// in wagmi types for wallet_addEthereumChain method is not provided
Expand Down
10 changes: 7 additions & 3 deletions lib/web3/wagmiConfig.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { WagmiAdapter } from '@reown/appkit-adapter-wagmi';
import { http } from 'viem';
import { fallback, http } from 'viem';
import { createConfig } from 'wagmi';

import config from 'configs/app';
Expand All @@ -13,7 +13,11 @@ const wagmi = (() => {
const wagmiConfig = createConfig({
chains: [ currentChain ],
transports: {
[currentChain.id]: http(config.chain.rpcUrl || `${ config.api.endpoint }/api/eth-rpc`),
[currentChain.id]: fallback(
config.chain.rpcUrls
.map((url) => http(url))
.concat(http(`${ config.api.endpoint }/api/eth-rpc`)),
),
},
ssr: true,
batch: { multicall: { wait: 100 } },
Expand All @@ -26,7 +30,7 @@ const wagmi = (() => {
networks: chains,
multiInjectedProviderDiscovery: true,
transports: {
[currentChain.id]: http(),
[currentChain.id]: fallback(config.chain.rpcUrls.map((url) => http(url))),
},
projectId: feature.walletConnect.projectId,
ssr: true,
Expand Down
2 changes: 1 addition & 1 deletion nextjs/csp/policies/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export function app(): CspDev.DirectiveDescriptor {
getFeaturePayload(config.features.rewards)?.api.endpoint,

// chain RPC server
config.chain.rpcUrl,
...config.chain.rpcUrls,
'https://infragrid.v.network', // RPC providers

// github (spec for api-docs page)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const ContractVerificationSolidityFoundry = () => {
const address = watch('address');

const codeSnippet = `forge verify-contract \\
--rpc-url ${ config.chain.rpcUrl || `${ config.api.endpoint }/api/eth-rpc` } \\
--rpc-url ${ config.chain.rpcUrls[0] || `${ config.api.endpoint }/api/eth-rpc` } \\
--verifier blockscout \\
--verifier-url '${ config.api.endpoint }/api/' \\
${ address || '<address>' } \\
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const ContractVerificationSolidityHardhat = ({ config: formConfig }: { config: S
solidity: "${ latestSolidityVersion || '0.8.24' }", // replace if necessary
networks: {
'${ chainNameSlug }': {
url: '${ config.chain.rpcUrl || `${ config.api.endpoint }/api/eth-rpc` }'
url: '${ config.chain.rpcUrls[0] || `${ config.api.endpoint }/api/eth-rpc` }'
},
},
etherscan: {
Expand Down
2 changes: 1 addition & 1 deletion ui/pages/Address.pw.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ test('degradation view', async({ render, page, mockRpcResponse, mockApiResponse
});

const component = await render(<Address/>, { hooksConfig });
await page.waitForResponse(config.chain.rpcUrl as string);
await page.waitForResponse(config.chain.rpcUrls[0]);

await expect(component).toHaveScreenshot({
mask: [ page.locator(pwConfig.adsBannerSelector) ],
Expand Down
6 changes: 3 additions & 3 deletions ui/pages/Block.pw.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ test('degradation view, details tab', async({ render, mockApiResponse, mockRpcRe
});

const component = await render(<Block/>, { hooksConfig });
await page.waitForResponse(config.chain.rpcUrl as string);
await page.waitForResponse(config.chain.rpcUrls[0]);

await expect(component).toHaveScreenshot();
});
Expand All @@ -49,7 +49,7 @@ test('degradation view, txs tab', async({ render, mockApiResponse, mockRpcRespon
});

const component = await render(<Block/>, { hooksConfig });
await page.waitForResponse(config.chain.rpcUrl as string);
await page.waitForResponse(config.chain.rpcUrls[0]);

await expect(component).toHaveScreenshot();
});
Expand All @@ -71,7 +71,7 @@ test('degradation view, withdrawals tab', async({ render, mockApiResponse, mockR
});

const component = await render(<Block/>, { hooksConfig });
await page.waitForResponse(config.chain.rpcUrl as string);
await page.waitForResponse(config.chain.rpcUrls[0]);

await expect(component).toHaveScreenshot();
});
4 changes: 2 additions & 2 deletions ui/pages/MarketplaceApp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ const MarketplaceAppContent = ({ address, data, isPending, appUrl }: Props) => {
blockscoutNetworkName: config.chain.name,
blockscoutNetworkId: Number(config.chain.id),
blockscoutNetworkCurrency: config.chain.currency,
blockscoutNetworkRpc: config.chain.rpcUrl,
blockscoutNetworkRpc: config.chain.rpcUrls[0],
};

iframeRef?.current?.contentWindow?.postMessage(message, data.url);
Expand Down Expand Up @@ -159,7 +159,7 @@ const MarketplaceApp = () => {
<DappscoutIframeProvider
address={ address }
appUrl={ appUrl }
rpcUrl={ config.chain.rpcUrl }
rpcUrl={ config.chain.rpcUrls[0] }
sendTransaction={ sendTransaction }
signMessage={ signMessage }
signTypedData={ signTypedData }
Expand Down
2 changes: 1 addition & 1 deletion ui/shared/NetworkAddToWallet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ const NetworkAddToWallet = () => {
}
}, [ addOrSwitchChain, provider, toast, wallet ]);

if (!provider || !wallet || !config.chain.rpcUrl || !feature.isEnabled) {
if (!provider || !wallet || !config.chain.rpcUrls.length || !feature.isEnabled) {
return null;
}

Expand Down
Loading