Skip to content

Commit

Permalink
Sort token by volume
Browse files Browse the repository at this point in the history
  • Loading branch information
thiagoulloa committed Nov 16, 2024
1 parent 14f45ab commit 0129ba0
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 60 deletions.
122 changes: 66 additions & 56 deletions src/components/SearchModal/CurrencyList/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@ import { CSSProperties, MutableRefObject, useCallback, useEffect, useMemo, useSt
import { Check } from 'react-feather';
import { FixedSizeList } from 'react-window';
import { AccountResponse } from '@stellar/stellar-sdk/lib/horizon';
import useSWRImmutable from 'swr/immutable';

import { CircularProgress, Typography, styled } from 'soroswap-ui';
import Column, { AutoColumn } from 'components/Column';
import Loader from 'components/Icons/LoadingSpinner';
import CurrencyLogo from 'components/Logo/CurrencyLogo';
import Row, { RowFixed } from 'components/Row';
import { TokenType } from '../../../interfaces';
import { TokenType, TokenVolumeData } from '../../../interfaces';
import StyledRow from '../../Row';
import { LoadingRows, MenuItem } from '../styleds';

Expand All @@ -20,7 +21,6 @@ import useHorizonLoadAccount from 'hooks/useHorizonLoadAccount';

import { isAddress, shortenAddress } from 'helpers/address';
import { formatTokenAmount } from 'helpers/format';
import useSWRImmutable from 'swr/immutable';

function currencyKey(currency: TokenType): string {
return currency.contract ? currency.contract : 'ETHER';
Expand Down Expand Up @@ -107,17 +107,30 @@ export function CurrencyRow({
otherSelected,
style,
showCurrencyAmount,
balance,
}: {
currency: TokenType;
onSelect: (hasWarning: boolean) => void;
isSelected: boolean;
otherSelected: boolean;
showCurrencyAmount?: boolean;
balance?: number;
eventProperties: Record<string, unknown>;
style?: CSSProperties;
}) {
const sorobanContext = useSorobanReact();

const { address } = sorobanContext;

const { tokenBalancesResponse } = useGetMyBalances();

const { account } = useHorizonLoadAccount();

const { data, isLoading } = useSWRImmutable(
sorobanContext.activeChain && sorobanContext.address && account
? ['currencyBalance', tokenBalancesResponse, currency, sorobanContext, account]
: null,
([_, tokenBalancesResponse, currency, sorobanContext, account]) =>
getCurrencyBalance(tokenBalancesResponse, currency, sorobanContext, account),
);
const shortenSorobanClassicAsset = (currency: TokenType) => {
if (!currency) return '';
if (currency?.name && currency.name.toString().length > 56) {
Expand Down Expand Up @@ -171,9 +184,9 @@ export function CurrencyRow({
{currency.domain ? currency.domain : formattedCurrencyName(currency as TokenType)}
</Typography>
</AutoColumn>
{showCurrencyAmount && balance ? (
{showCurrencyAmount ? (
<RowFixed style={{ justifySelf: 'flex-end' }}>
{<Balance balance={balance.toString() || '0'} />}
{isLoading ? <Loader /> : address ? <Balance balance={data || '0'} /> : null}
{isSelected && <CheckIcon />}
</RowFixed>
) : (
Expand Down Expand Up @@ -245,86 +258,83 @@ export default function CurrencyList({
isAddressSearch: string | false;
isLoading?: boolean;
}) {
const sorobanContext = useSorobanReact();
const { tokenBalancesResponse } = useGetMyBalances();
const { account } = useHorizonLoadAccount();
const [tokenVolumes, setTokenVolumes] = useState<TokenVolumeData[] | null>(null);

const itemData = useMemo(() => {
return otherListTokens && otherListTokens.length > 0
? [...currencies, ...otherListTokens]
: currencies;
}, [currencies, otherListTokens]);

const fetchBalances = async () => {
if (
!account ||
!sorobanContext.activeChain ||
!sorobanContext.address ||
itemData.length === 0
) {
return {};
}
const [sortedItemData, setSortedItemData] = useState<TokenType[]>([]);

const newBalances: Record<string, number> = {};
for (const currency of itemData) {
const balance = await getCurrencyBalance(
tokenBalancesResponse,
currency,
sorobanContext,
account,
);
newBalances[currencyKey(currency)] = Number(balance ?? 0);
const fetchTokenVolumes = async (network: string): Promise<TokenVolumeData[]> => {
const response = await fetch(`https://info.soroswap.finance/api/tokens?network=${network}`);
if (!response.ok) {
throw new Error('Failed to fetch token volumes');
}
return newBalances;
const data = await response.json();
return data.map((token: any) => ({
asset: token.asset,
volume24h: token.volume24h,
}));
};

const { data: balances = {}, error } = useSWRImmutable(
account ? ['balances', account, tokenBalancesResponse, itemData] : null,
fetchBalances,
);
useEffect(() => {
const getVolumes = async () => {
try {
const volumes = await fetchTokenVolumes('MAINNET');
setTokenVolumes(volumes);
} catch (error) {
console.error(error);
}
};
getVolumes();
}, []);

if (error) {
console.error('Error loading balances:', error);
}
const itemData: TokenType[] = useMemo(() => {
if (otherListTokens && otherListTokens?.length > 0) {
return [...currencies, ...otherListTokens];
}
return currencies;
}, [currencies, otherListTokens]);

const sortedItemData = useMemo(() => {
if (!itemData || !Array.isArray(itemData)) {
return [];
useEffect(() => {
if (tokenVolumes) {
const sorted = [...itemData];
sorted.sort((a, b) => {
const volumeA =
tokenVolumes.find((item) => item.asset.contract === a.contract)?.volume24h || 0;
const volumeB =
tokenVolumes.find((item) => item.asset.contract === b.contract)?.volume24h || 0;
return volumeB - volumeA;
});
setSortedItemData(sorted);
}
return [...itemData]
.map((currency) => ({
...currency,
balance: balances[currencyKey(currency)] || 0,
}))
.sort((a, b) => b.balance - a.balance);
}, [itemData, balances]);
}, [itemData, tokenVolumes]);

const Row = useCallback(
function TokenRow({ data, index, style }: TokenRowProps) {
const row = data[index];
const row: TokenType = data[index];

const currency = row;

const isSelected = Boolean(
currency && selectedCurrency && selectedCurrency.contract === currency.contract,
currency && selectedCurrency && selectedCurrency.contract == currency.contract,
);
const otherSelected = Boolean(
currency && otherCurrency && otherCurrency.contract === currency.contract,
currency && otherCurrency && otherCurrency.contract == currency.contract,
);
const handleSelect = (hasWarning: boolean) =>
currency && onCurrencySelect(currency, hasWarning);

const token = currency;

if (currency) {
return (
<CurrencyRow
style={style}
balance={row.balance}
currency={currency}
isSelected={isSelected}
onSelect={handleSelect}
otherSelected={otherSelected}
showCurrencyAmount={showCurrencyAmount}
eventProperties={formatAnalyticsEventProperties(
currency,
token,
index,
data,
searchQuery,
Expand Down
2 changes: 0 additions & 2 deletions src/hooks/tokens/useToken.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ export const findToken = async (
*/
if (stellarAsset && typeof stellarAsset !== 'boolean') {
return {
balance: 0,
issuer: stellarAsset.issuer,
contract: token.contract,
name: stellarAsset.asset,
Expand Down Expand Up @@ -124,7 +123,6 @@ export function useToken(tokenAddress: string | undefined) {
if (sorobanAddress || (stellarAsset && typeof sorobanAddress === 'string')) {
if (stellarAsset && typeof stellarAsset !== 'boolean') {
newTokenData = {
balance: 0,
issuer: stellarAsset.issuer,
contract: sorobanAddress,
name: stellarAsset.asset,
Expand Down
1 change: 0 additions & 1 deletion src/hooks/tokens/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@ export async function getToken(
logo = await getTokenLogo(formattedAddress, sorobanContext);

const token: TokenType = {
balance: 0,
contract: formattedAddress,
name: name as string,
code: symbol as string,
Expand Down
12 changes: 11 additions & 1 deletion src/interfaces/tokens.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
export interface TokenType {
balance?: number;
code: string;
issuer?: string;
contract: string;
Expand All @@ -22,3 +21,14 @@ export type TokenMapType = {
export type TokenBalancesMap = {
[tokenAddress: string]: { usdValue: number; balance: string };
};

export interface TokenVolumeData {
asset: {
name: string;
contract: string;
code: string;
icon: string;
decimals: number;
};
volume24h: number;
}

0 comments on commit 0129ba0

Please sign in to comment.