Skip to content

Commit

Permalink
feat: perceived finality initial
Browse files Browse the repository at this point in the history
  • Loading branch information
derHowie committed Aug 23, 2024
1 parent 79be98c commit b9d17fa
Show file tree
Hide file tree
Showing 4 changed files with 338 additions and 13 deletions.
41 changes: 41 additions & 0 deletions src/hooks/useWatchPendingTxs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import { useNonceStore } from '@/state/nonces';
import { Address } from 'viem';
import { nftsQueryKey } from '@/resources/nfts';
import { getNftSortForAddress } from './useNFTsSortBy';
import { staleBalancesStore } from '@/state/staleBalances';
import { ChainId } from '@/__swaps__/types/chains';

export const useWatchPendingTransactions = ({ address }: { address: string }) => {
const { storePendingTransactions, setPendingTransactions } = usePendingTransactionsStore(state => ({
Expand Down Expand Up @@ -165,6 +167,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 @@ -188,6 +191,20 @@ export const useWatchPendingTransactions = ({ address }: { address: string }) =>
);

if (minedTransactions.length) {
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 }),
});

const chainIds = RainbowNetworks.filter(network => network.enabled && network.networkType !== 'testnet').map(network => network.id);
await queryClient.refetchQueries({
queryKey: consolidatedTransactionsQueryKey({
Expand Down Expand Up @@ -216,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,
},
});
}
}
46 changes: 33 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 { useQuery } from '@tanstack/react-query';
import { filterPositionsData, parseAddressAsset } from './assets';
import { fetchHardhatBalances } from './hardhatAssets';
import { AddysAccountAssetsMeta, AddysAccountAssetsResponse, RainbowAddressAssets } from './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 @@ -62,9 +74,11 @@ async function userAssetsQueryFunction({
}

try {
staleBalancesStore.getState().clearExpiredData(address);
const staleBalanceParam = staleBalancesStore.getState().getStaleBalancesQueryParam(address);
const chainIds = RainbowNetworks.filter(network => network.enabled && network.networkType !== 'testnet').map(network => network.id);

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

// grab cached data for chain IDs with errors
Expand Down Expand Up @@ -100,7 +114,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 @@ -140,12 +154,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
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 { ChainId } from '@/__swaps__/types/chains';
import { DAI_ADDRESS, OP_ADDRESS } from '@/references';
import { ETH_ADDRESS } from '@rainbow-me/swaps';

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 b9d17fa

Please sign in to comment.