-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
bc7a215
commit 2f815ba
Showing
5 changed files
with
280 additions
and
257 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
export * from './multichain'; | ||
export * from './multichainEvm'; | ||
export * from './multichainNonEvm'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
}, | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
}); | ||
}); | ||
}); |
Oops, something went wrong.