Skip to content

Commit

Permalink
Merge branch 'develop' of github.com:rainbow-me/rainbow into @bruno/p…
Browse files Browse the repository at this point in the history
…ublish-artifacts
  • Loading branch information
brunobar79 committed Aug 29, 2024
2 parents f65d177 + 349bd22 commit a96302e
Show file tree
Hide file tree
Showing 7 changed files with 343 additions and 37 deletions.
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,6 @@
"moti": "0.28",
"multiformats": "9.6.2",
"nanoid": "3.2.0",
"p-queue": "7.2.0",
"p-wait-for": "4.1.0",
"pako": "2.0.4",
"parse-ms": "2.1.0",
Expand Down
40 changes: 40 additions & 0 deletions src/hooks/useWatchPendingTxs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { Address } from 'viem';
import { nftsQueryKey } from '@/resources/nfts';
import { getNftSortForAddress } from './useNFTsSortBy';
import { ChainId } from '@/networks/types';
import { staleBalancesStore } from '@/state/staleBalances';

export const useWatchPendingTransactions = ({ address }: { address: string }) => {
const { storePendingTransactions, setPendingTransactions } = usePendingTransactionsStore(state => ({
Expand Down Expand Up @@ -164,6 +165,7 @@ export const useWatchPendingTransactions = ({ address }: { address: string }) =>
);

const watchPendingTransactions = useCallback(async () => {
const connectedToHardhat = getIsHardhatConnected();
if (!pendingTransactions?.length) return;
const updatedPendingTransactions = await Promise.all(
pendingTransactions.map((tx: RainbowTransaction) => processPendingTransaction(tx))
Expand All @@ -190,6 +192,20 @@ export const useWatchPendingTransactions = ({ address }: { address: string }) =>
const chainIds = RainbowNetworkObjects.filter(networkObject => networkObject.enabled && networkObject.networkType !== 'testnet').map(
networkObject => networkObject.id
);
minedTransactions.forEach(tx => {
if (tx.changes?.length) {
tx.changes?.forEach(change => {
processStaleAsset({ asset: change?.asset, address, transactionHash: tx?.hash });
});
} else if (tx.asset) {
processStaleAsset({ address, asset: tx.asset, transactionHash: tx?.hash });
}
});

queryClient.refetchQueries({
queryKey: userAssetsQueryKey({ address, currency: nativeCurrency, connectedToHardhat }),
});

await queryClient.refetchQueries({
queryKey: consolidatedTransactionsQueryKey({
address,
Expand Down Expand Up @@ -217,3 +233,27 @@ export const useWatchPendingTransactions = ({ address }: { address: string }) =>

return { watchPendingTransactions };
};

function processStaleAsset({
asset,
address,
transactionHash,
}: {
asset: RainbowTransaction['asset'];
address: string;
transactionHash: string;
}) {
const { addStaleBalance } = staleBalancesStore.getState();
const chainId = asset?.chainId;
if (asset && typeof chainId === 'number') {
const changedAssetAddress = asset?.address as Address;
addStaleBalance({
address,
chainId,
info: {
address: changedAssetAddress,
transactionHash,
},
});
}
}
47 changes: 34 additions & 13 deletions src/resources/assets/UserAssetsQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { filterPositionsData, parseAddressAsset } from './assets';
import { fetchHardhatBalances } from './hardhatAssets';
import { AddysAccountAssetsMeta, AddysAccountAssetsResponse, RainbowAddressAssets } from './types';
import { Network } from '@/networks/types';
import { staleBalancesStore } from '@/state/staleBalances';

// ///////////////////////////////////////////////
// Query Types
Expand All @@ -32,15 +33,26 @@ type UserAssetsQueryKey = ReturnType<typeof userAssetsQueryKey>;
// ///////////////////////////////////////////////
// Query Function

const fetchUserAssetsForChainIds = async (address: string, currency: NativeCurrencyKey, chainIds: number[]) => {
const fetchUserAssetsForChainIds = async ({
address,
currency,
chainIds,
staleBalanceParam,
}: {
address: string;
currency: NativeCurrencyKey;
chainIds: number[];
staleBalanceParam?: string;
}) => {
const chainIdsString = chainIds.join(',');
const url = `https://addys.p.rainbow.me/v3/${chainIdsString}/${address}/assets`;
let url = `https://addys.p.rainbow.me/v3/${chainIdsString}/${address}/assets?currency=${currency.toLowerCase()}`;

if (staleBalanceParam) {
url += url + staleBalanceParam;
}

const response = await rainbowFetch(url, {
method: 'get',
params: {
currency: currency.toLowerCase(),
},
headers: {
Authorization: `Bearer ${ADDYS_API_KEY}`,
},
Expand All @@ -66,7 +78,10 @@ async function userAssetsQueryFunction({
network => network.id
);

const { erroredChainIds, results } = await fetchAndParseUserAssetsForChainIds(address, currency, chainIds);
staleBalancesStore.getState().clearExpiredData(address);
const staleBalanceParam = staleBalancesStore.getState().getStaleBalancesQueryParam(address);

const { erroredChainIds, results } = await fetchAndParseUserAssetsForChainIds({ address, currency, chainIds, staleBalanceParam });
let parsedSuccessResults = results;

// grab cached data for chain IDs with errors
Expand Down Expand Up @@ -102,7 +117,7 @@ const retryErroredChainIds = async (
connectedToHardhat: boolean,
erroredChainIds: number[]
) => {
const { meta, results } = await fetchAndParseUserAssetsForChainIds(address, currency, erroredChainIds);
const { meta, results } = await fetchAndParseUserAssetsForChainIds({ address, currency, chainIds: erroredChainIds });
let parsedSuccessResults = results;
const successChainIds = meta?.chain_ids;

Expand Down Expand Up @@ -142,12 +157,18 @@ interface AssetsAndMetadata {
results: RainbowAddressAssets;
}

const fetchAndParseUserAssetsForChainIds = async (
address: string,
currency: NativeCurrencyKey,
chainIds: number[]
): Promise<AssetsAndMetadata> => {
const data = await fetchUserAssetsForChainIds(address, currency, chainIds);
const fetchAndParseUserAssetsForChainIds = async ({
address,
currency,
chainIds,
staleBalanceParam,
}: {
address: string;
currency: NativeCurrencyKey;
chainIds: number[];
staleBalanceParam?: string;
}): Promise<AssetsAndMetadata> => {
const data = await fetchUserAssetsForChainIds({ address, currency, chainIds, staleBalanceParam });
let parsedSuccessResults = parseUserAssetsByChain(data);

// filter out positions data
Expand Down
8 changes: 4 additions & 4 deletions src/resources/transactions/firstTransactionTimestampQuery.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { useQuery } from '@tanstack/react-query';
import PQueue from 'p-queue/dist';

import { createQueryKey, queryClient, QueryConfig, QueryFunctionArgs, QueryFunctionResult } from '@/react-query';
import { getFirstTransactionTimestamp } from '@/utils/ethereumUtils';
Expand All @@ -23,8 +22,6 @@ export type FirstTransactionTimestampQueryKey = ReturnType<typeof firstTransacti
// ///////////////////////////////////////////////
// Query Function

const queue = new PQueue({ interval: 1000, intervalCap: 5 });

export async function firstTransactionTimestampQueryFunction({
queryKey: [{ addressOrName }],
}: QueryFunctionArgs<typeof firstTransactionTimestampQueryKey>) {
Expand All @@ -35,7 +32,10 @@ export async function firstTransactionTimestampQueryFunction({
address = (await fetchENSAddress({ name: addressOrName })) ?? '';
}

const timestamp = address ? await queue.add(async () => getFirstTransactionTimestamp(address)) : null;
let timestamp;
if (address) {
timestamp = await getFirstTransactionTimestamp(address);
}
return timestamp ?? null;
}

Expand Down
165 changes: 165 additions & 0 deletions src/state/staleBalances/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
import { Address } from 'viem';

import { staleBalancesStore } from '.';
import { DAI_ADDRESS, OP_ADDRESS } from '@/references';
import { ETH_ADDRESS } from '@rainbow-me/swaps';
import { ChainId } from '@/networks/types';

const TEST_ADDRESS_1 = '0xFOO';
const TEST_ADDRESS_2 = '0xBAR';
const THEN = Date.now() - 700000;
const WHEN = Date.now() + 60000;

test('should be able to add asset information to the staleBalances object', async () => {
const { addStaleBalance, staleBalances } = staleBalancesStore.getState();
expect(staleBalances).toStrictEqual({});
addStaleBalance({
address: TEST_ADDRESS_1,
chainId: ChainId.mainnet,
info: {
address: DAI_ADDRESS,
transactionHash: '0xFOOBAR',
expirationTime: THEN,
},
});
addStaleBalance({
address: TEST_ADDRESS_1,
chainId: ChainId.mainnet,
info: {
address: ETH_ADDRESS,
transactionHash: '0xFOOBAR',
expirationTime: WHEN,
},
});
const newStaleBalances = staleBalancesStore.getState().staleBalances;
expect(newStaleBalances).toStrictEqual({
[TEST_ADDRESS_1]: {
[ChainId.mainnet]: {
[DAI_ADDRESS]: {
address: DAI_ADDRESS,
transactionHash: '0xFOOBAR',
expirationTime: THEN,
},
[ETH_ADDRESS]: {
address: ETH_ADDRESS,
transactionHash: '0xFOOBAR',
expirationTime: WHEN,
},
},
},
});
});

test('should generate accurate stale balance query params and clear expired data - case #1', async () => {
const { getStaleBalancesQueryParam, clearExpiredData } = staleBalancesStore.getState();
clearExpiredData(TEST_ADDRESS_1);
const queryParam = getStaleBalancesQueryParam(TEST_ADDRESS_1);
expect(queryParam).toStrictEqual(`&token=${ChainId.mainnet}.${ETH_ADDRESS}`);
});

test('should be able to remove expired stale balance and preserve unexpired data', async () => {
const { addStaleBalance, clearExpiredData } = staleBalancesStore.getState();
addStaleBalance({
address: TEST_ADDRESS_1,
chainId: ChainId.mainnet,
info: {
address: DAI_ADDRESS,
transactionHash: '0xFOOBAR',
expirationTime: THEN,
},
});
addStaleBalance({
address: TEST_ADDRESS_1,
chainId: ChainId.mainnet,
info: {
address: ETH_ADDRESS as Address,
transactionHash: '0xFOOBAR',
expirationTime: WHEN,
},
});
clearExpiredData(TEST_ADDRESS_1);
const newStaleBalances = staleBalancesStore.getState().staleBalances;
expect(newStaleBalances).toStrictEqual({
[TEST_ADDRESS_1]: {
[ChainId.mainnet]: {
[ETH_ADDRESS]: {
address: ETH_ADDRESS,
transactionHash: '0xFOOBAR',
expirationTime: WHEN,
},
},
},
});
});

test('should preserve data from other addresses when clearing expired data', async () => {
const { addStaleBalance, clearExpiredData } = staleBalancesStore.getState();
addStaleBalance({
address: TEST_ADDRESS_1,
chainId: ChainId.mainnet,
info: {
address: DAI_ADDRESS,
transactionHash: '0xFOOBAR',
expirationTime: THEN,
},
});
addStaleBalance({
address: TEST_ADDRESS_2,
chainId: ChainId.mainnet,
info: {
address: ETH_ADDRESS as Address,
transactionHash: '0xFOOBAR',
expirationTime: WHEN,
},
});
clearExpiredData(TEST_ADDRESS_1);
const newStaleBalances = staleBalancesStore.getState().staleBalances;
expect(newStaleBalances).toStrictEqual({
[TEST_ADDRESS_1]: {
[ChainId.mainnet]: {
[ETH_ADDRESS]: {
address: ETH_ADDRESS,
transactionHash: '0xFOOBAR',
expirationTime: WHEN,
},
},
},
[TEST_ADDRESS_2]: {
[ChainId.mainnet]: {
[ETH_ADDRESS]: {
address: ETH_ADDRESS,
transactionHash: '0xFOOBAR',
expirationTime: WHEN,
},
},
},
});
});

test('should generate accurate stale balance query params and clear expired data - case #2', async () => {
const { getStaleBalancesQueryParam, clearExpiredData } = staleBalancesStore.getState();
clearExpiredData(TEST_ADDRESS_2);
const queryParam = getStaleBalancesQueryParam(TEST_ADDRESS_2);
expect(queryParam).toStrictEqual(`&token=${ChainId.mainnet}.${ETH_ADDRESS}`);
});

test('should generate accurate stale balance query params and clear expired data - case #3', async () => {
const { addStaleBalance, getStaleBalancesQueryParam, clearExpiredData } = staleBalancesStore.getState();
addStaleBalance({
address: TEST_ADDRESS_1,
chainId: ChainId.optimism,
info: {
address: OP_ADDRESS,
transactionHash: '0xFOOBAR',
expirationTime: WHEN,
},
});

clearExpiredData(TEST_ADDRESS_1);
const queryParam = getStaleBalancesQueryParam(TEST_ADDRESS_1);
expect(queryParam).toStrictEqual(`&token=${ChainId.mainnet}.${ETH_ADDRESS}&token=${ChainId.optimism}.${OP_ADDRESS}`);

clearExpiredData(TEST_ADDRESS_2);
const queryParam2 = getStaleBalancesQueryParam(TEST_ADDRESS_2);
expect(queryParam2).toStrictEqual(`&token=${ChainId.mainnet}.${ETH_ADDRESS}`);
});
Loading

0 comments on commit a96302e

Please sign in to comment.