Skip to content

Commit

Permalink
refactor: use one rpc config env var
Browse files Browse the repository at this point in the history
  • Loading branch information
jtourkos committed Oct 14, 2024
1 parent 46b959f commit 67c931d
Show file tree
Hide file tree
Showing 8 changed files with 63 additions and 116 deletions.
47 changes: 19 additions & 28 deletions .env.template
Original file line number Diff line number Diff line change
@@ -1,33 +1,23 @@
PORT=string # Optional. The API server port. Defaults to 8080.

NETWORK=string # Required. The network to connect to. See `SupportedChains` in `types.ts` for supported networks.

# You should provide at least one of the following RPC URLs:

RPC_URL_MAINNET=string # The RPC URL for the Mainnet provider.
RPC_ACCESS_TOKEN_MAINNET=string # Optional. The access token to use for the RPC. If specified, it will be used as a bearer token in the `Authorization` header.
FALLBACK_RPC_URL_MAINNET=string # Optional. The fallback RPC URL for the Mainnet provider. If specified, it will be used if the main RPC URL fails.
FALLBACK_RPC_ACCESS_TOKEN_MAINNET=string # Optional. The access token to use for the fallback RPC. If specified, it will be used as a bearer token in the `Authorization` header.

RPC_URL_SEPOLIA=string # The RPC URL for the Sepolia provider.
RPC_ACCESS_TOKEN_SEPOLIA=string # Optional. The access token to use for the RPC. If specified, it will be used as a bearer token in the `Authorization` header.
FALLBACK_RPC_URL_SEPOLIA=string # Optional. The fallback RPC URL for the Sepolia provider. If specified, it will be used if the main RPC URL fails.
FALLBACK_RPC_ACCESS_TOKEN_SEPOLIA=string # Optional. The access token to use for the fallback RPC. If specified, it will be used as a bearer token in the `Authorization` header.

RPC_URL_OPTIMISM_SEPOLIA=string # The RPC URL for the Optimism Sepolia provider.
RPC_ACCESS_TOKEN_OPTIMISM_SEPOLIA=string # Optional. The access token to use for the RPC. If specified, it will be used as a bearer token in the `Authorization` header.
FALLBACK_RPC_URL_OPTIMISM_SEPOLIA=string # Optional. The fallback RPC URL for the Optimism Sepolia provider. If specified, it will be used if the main RPC URL fails.
FALLBACK_RPC_ACCESS_TOKEN_OPTIMISM_SEPOLIA=string # Optional. The access token to use for the fallback RPC. If specified, it will be used as a bearer token in the `Authorization` header.

RPC_URL_POLYGON_AMOY=string # The RPC URL for the Polygon Amoy provider.
RPC_ACCESS_TOKEN_POLYGON_AMOY=string # Optional. The access token to use for the RPC. If specified, it will be used as a bearer token in the `Authorization` header.
FALLBACK_RPC_URL_POLYGON_AMOY=string # Optional. The fallback RPC URL for the Polygon Amoy provider. If specified, it will be used if the main RPC URL fails.
FALLBACK_RPC_ACCESS_TOKEN_POLYGON_AMOY=string # Optional. The access token to use for the fallback RPC. If specified, it will be used as a bearer token in the `Authorization` header.

RPC_URL_FILECOIN=string # The RPC URL for the Filecoin provider.
RPC_ACCESS_TOKEN_FILECOIN=string # Optional. The access token to use for the RPC. If specified, it will be used as a bearer token in the `Authorization` header.
FALLBACK_RPC_URL_FILECOIN=string # Optional. The fallback RPC URL for the Filecoin provider. If specified, it will be used if the main RPC URL fails.
FALLBACK_RPC_ACCESS_TOKEN_FILECOIN=string # Optional. The access token to use for the fallback RPC. If specified, it will be used as a bearer token in the `Authorization` header.
NETWORK=string # Required. The network to connect to. See `SupportedChain` in `graphql.ts` for supported networks.

# Required. The RPC configuration. See `SupportedChain` in `graphql.ts` for supported networks.
RPC_CONFIG='{
"MAINNET": {
"url": "string", # The RPC URL.
"accessToken": "string", # Optional. The access token for the RPC URL.
"fallbackUrl": "string", # Optional. The fallback RPC URL.
"fallbackAccessToken": "string" # Optional. The access token for the fallback RPC URL.
},
"SEPOLIA": {
"url": "string", # The RPC URL.
"accessToken": "string", # Optional. The access token for the RPC URL.
"fallbackUrl": "string", # Optional. The fallback RPC URL.
"fallbackAccessToken": "string" # Optional. The access token for the fallback RPC URL.
},
... # Add more networks as needed.
}'

PUBLIC_API_KEYS=string # Required. Comma-separated list of strings API keys that rate limit is applied to.
DRIPS_API_KEY=string # Required. API key withouth rate limit.
Expand All @@ -44,3 +34,4 @@ MAX_QUERY_DEPTH=number # Optional. defaults to 4.
TIMEOUT_IN_SECONDS=number # Optional. defaults to 20.

IPFS_GATEWAY_URL=string # Optional. The IPFS gateway URL to use for fetching IPFS data. Defaults to 'https://drips.mypinata.cloud'.

2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,6 @@
"graphql-tag": "^2.12.6",
"pg": "^8.11.3",
"sequelize": "^6.32.1",
"zod": "^3.23.4"
"zod": "^3.23.8"
}
}
29 changes: 14 additions & 15 deletions src/common/FailoverProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,19 @@ import type {
} from 'ethers';
import { JsonRpcProvider, FetchRequest } from 'ethers';

class AggregatedRpcError extends Error {
public readonly errors: { rpcEndpoint: string; error: any }[];

constructor(errors: { rpcEndpoint: string; error: any }[]) {
super(
`All RPC endpoints failed:\n${errors.map((e) => `Endpoint '${e.rpcEndpoint}' failed with error: ${e.error.message}.`).join('\n')}`,
);

this.name = 'AggregatedRpcError';
this.errors = errors;
}
}

/**
* A `JsonRpcProvider` that transparently fails over to a list of backup JSON-RPC endpoints.
*/
Expand Down Expand Up @@ -68,21 +81,7 @@ export default class FailoverJsonRpcProvider extends JsonRpcProvider {
}
}

// All endpoints failed. Throw an error containing the details.
const errorMessages = errors
.map(
(e) =>
`Endpoint '${e.rpcEndpoint}' failed with error: ${e.error.message}.`,
)
.join('\n');

const aggregatedError = new Error(
`All RPC endpoints failed:\n${errorMessages}`,
) as Error & { errors: { rpcEndpoint: string; error: any }[] };

aggregatedError.errors = errors;

throw aggregatedError;
throw new AggregatedRpcError(errors);
}

/**
Expand Down
62 changes: 22 additions & 40 deletions src/common/appSettings.ts
Original file line number Diff line number Diff line change
@@ -1,50 +1,32 @@
import dotenv from 'dotenv';
import type { SupportedChain } from '../generated/graphql';
import { z } from 'zod';
import { SupportedChain } from '../generated/graphql';

dotenv.config();

type RpcConfig = {
url: string;
accessToken?: string;
fallbackUrl?: string;
fallbackAccessToken?: string;
};
function missingEnvVar(name: string): never {
throw new Error(`Missing ${name} in .env file.`);
}

const RpcConfigSchema = z.record(
z.nativeEnum(SupportedChain),
z
.object({
url: z.string().url(), // The RPC URL for the provider.
accessToken: z.string().optional(), // Optional. The access token for the RPC URL.
fallbackUrl: z.string().optional(), // Optional. The fallback RPC URL.
fallbackAccessToken: z.string().optional(), // Optional. The access token for the fallback RPC URL.
})
.optional(),
);

console.log('process.env.RPC_CONFIG', process.env.RPC_CONFIG);

export default {
port: (process.env.PORT || 8080) as number,
rpcConfigs: {
MAINNET: {
url: process.env.RPC_URL_MAINNET,
accessToken: process.env.RPC_ACCESS_TOKEN_MAINNET,
fallbackUrl: process.env.FALLBACK_RPC_URL_MAINNET,
fallbackAccessToken: process.env.FALLBACK_RPC_ACCESS_TOKEN_MAINNET,
},
SEPOLIA: {
url: process.env.RPC_URL_SEPOLIA,
accessToken: process.env.RPC_ACCESS_TOKEN_SEPOLIA,
fallbackUrl: process.env.FALLBACK_RPC_URL_SEPOLIA,
fallbackAccessToken: process.env.FALLBACK_RPC_ACCESS_TOKEN_SEPOLIA,
},
OPTIMISM_SEPOLIA: {
url: process.env.RPC_URL_OPTIMISM_SEPOLIA,
accessToken: process.env.RPC_ACCESS_TOKEN_OPTIMISM_SEPOLIA,
fallbackUrl: process.env.FALLBACK_RPC_URL_OPTIMISM_SEPOLIA,
fallbackAccessToken:
process.env.FALLBACK_RPC_ACCESS_TOKEN_OPTIMISM_SEPOLIA,
},
POLYGON_AMOY: {
url: process.env.RPC_URL_POLYGON_AMOY,
fallbackUrl: process.env.FALLBACK_RPC_URL_POLYGON_AMOY,
accessToken: process.env.RPC_ACCESS_TOKEN_POLYGON_AMOY,
fallbackAccessToken: process.env.FALLBACK_RPC_ACCESS_TOKEN_POLYGON_AMOY,
},
FILECOIN: {
url: process.env.RPC_URL_FILECOIN,
fallbackUrl: process.env.FALLBACK_RPC_URL_FILECOIN,
accessToken: process.env.RPC_ACCESS_TOKEN_FILECOIN,
fallbackAccessToken: process.env.FALLBACK_RPC_ACCESS_TOKEN_FILECOIN,
},
} as Record<SupportedChain, RpcConfig | undefined>,
rpcConfig: process.env.RPC_CONFIG
? RpcConfigSchema.parse(JSON.parse(process.env.RPC_CONFIG))
: missingEnvVar('RPC_CONFIG'),
publicApiKeys: process.env.PUBLIC_API_KEYS?.split(',') || [],
dripsApiKey: process.env.DRIPS_API_KEY,
postgresConnectionString: process.env.POSTGRES_CONNECTION_STRING,
Expand Down
8 changes: 4 additions & 4 deletions src/common/dripsContracts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ const chainConfigs: Record<
},
};

const { rpcConfigs } = appSettings;
const { rpcConfig } = appSettings;

const providers: {
[network in SupportedChain]?: FailoverJsonRpcProvider;
Expand All @@ -73,13 +73,13 @@ function createAuthFetchRequest(rpcUrl: string, token: string): FetchRequest {
}

Object.values(SupportedChain).forEach((network) => {
const rpcConfig = rpcConfigs[network];
const config = rpcConfig[network];

if (!rpcConfig) {
if (!config) {
return;
}

const { url, accessToken, fallbackUrl, fallbackAccessToken } = rpcConfig;
const { url, accessToken, fallbackUrl, fallbackAccessToken } = config;

const primaryEndpoint = accessToken
? createAuthFetchRequest(url, accessToken)
Expand Down
2 changes: 1 addition & 1 deletion src/common/queryableChains.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import appSettings from './appSettings';
const queryableChains: SupportedChain[] = [];

Object.keys(SupportedChain).forEach((chain) => {
if (appSettings.rpcConfigs[chain as SupportedChain]?.url) {
if (appSettings.rpcConfig[chain as SupportedChain]?.url) {
queryableChains.push(chain as SupportedChain);
}
});
Expand Down
27 changes: 1 addition & 26 deletions src/environment.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,7 @@ declare global {
namespace NodeJS {
interface ProcessEnv {
PORT: string;

RPC_URL_MAINNET: string | undefined;
RPC_ACCESS_TOKEN_MAINNET: string | undefined;
FALLBACK_RPC_URL_MAINNET: string | undefined;
FALLBACK_RPC_ACCESS_TOKEN_MAINNET: string | undefined;

RPC_URL_SEPOLIA: string | undefined;
FALLBACK_RPC_URL_SEPOLIA: string | undefined;
RPC_ACCESS_TOKEN_SEPOLIA: string | undefined;
FALLBACK_RPC_ACCESS_TOKEN_SEPOLIA: string | undefined;

RPC_URL_OPTIMISM_SEPOLIA: string | undefined;
FALLBACK_RPC_URL_OPTIMISM_SEPOLIA: string | undefined;
RPC_ACCESS_TOKEN_OPTIMISM_SEPOLIA: string | undefined;
FALLBACK_RPC_ACCESS_TOKEN_OPTIMISM_SEPOLIA: string | undefined;

RPC_URL_POLYGON_AMOY: string | undefined;
FALLBACK_RPC_URL_POLYGON_AMOY: string | undefined;
RPC_ACCESS_TOKEN_POLYGON_AMOY: string | undefined;
FALLBACK_RPC_ACCESS_TOKEN_POLYGON_AMOY: string | undefined;

RPC_URL_FILECOIN: string | undefined;
FALLBACK_RPC_URL_FILECOIN: string;
RPC_ACCESS_TOKEN_FILECOIN: string | undefined;
FALLBACK_RPC_ACCESS_TOKEN_FILECOIN: string | undefined;

RPC_CONFIG: string;
PUBLIC_API_KEYS: string;
DRIPS_API_KEY: string;
NODE_ENV: 'development' | 'production';
Expand Down

0 comments on commit 67c931d

Please sign in to comment.