Skip to content

Commit

Permalink
Pull rpc info from env
Browse files Browse the repository at this point in the history
Instead of forming provider objects on sdk load, create them based on need. Update/swap provider object if linked RPC in the environment is updated
  • Loading branch information
g1nt0ki committed Jul 10, 2023
1 parent 367cb24 commit ead46d3
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 25 deletions.
1 change: 1 addition & 0 deletions src/ChainApi.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ test("ChainApi - getChainId", async () => {
expect((new ChainApi({ chain: 'arbitrum' })).getChainId()).toEqual(42161);
expect((new ChainApi({ chain: 'optimism' })).getChainId()).toEqual(10);
expect((new ChainApi({ chain: 'solana' })).getChainId()).toEqual(undefined);
expect((new ChainApi({ chain: 'solana' })).provider).toEqual(null);
})

test("ChainApi - call", async () => {
Expand Down
44 changes: 44 additions & 0 deletions src/general.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { getBalance } from "./eth/index";
import { getProvider, setProvider } from "./general";
import { ethers, } from "ethers"

const dummyRPC = 'https://eth.llamarpc.com'

jest.setTimeout(10000);
test("RPC nodes from multiple chains support archive queries", async () => {
Expand All @@ -16,3 +20,43 @@ test("RPC nodes from multiple chains support archive queries", async () => {
}
}
});

test("getProvider default behavior", async () => {
const ethProvider = getProvider("ethereum")
const ethProvider2 = getProvider("ethereum")

expect(ethProvider).toEqual(ethProvider2);
});

test("getProvider - use rpc from env", async () => {
const ethProvider = getProvider("ethereum")
process.env.ETHEREUM_RPC = dummyRPC
const ethProvider2 = getProvider("ethereum")
const ethProvider3 = getProvider("ethereum")

expect(ethProvider).not.toBe(ethProvider2)
expect(ethProvider2).toBe(ethProvider3)
expect((ethProvider3 as any).providerConfigs[0].provider.connection.url).toBe(dummyRPC)
});

test("getProvider - invalid chain", async () => {
const llamaP = getProvider("llama-chain")
expect(llamaP).toBeNull()
});

test("getProvider - chain throws error", async () => {
process.env.SOLANA_RPC = 'https://api.mainnet-beta.solana.com'
const solP = getProvider("solana")
expect(solP).toBeNull()
});

test("getProvider - custom chain", async () => {
const clvRPC = "https://api-para.clover.finance"
const clvObject = new ethers.providers.StaticJsonRpcProvider(clvRPC, { name: "clv-llama-test", chainId: 1024, })
setProvider("clv-llama-test",clvObject)
const clvP = getProvider("clv-llama-test")
const clvPMissing = getProvider("clv-llama-test-not")
expect(clvP).not.toBeNull()
expect(clvPMissing).toBeNull()
expect((clvP as any).connection.url).toBe(clvRPC)
});
80 changes: 55 additions & 25 deletions src/general.ts
Original file line number Diff line number Diff line change
@@ -1,48 +1,75 @@
import { debugLog} from './util/debugLog'
import { ethers, BigNumber } from "ethers"
import providerList from './providers.json'

function createProvider(name: string, rpcString: string, chainId: number) {
const rpcList = rpcString.split(',')

function createProvider(name: string, defaultRpc: string, chainId: number) {
if (process.env.HISTORICAL) {
if (chainId === 1) {
console.log("RPC providers set to historical, only the first RPC provider will be used")
}
return new ethers.providers.StaticJsonRpcProvider(
(process.env[name.toUpperCase() + "_RPC"] ?? defaultRpc)?.split(',')[0],
rpcList[0],
{
name,
chainId,
}
)
} else {
return new ethers.providers.FallbackProvider(
(process.env[name.toUpperCase() + "_RPC"] ?? defaultRpc).split(',').map((url, i) => ({
provider: new ethers.providers.StaticJsonRpcProvider(
url,
{
name,
chainId,
}
),
priority: i
})),
1
)
try {
return new ethers.providers.FallbackProvider(
rpcList.map((url, i) => ({
provider: new ethers.providers.StaticJsonRpcProvider(
url,
{
name,
chainId,
}
),
priority: i
})),
1
)
} catch (e) {
debugLog(`Error creating provider for ${name} with RPCs: ${rpcList.join(', ')}`)
// we dont throw errors for chains not present in providers.json, these can be non-evm chains like solana
if ((providerList as any)[name])
throw e
return null
}
}
}

type Provider = ethers.providers.StaticJsonRpcProvider | ethers.providers.FallbackProvider

type ProviderWrapped = {
rpcList: string;
_provider: Provider;
}

export const providers = {} as {
[chain: string]: ethers.providers.BaseProvider;
[chain: string]: ProviderWrapped;
};

Object.entries(providerList).forEach(([name, value]) => {
const { rpc, chainId } = value as any
providers[name] = createProvider(name, rpc.join(','), chainId)
})

export type Chain = string
export function getProvider(chain: Chain = "ethereum"): ethers.providers.BaseProvider {
return providers[chain];
export function getProvider(chain: Chain = "ethereum"): Provider {
// use RPC from env variable if set else use RPC from providers.json
let rpcList: (string | undefined) = process.env[chain.toUpperCase() + "_RPC"]
if (!rpcList) rpcList = (providerList as any)[chain]?.rpc.join(',')
if (!rpcList) {
// in case provider was set using `setProvider` function
if (providers[chain]) return providers[chain]._provider
// @ts-ignore (throwing error here would alter function behavior and have side effects)
return null
}
if (!providers[chain] || providers[chain].rpcList !== rpcList) {
providers[chain] = {
rpcList,
_provider: (createProvider(chain, rpcList, (providerList as any)[chain]?.chainId) as Provider)
}
}
return providers[chain]._provider
}

export const TEN = BigNumber.from(10);
Expand All @@ -59,7 +86,10 @@ export const ETHER_ADDRESS = "0x0000000000000000000000000000000000000000";

export function setProvider(
chain: Chain,
provider: ethers.providers.BaseProvider
provider: Provider
) {
providers[chain] = provider;
providers[chain] = {
rpcList: "",
_provider: provider
}
}

0 comments on commit ead46d3

Please sign in to comment.