Skip to content

Commit

Permalink
split up non evm selectors
Browse files Browse the repository at this point in the history
  • Loading branch information
owencraston committed Jan 20, 2025
1 parent bc7a215 commit 2f815ba
Show file tree
Hide file tree
Showing 5 changed files with 280 additions and 257 deletions.
3 changes: 2 additions & 1 deletion app/selectors/multichain/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './multichain';
export * from './multichainEvm';
export * from './multichainNonEvm';
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@ import { RootState } from '../../reducers';
import {
selectedAccountNativeTokenCachedBalanceByChainId,
selectAccountTokensAcrossChains,
selectIsBitcoinSupportEnabled,
selectIsBitcoinTestnetSupportEnabled,
} from './multichain';
} from './multichainEvm';

describe('Multichain Selectors', () => {
const mockState: RootState = {
Expand Down Expand Up @@ -89,10 +87,6 @@ describe('Multichain Selectors', () => {
},
},
},
multichainSettings: {
bitcoinSupportEnabled: true,
bitcoinTestnetSupportEnabled: false,
},
} as unknown as RootState;

describe('selectedAccountNativeTokenCachedBalanceByChainId', () => {
Expand Down Expand Up @@ -175,14 +169,4 @@ describe('Multichain Selectors', () => {
);
});
});

describe('Bitcoin Support Flags', () => {
it('should return bitcoin support enabled state', () => {
expect(selectIsBitcoinSupportEnabled(mockState)).toBe(true);
});

it('should return bitcoin testnet support enabled state', () => {
expect(selectIsBitcoinTestnetSupportEnabled(mockState)).toBe(false);
});
});
});
220 changes: 220 additions & 0 deletions app/selectors/multichain/multichainEvm.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
import { createSelector } from 'reselect';
import { Hex } from '@metamask/utils';
import { Token, getNativeTokenAddress } from '@metamask/assets-controllers';
import {
selectSelectedInternalAccountFormattedAddress,
selectSelectedInternalAccount,
} from '../accountsController';
import { selectAllTokens } from '../tokensController';
import { selectAccountsByChainId } from '../accountTrackerController';
import { selectNetworkConfigurations } from '../networkController';
import { TokenI } from '../../components/UI/Tokens/types';
import { renderFromWei, weiToFiat } from '../../util/number';
import { hexToBN, toHex } from '@metamask/controller-utils';
import {
selectCurrencyRates,
selectCurrentCurrency,
} from '../currencyRateController';

interface NativeTokenBalance {
balance: string;
stakedBalance: string;
isStaked: boolean;
name: string;
}

type ChainBalances = Record<string, NativeTokenBalance>;

/**
* Get the cached native token balance for the selected account by chainId.
*
* @param {RootState} state - The root state.
* @returns {ChainBalances} The cached native token balance for the selected account by chainId.
*/
export const selectedAccountNativeTokenCachedBalanceByChainId = createSelector(
[selectSelectedInternalAccountFormattedAddress, selectAccountsByChainId],
(selectedAddress, accountsByChainId): ChainBalances => {
if (!selectedAddress || !accountsByChainId) {
return {};
}

const result: ChainBalances = {};
for (const chainId in accountsByChainId) {
const accounts = accountsByChainId[chainId];
const account = accounts[selectedAddress];
if (account) {
result[chainId] = {
balance: account.balance,
stakedBalance: account.stakedBalance ?? '0x0',
isStaked: account.stakedBalance !== '0x0',
name: '',
};
}
}

return result;
},
);

/**
* Selector to get native tokens for the selected account across all chains.
*/
export const selectNativeTokensAcrossChains = createSelector(
[
selectNetworkConfigurations,
selectedAccountNativeTokenCachedBalanceByChainId,
selectCurrencyRates,
selectCurrentCurrency,
],
(
networkConfigurations,
nativeTokenBalancesByChainId,
currencyRates,
currentCurrency,
) => {
const tokensByChain: { [chainId: string]: TokenI[] } = {};
for (const token of Object.values(networkConfigurations)) {
const nativeChainId = token.chainId as Hex;
const nativeTokenInfoByChainId =
nativeTokenBalancesByChainId[nativeChainId];
const isETH = ['ETH', 'GOETH', 'SepoliaETH', 'LineaETH'].includes(
token.nativeCurrency || '',
);

const name = isETH ? 'Ethereum' : token.nativeCurrency;
const logo = isETH ? '../images/eth-logo-new.png' : '';
tokensByChain[nativeChainId] = [];

const nativeBalanceFormatted = renderFromWei(
nativeTokenInfoByChainId?.balance,
);
const stakedBalanceFormatted = renderFromWei(
nativeTokenInfoByChainId?.stakedBalance,
);

let balanceFiat = '';
let stakedBalanceFiat = '';

const conversionRate =
currencyRates?.[token.nativeCurrency]?.conversionRate ?? 0;

balanceFiat = weiToFiat(
// TODO: Replace "any" with type
// eslint-disable-next-line @typescript-eslint/no-explicit-any
hexToBN(nativeTokenInfoByChainId?.balance) as any,
conversionRate,
currentCurrency,
);
stakedBalanceFiat = weiToFiat(
// TODO: Replace "any" with type
// eslint-disable-next-line @typescript-eslint/no-explicit-any
hexToBN(nativeTokenInfoByChainId?.stakedBalance) as any,
conversionRate,
currentCurrency,
);

const tokenByChain = {
...nativeTokenInfoByChainId,
name,
address: getNativeTokenAddress(nativeChainId),
balance: nativeBalanceFormatted,
chainId: nativeChainId,
isNative: true,
aggregators: [],
balanceFiat,
image: '',
logo,
isETH,
decimals: 18,
symbol: name,
isStaked: false,
ticker: token.nativeCurrency,
};

// Non-staked tokens
tokensByChain[nativeChainId].push(tokenByChain);

if (
nativeTokenInfoByChainId &&
nativeTokenInfoByChainId.isStaked &&
nativeTokenInfoByChainId.stakedBalance !== '0x00' &&
nativeTokenInfoByChainId.stakedBalance !== toHex(0)
) {
// Staked tokens
tokensByChain[nativeChainId].push({
...nativeTokenInfoByChainId,
nativeAsset: tokenByChain,
chainId: nativeChainId,
address: getNativeTokenAddress(nativeChainId),
balance: stakedBalanceFormatted,
balanceFiat: stakedBalanceFiat,
isNative: true,
aggregators: [],
image: '',
logo,
isETH,
decimals: 18,
name: 'Staked Ethereum',
symbol: name,
isStaked: true,
ticker: token.nativeCurrency,
});
}
}

return tokensByChain;
},
);

/**
* Get the tokens for the selected account across all chains.
*
* @param {RootState} state - The root state.
* @returns {TokensByChain} The tokens for the selected account across all chains.
*/
export const selectAccountTokensAcrossChains = createSelector(
[
selectSelectedInternalAccount,
selectAllTokens,
selectNetworkConfigurations,
selectNativeTokensAcrossChains,
],
(selectedAccount, allTokens, networkConfigurations, nativeTokens) => {
const selectedAddress = selectedAccount?.address;
const tokensByChain: {
[chainId: string]: (
| TokenI
| (Token & { isStaked?: boolean; isNative?: boolean; isETH?: boolean })
)[];
} = {};

if (!selectedAddress) {
return tokensByChain;
}

// Create a list of available chainIds
const chainIds = Object.keys(networkConfigurations);

for (const chainId of chainIds) {
const currentChainId = chainId as Hex;
const nonNativeTokens =
allTokens[currentChainId]?.[selectedAddress]?.map((token) => ({
...token,
token: token.name,
chainId,
isETH: false,
isNative: false,
balanceFiat: '',
isStaked: false,
})) || [];

// Add both native and non-native tokens
tokensByChain[currentChainId] = [
...(nativeTokens[currentChainId] || []),
...nonNativeTokens,
];
}

return tokensByChain;
},
);
38 changes: 38 additions & 0 deletions app/selectors/multichain/multichainNonEvm.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { RootState } from '../../reducers';
import {
selectIsBitcoinSupportEnabled,
selectIsBitcoinTestnetSupportEnabled,
} from './multichainNonEvm';

describe('MultichainNonEvm Selectors', () => {
const mockState: RootState = {
engine: {
backgroundState: {
AccountsController: {
internalAccounts: {
selectedAccount: '0xAddress1',
accounts: {
'0xAddress1': {
address: '0xAddress1',
},
},
},
},
},
},
multichainSettings: {
bitcoinSupportEnabled: true,
bitcoinTestnetSupportEnabled: false,
},
} as unknown as RootState;

describe('Bitcoin Support Flags', () => {
it('should return bitcoin support enabled state', () => {
expect(selectIsBitcoinSupportEnabled(mockState)).toBe(true);
});

it('should return bitcoin testnet support enabled state', () => {
expect(selectIsBitcoinTestnetSupportEnabled(mockState)).toBe(false);
});
});
});
Loading

0 comments on commit 2f815ba

Please sign in to comment.