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

Fix provider recursion bug, provider function types #5829

Merged
merged 4 commits into from
Jun 11, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
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 @@ -172,7 +172,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
86 changes: 51 additions & 35 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 @@ -165,8 +164,13 @@ const isNetworkEnum = (network: Network | string): network is Network => {
* @return A promise that resolves with an Ethers Network when the provider is ready.
*/
export const web3SetHttpProvider = async (network: Network | string): Promise<EthersNetwork> => {
web3Provider = await getProviderForNetwork(network);
return web3Provider.ready;
const provider = await getProviderForNetwork(network);
if (!provider) {
throw new RainbowError('Provider not found');
}
await provider.ready;
web3Provider = provider;
return provider.ready;
};

/**
Expand Down Expand Up @@ -207,8 +211,12 @@ export const getFlashbotsProvider = async () => {
);
};

export const getCachedProviderForNetwork = (network: Network = Network.mainnet) => {
return networkProviders[network]!;
export const getCachedProviderForNetwork = (network: Network = Network.mainnet): StaticJsonRpcProvider | undefined => {
const cachedProvider = networkProviders.get(network);
if (cachedProvider) {
return cachedProvider;
}
return undefined;
};
christianbaroni marked this conversation as resolved.
Show resolved Hide resolved

/**
Expand All @@ -217,27 +225,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;
}
};
christianbaroni marked this conversation as resolved.
Show resolved Hide resolved

/**
* @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 +266,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 +377,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 +401,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 +422,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 +431,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 +538,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 +795,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
Loading
Loading