Skip to content

Commit

Permalink
Fix provider recursion bug, provider function types (#5829)
Browse files Browse the repository at this point in the history
  • Loading branch information
christianbaroni authored Jun 11, 2024
1 parent f2c7bfb commit 7cdcc7a
Show file tree
Hide file tree
Showing 24 changed files with 104 additions and 107 deletions.
2 changes: 1 addition & 1 deletion src/__swaps__/screens/Swap/providers/swap-provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ export const SwapProvider = ({ children }: SwapProviderProps) => {
? await getFlashbotsProvider()
: getCachedProviderForNetwork(network);
const providerUrl = provider?.connection?.url;
const connectedToHardhat = isHardHat(providerUrl);
const connectedToHardhat = !!providerUrl && isHardHat(providerUrl);

const selectedGas = getSelectedGas(parameters.chainId);
if (!selectedGas) {
Expand Down
24 changes: 10 additions & 14 deletions src/__swaps__/screens/Swap/resources/assets/userAssets.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
import { useQuery } from '@tanstack/react-query';
import { Address } from 'viem';
import { ADDYS_API_KEY } from 'react-native-dotenv';

import { QueryConfigWithSelect, QueryFunctionArgs, QueryFunctionResult, createQueryKey, queryClient } from '@/react-query';

import { getIsHardhatConnected } from '@/handlers/web3';
import { RainbowError, logger } from '@/logger';
import { RainbowFetchClient } from '@/rainbow-fetch';
import { SupportedCurrencyKey, SUPPORTED_CHAIN_IDS } from '@/references';
import { ParsedAssetsDictByChain, ZerionAsset } from '@/__swaps__/types/assets';
import { ChainId } from '@/__swaps__/types/chains';
import { AddressAssetsReceivedMessage } from '@/__swaps__/types/refraction';
import { filterAsset, parseUserAsset } from '@/__swaps__/utils/assets';
import { greaterThan } from '@/__swaps__/utils/numbers';
import { RainbowError, logger } from '@/logger';

import { fetchUserAssetsByChain } from './userAssetsByChain';
import { RainbowFetchClient } from '@/rainbow-fetch';
import { useAccountSettings } from '@/hooks';
import { getCachedProviderForNetwork, isHardHat } from '@/handlers/web3';

const addysHttp = new RainbowFetchClient({
baseURL: 'https://addys.p.rainbow.me/v3',
Expand All @@ -31,27 +30,27 @@ export const USER_ASSETS_STALE_INTERVAL = 30000;
// Query Types

export type UserAssetsArgs = {
address?: Address;
address: Address;
currency: SupportedCurrencyKey;
testnetMode?: boolean;
};

type SetUserAssetsArgs = {
address?: Address;
address: Address;
currency: SupportedCurrencyKey;
userAssets?: UserAssetsResult;
testnetMode?: boolean;
};

type SetUserDefaultsArgs = {
address?: Address;
address: Address;
currency: SupportedCurrencyKey;
staleTime: number;
testnetMode?: boolean;
};

type FetchUserAssetsArgs = {
address?: Address;
address: Address;
currency: SupportedCurrencyKey;
testnetMode?: boolean;
};
Expand Down Expand Up @@ -211,12 +210,9 @@ export function useUserAssets<TSelectResult = UserAssetsResult>(
{ address, currency }: UserAssetsArgs,
config: QueryConfigWithSelect<UserAssetsResult, Error, TSelectResult, UserAssetsQueryKey> = {}
) {
const { network: currentNetwork } = useAccountSettings();
const provider = getCachedProviderForNetwork(currentNetwork);
const providerUrl = provider?.connection?.url;
const connectedToHardhat = isHardHat(providerUrl);
const isHardhatConnected = getIsHardhatConnected();

return useQuery(userAssetsQueryKey({ address, currency, testnetMode: connectedToHardhat }), userAssetsQueryFunction, {
return useQuery(userAssetsQueryKey({ address, currency, testnetMode: isHardhatConnected }), userAssetsQueryFunction, {
...config,
refetchInterval: USER_ASSETS_REFETCH_INTERVAL,
staleTime: process.env.IS_TESTING === 'true' ? 0 : 1000,
Expand Down
73 changes: 40 additions & 33 deletions src/handlers/web3.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,15 +37,14 @@ import { ethereumUtils } from '@/utils';
import { logger, RainbowError } from '@/logger';
import { IS_IOS, RPC_PROXY_API_KEY, RPC_PROXY_BASE_URL } from '@/env';
import { getNetworkObj } from '@/networks';
import store from '@/redux/store';

export enum TokenStandard {
ERC1155 = 'ERC1155',
ERC721 = 'ERC721',
}

export const networkProviders: {
[network in Network]?: StaticJsonRpcProvider;
} = {};
export const networkProviders = new Map<Network, StaticJsonRpcProvider>();

/**
* Creates an rpc endpoint for a given chain id using the Rainbow rpc proxy.
Expand Down Expand Up @@ -207,8 +206,8 @@ export const getFlashbotsProvider = async () => {
);
};

export const getCachedProviderForNetwork = (network: Network = Network.mainnet) => {
return networkProviders[network]!;
export const getCachedProviderForNetwork = (network: Network = Network.mainnet): StaticJsonRpcProvider | undefined => {
return networkProviders.get(network);
};

/**
Expand All @@ -217,27 +216,37 @@ export const getCachedProviderForNetwork = (network: Network = Network.mainnet)
* @return The provider for the network.
*/
export const getProviderForNetwork = async (network: Network | string = Network.mainnet): Promise<StaticJsonRpcProvider> => {
if (isNetworkEnum(network) && networkProviders[network]) {
return networkProviders[network]!;
const isSupportedNetwork = isNetworkEnum(network);
const cachedProvider = isSupportedNetwork ? networkProviders.get(network) : undefined;

if (isSupportedNetwork && cachedProvider) {
return cachedProvider;
}

if (!isNetworkEnum(network)) {
if (!isSupportedNetwork) {
const provider = new StaticJsonRpcProvider(network, Network.mainnet);
networkProviders[Network.mainnet] = provider;
networkProviders.set(Network.mainnet, provider);
return provider;
} else {
const chainId = getNetworkObj(network).id;
const provider = new StaticJsonRpcProvider(getNetworkObj(network).rpc, chainId);
if (!networkProviders[network]) {
networkProviders[network] = provider;
}
await provider.ready;
const provider = new StaticJsonRpcProvider(getNetworkObj(network).rpc(), getNetworkObj(network).id);
networkProviders.set(network, provider);
return provider;
}
};

/**
* @desc Sends an arbitrary RCP call using a given provider, or the default
* @desc Checks if the active network is Hardhat.
* @returns boolean: `true` if connected to Hardhat.
*/
export const getIsHardhatConnected = (): boolean => {
const currentNetwork = store.getState().settings.network;
const currentProviderUrl = getCachedProviderForNetwork(currentNetwork)?.connection?.url;
const connectedToHardhat = !!currentProviderUrl && isHardHat(currentProviderUrl);
return connectedToHardhat;
};

/**
* @desc Sends an arbitrary RPC call using a given provider, or the default
* cached provider.
* @param payload The payload, including a method and parameters, based on
* the Ethers.js `StaticJsonRpcProvider.send` arguments.
Expand All @@ -248,10 +257,10 @@ export const getProviderForNetwork = async (network: Network | string = Network.
export const sendRpcCall = async (
payload: {
method: string;
params: any[];
params: unknown[];
},
provider: StaticJsonRpcProvider | null = null
): Promise<any> => (provider || web3Provider)?.send(payload.method, payload.params);
): Promise<unknown> => (provider || web3Provider)?.send(payload.method, payload.params);

/**
* @desc check if hex string
Expand Down Expand Up @@ -359,9 +368,9 @@ export const estimateGas = async (
export async function estimateGasWithPadding(
txPayload: TransactionRequest,
contractCallEstimateGas: Contract['estimateGas'][string] | null = null,
callArguments: any[] | null = null,
callArguments: unknown[] | null = null,
provider: StaticJsonRpcProvider | null = null,
paddingFactor: number = 1.1
paddingFactor = 1.1
): Promise<string | null> {
try {
const p = provider || web3Provider;
Expand All @@ -383,16 +392,16 @@ export async function estimateGasWithPadding(
const code = to ? await p.getCode(to) : undefined;
// 2 - if it's not a contract AND it doesn't have any data use the default gas limit
if ((!contractCallEstimateGas && !to) || (to && !data && (!code || code === '0x'))) {
logger.info('⛽ Skipping estimates, using default', {
logger.debug('⛽ Skipping estimates, using default', {
ethUnits: ethUnits.basic_tx.toString(),
});
return ethUnits.basic_tx.toString();
}

logger.info('⛽ Calculating safer gas limit for last block');
logger.debug('⛽ Calculating safer gas limit for last block');
// 3 - If it is a contract, call the RPC method `estimateGas` with a safe value
const saferGasLimit = fraction(gasLimit.toString(), 19, 20);
logger.info('⛽ safer gas limit for last block is', { saferGasLimit });
logger.debug('⛽ safer gas limit for last block is', { saferGasLimit });

txPayloadToEstimate[contractCallEstimateGas ? 'gasLimit' : 'gas'] = toHex(saferGasLimit);

Expand All @@ -404,7 +413,7 @@ export async function estimateGasWithPadding(

const lastBlockGasLimit = addBuffer(gasLimit.toString(), 0.9);
const paddedGas = addBuffer(estimatedGas.toString(), paddingFactor.toString());
logger.info('⛽ GAS CALCULATIONS!', {
logger.debug('⛽ GAS CALCULATIONS!', {
estimatedGas: estimatedGas.toString(),
gasLimit: gasLimit.toString(),
lastBlockGasLimit: lastBlockGasLimit,
Expand All @@ -413,26 +422,24 @@ export async function estimateGasWithPadding(

// If the safe estimation is above the last block gas limit, use it
if (greaterThan(estimatedGas.toString(), lastBlockGasLimit)) {
logger.info('⛽ returning orginal gas estimation', {
logger.debug('⛽ returning orginal gas estimation', {
esimatedGas: estimatedGas.toString(),
});
return estimatedGas.toString();
}
// If the estimation is below the last block gas limit, use the padded estimate
if (greaterThan(lastBlockGasLimit, paddedGas)) {
logger.info('⛽ returning padded gas estimation', { paddedGas });
logger.debug('⛽ returning padded gas estimation', { paddedGas });
return paddedGas;
}
// otherwise default to the last block gas limit
logger.info('⛽ returning last block gas limit', { lastBlockGasLimit });
logger.debug('⛽ returning last block gas limit', { lastBlockGasLimit });
return lastBlockGasLimit;
} catch (e: any) {
} catch (e) {
/*
* Reported ~400x per day, but if it's not actionable it might as well be a warning.
*/
logger.warn('Error calculating gas limit with padding', {
message: e.message,
});
logger.warn('Error calculating gas limit with padding', { message: e instanceof Error ? e.message : 'Unknown error' });
return null;
}
}
Expand Down Expand Up @@ -522,7 +529,7 @@ export const resolveUnstoppableDomain = async (domain: string): Promise<string |
.then((address: string) => {
return address;
})
.catch((error: any) => {
.catch(error => {
logger.error(new RainbowError(`resolveUnstoppableDomain error`), {
message: error.message,
});
Expand Down Expand Up @@ -779,7 +786,7 @@ export const estimateGasLimit = async (
recipient: string;
amount: number;
},
addPadding: boolean = false,
addPadding = false,
provider: StaticJsonRpcProvider | null = null,
network: Network = Network.mainnet
): Promise<string | null> => {
Expand Down
11 changes: 4 additions & 7 deletions src/hooks/useRefreshAccountData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ import { captureException } from '@sentry/react-native';
import delay from 'delay';
import { useCallback, useState } from 'react';
import { useDispatch } from 'react-redux';
import { getCachedProviderForNetwork, isHardHat } from '@/handlers/web3';
import NetworkTypes from '../helpers/networkTypes';
import { getIsHardhatConnected } from '@/handlers/web3';
import { walletConnectLoadState } from '../redux/walletconnect';
import { fetchWalletENSAvatars, fetchWalletNames } from '../redux/wallets';
import useAccountSettings from './useAccountSettings';
Expand All @@ -16,14 +15,12 @@ import { positionsQueryKey } from '@/resources/defi/PositionsQuery';

export default function useRefreshAccountData() {
const dispatch = useDispatch();
const { accountAddress, network, nativeCurrency } = useAccountSettings();
const { accountAddress, nativeCurrency } = useAccountSettings();
const [isRefreshing, setIsRefreshing] = useState(false);
const profilesEnabled = useExperimentalFlag(PROFILES);

const fetchAccountData = useCallback(async () => {
const provider = getCachedProviderForNetwork(network);
const providerUrl = provider?.connection?.url;
const connectedToHardhat = isHardHat(providerUrl);
const connectedToHardhat = getIsHardhatConnected();

queryClient.invalidateQueries({
queryKey: nftsQueryKey({ address: accountAddress }),
Expand Down Expand Up @@ -57,7 +54,7 @@ export default function useRefreshAccountData() {
captureException(error);
throw error;
}
}, [accountAddress, dispatch, nativeCurrency, network, profilesEnabled]);
}, [accountAddress, dispatch, nativeCurrency, profilesEnabled]);

const refresh = useCallback(async () => {
if (isRefreshing) return;
Expand Down
4 changes: 2 additions & 2 deletions src/networks/arbitrum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ export const getArbitrumNetworkObject = (): NetworkProperties => {
address: ARBITRUM_ETH_ADDRESS,
},

rpc: proxyRpcEndpoint(arbitrum.id),
getProvider: getProviderForNetwork(Network.arbitrum),
rpc: () => proxyRpcEndpoint(arbitrum.id),
getProvider: () => getProviderForNetwork(Network.arbitrum),
balanceCheckerAddress: '0x54A4E5800345c01455a7798E0D96438364e22723',

// features
Expand Down
4 changes: 2 additions & 2 deletions src/networks/avalanche.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ export const getAvalancheNetworkObject = (): NetworkProperties => {
address: AVAX_AVALANCHE_ADDRESS,
},

rpc: proxyRpcEndpoint(avalanche.id),
getProvider: getProviderForNetwork(Network.avalanche),
rpc: () => proxyRpcEndpoint(avalanche.id),
getProvider: () => getProviderForNetwork(Network.avalanche),
// need to find balance checker address
balanceCheckerAddress: '',

Expand Down
4 changes: 2 additions & 2 deletions src/networks/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ export const getBaseNetworkObject = (): NetworkProperties => {
address: BASE_ETH_ADDRESS,
},

rpc: proxyRpcEndpoint(base.id),
getProvider: getProviderForNetwork(Network.base),
rpc: () => proxyRpcEndpoint(base.id),
getProvider: () => getProviderForNetwork(Network.base),
balanceCheckerAddress: '0x1C8cFdE3Ba6eFc4FF8Dd5C93044B9A690b6CFf36',

// features
Expand Down
4 changes: 2 additions & 2 deletions src/networks/blast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ export const getBlastNetworkObject = (): NetworkProperties => {
},

balanceCheckerAddress: '',
rpc: proxyRpcEndpoint(BLAST_CHAIN_ID),
getProvider: getProviderForNetwork(Network.blast),
rpc: () => proxyRpcEndpoint(BLAST_CHAIN_ID),
getProvider: () => getProviderForNetwork(Network.blast),

// features
features: {
Expand Down
4 changes: 2 additions & 2 deletions src/networks/bsc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ export const getBSCNetworkObject = (): NetworkProperties => {
},

// this should be refactored to have less deps
rpc: proxyRpcEndpoint(bsc.id),
getProvider: getProviderForNetwork(Network.bsc),
rpc: () => proxyRpcEndpoint(bsc.id),
getProvider: () => getProviderForNetwork(Network.bsc),
balanceCheckerAddress: '0x400A9f1Bb1Db80643C33710C2232A0D74EF5CFf1',

// features
Expand Down
4 changes: 2 additions & 2 deletions src/networks/degen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ export const getDegenNetworkObject = (): NetworkProperties => {
address: DEGEN_CHAIN_DEGEN_ADDRESS,
},

rpc: proxyRpcEndpoint(degen.id),
getProvider: getProviderForNetwork(Network.degen),
rpc: () => proxyRpcEndpoint(degen.id),
getProvider: () => getProviderForNetwork(Network.degen),
// need to find balance checker address
balanceCheckerAddress: '',

Expand Down
4 changes: 2 additions & 2 deletions src/networks/gnosis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ export const getGnosisNetworkObject = (): NetworkProperties => {
address: ETH_ADDRESS,
},

rpc: '',
getProvider: getProviderForNetwork(Network.optimism),
rpc: () => '',
getProvider: () => getProviderForNetwork(Network.optimism),
balanceCheckerAddress: '',

// features
Expand Down
4 changes: 2 additions & 2 deletions src/networks/goerli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ export const getGoerliNetworkObject = (): NetworkProperties => {
},

// this should be refactored to have less deps
getProvider: getProviderForNetwork(Network.goerli),
rpc: proxyRpcEndpoint(goerli.id),
getProvider: () => getProviderForNetwork(Network.goerli),
rpc: () => proxyRpcEndpoint(goerli.id),
balanceCheckerAddress: '0xf3352813b612a2d198e437691557069316b84ebe',

// features
Expand Down
Loading

0 comments on commit 7cdcc7a

Please sign in to comment.