Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for using different tokens on ParaTimes #1265

Merged
merged 4 commits into from
Feb 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .changelog/1265.feature.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add support for using different tokens on ParaTimes
Binary file added public/euroe.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
85 changes: 32 additions & 53 deletions src/app/components/Account/index.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
import { FC } from 'react'
import { useTranslation } from 'react-i18next'
import { Link as RouterLink } from 'react-router-dom'
import Box from '@mui/material/Box'
import { useScreenSize } from '../../hooks/useScreensize'
import { styled } from '@mui/material/styles'
import { StyledDescriptionList, StyledListTitleWithAvatar } from '../../components/StyledDescriptionList'
import { CopyToClipboard } from '../../components/CopyToClipboard'
import { CoinGeckoReferral } from '../../components/CoinGeckoReferral'
import { TextSkeleton } from '../../components/Skeleton'
import { EvmToken, type RuntimeAccount } from '../../../oasis-nexus/api'
import { TokenPills } from './TokenPills'
Expand All @@ -15,34 +12,29 @@ import { RouteUtils } from '../../utils/route-utils'
import { accountTransactionsContainerId } from '../../pages/AccountDetailsPage/AccountTransactionsCard'
import Link from '@mui/material/Link'
import { DashboardLink } from '../../pages/ParatimeDashboardPage/DashboardLink'
import { getNameForTicker, Ticker } from '../../../types/ticker'
import { TokenPriceInfo } from '../../../coin-gecko/api'
import { getNameForTicker } from '../../../types/ticker'
import { AllTokenPrices } from '../../../coin-gecko/api'
import { ContractCreatorInfo } from './ContractCreatorInfo'
import { ContractVerificationIcon } from '../ContractVerificationIcon'
import { TokenLink } from '../Tokens/TokenLink'
import BigNumber from 'bignumber.js'
import { getPreciseNumberFormat } from '../../../locales/getPreciseNumberFormat'
import { AccountAvatar } from '../AccountAvatar'

export const FiatMoneyAmountBox = styled(Box)(() => ({
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
flex: 1,
}))
import { RuntimeBalanceDisplay } from '../Balance/RuntimeBalanceDisplay'
import { calculateFiatValue } from '../Balance/hooks'
import { FiatMoneyAmount } from '../Balance/FiatMoneyAmount'
import { getFiatCurrencyForScope, getTokensForScope } from '../../../config'

type AccountProps = {
account?: RuntimeAccount
token?: EvmToken
isLoading: boolean
tokenPriceInfo: TokenPriceInfo
tokenPrices: AllTokenPrices
showLayer?: boolean
}

export const Account: FC<AccountProps> = ({ account, token, isLoading, tokenPriceInfo, showLayer }) => {
export const Account: FC<AccountProps> = ({ account, token, isLoading, tokenPrices, showLayer }) => {
const { t } = useTranslation()
const { isMobile } = useScreenSize()
const balance = account?.balances[0]?.balance
const address = account ? account.address_eth ?? account.address : undefined

const transactionsLabel = account ? account.stats.num_txns.toLocaleString() : ''
Expand All @@ -53,15 +45,10 @@ export const Account: FC<AccountProps> = ({ account, token, isLoading, tokenPric
)}#${accountTransactionsContainerId}`
: undefined

const nativeToken = account?.ticker || Ticker.ROSE
const nativeTickerName = getNameForTicker(t, nativeToken)
const {
isLoading: isPriceLoading,
price: tokenFiatValue,
isFree: isTokenFree,
hasUsedCoinGecko,
} = tokenPriceInfo
const nativeTokens = getTokensForScope(account || { network: 'mainnet', layer: 'sapphire' })
const nativeTickerNames = nativeTokens.map(token => getNameForTicker(t, token.ticker))
const contract = account?.evm_contract
const fiatValueInfo = calculateFiatValue(account?.balances, tokenPrices, getFiatCurrencyForScope(account))

return (
<>
Expand Down Expand Up @@ -120,31 +107,19 @@ export const Account: FC<AccountProps> = ({ account, token, isLoading, tokenPric

<dt>{t('common.balance')}</dt>
<dd>
{balance === undefined
? t('common.missing')
: t('common.valueInToken', { ...getPreciseNumberFormat(balance), ticker: nativeTickerName })}
<RuntimeBalanceDisplay balances={account.balances} />
</dd>

<dt>{t('common.tokens')}</dt>
<dd>
<TokenPills account={account} tokens={account.evm_balances} />
</dd>

{!isPriceLoading && !isTokenFree && tokenFiatValue !== undefined && balance && (
{!fiatValueInfo.loading && fiatValueInfo.hasValue && (
<>
<dt>{t('common.fiatValue')}</dt>
<dd>
<FiatMoneyAmountBox>
{t('common.fiatValueInUSD', {
value: new BigNumber(balance).multipliedBy(tokenFiatValue).toFixed(),
formatParams: {
value: {
currency: 'USD',
} satisfies Intl.NumberFormatOptions,
},
})}
{hasUsedCoinGecko && <CoinGeckoReferral />}
</FiatMoneyAmountBox>
<FiatMoneyAmount {...fiatValueInfo} />
</dd>
</>
)}
Expand All @@ -160,21 +135,25 @@ export const Account: FC<AccountProps> = ({ account, token, isLoading, tokenPric
)}
</dd>

<dt>{t('account.totalReceived')}</dt>
<dd>
{t('common.valueInToken', {
...getPreciseNumberFormat(account.stats.total_received),
ticker: nativeTickerName,
})}
</dd>
{nativeTokens.length === 1 && (
<>
<dt>{t('account.totalReceived')}</dt>
<dd>
{t('common.valueInToken', {
...getPreciseNumberFormat(account.stats.total_received),
ticker: nativeTickerNames[0],
})}
</dd>

<dt>{t('account.totalSent')}</dt>
<dd>
{t('common.valueInToken', {
...getPreciseNumberFormat(account.stats.total_sent),
ticker: nativeTickerName,
})}
</dd>
<dt>{t('account.totalSent')}</dt>
<dd>
{t('common.valueInToken', {
...getPreciseNumberFormat(account.stats.total_sent),
ticker: nativeTickerNames[0],
})}
</dd>
</>
)}
</StyledDescriptionList>
)}
</>
Expand Down
51 changes: 51 additions & 0 deletions src/app/components/Balance/FiatMoneyAmount.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { styled } from '@mui/material/styles'
import WarningIcon from '@mui/icons-material/WarningAmber'
import Box from '@mui/material/Box'
import { useTranslation } from 'react-i18next'
import { FC } from 'react'
import { CoinGeckoReferral } from '../CoinGeckoReferral'
import { FiatValueInfo } from './hooks'
import Tooltip from '@mui/material/Tooltip'
import Skeleton from '@mui/material/Skeleton'

export const FiatMoneyAmountBox = styled(Box)(() => ({
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
gap: 4,
flex: 1,
}))

export const FiatMoneyAmount: FC<FiatValueInfo> = ({
value,
fiatCurrency,
hasUsedCoinGecko,
unknownTickers,
loading,
}) => {
const { t } = useTranslation()
return (
<FiatMoneyAmountBox>
<span>
{t('common.fiatValueInUSD', {
value,
formatParams: {
value: {
currency: fiatCurrency,
} satisfies Intl.NumberFormatOptions,
},
})}
{!!unknownTickers.length && (
<Tooltip
title={t('account.failedToLookUpTickers', { tickers: unknownTickers.join(', ') })}
placement="top"
>
<WarningIcon />
</Tooltip>
)}
{loading && <Skeleton variant="rectangular" sx={{ height: 200 }} />}
</span>
{hasUsedCoinGecko && <CoinGeckoReferral />}
</FiatMoneyAmountBox>
)
}
25 changes: 25 additions & 0 deletions src/app/components/Balance/RuntimeBalanceDisplay.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { FC } from 'react'
import { RuntimeSdkBalance } from '../../../oasis-nexus/api'
import { useTranslation } from 'react-i18next'
import { getPreciseNumberFormat } from '../../../locales/getPreciseNumberFormat'

export const RuntimeBalanceDisplay: FC<{ balances: RuntimeSdkBalance[] | undefined }> = ({
balances = [],
}) => {
const { t } = useTranslation()
if (balances.length === 0 || balances[0].balance === undefined) {
return t('common.missing')
}
return (
<>
{balances.map(balance => (
<span key={balance.token_symbol}>
{t('common.valueInToken', {
...getPreciseNumberFormat(balance.balance),
ticker: balance.token_symbol,
})}
</span>
))}
</>
)
}
83 changes: 83 additions & 0 deletions src/app/components/Balance/hooks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { RuntimeSdkBalance } from '../../../oasis-nexus/api'
import { AllTokenPrices } from '../../../coin-gecko/api'
import BigNumber from 'bignumber.js'
import { Ticker } from '../../../types/ticker'

export const hasRuntimeBalance = (balances: RuntimeSdkBalance[] = []) =>
csillag marked this conversation as resolved.
Show resolved Hide resolved
balances.some(balance => balance.token_decimals)

export type FiatValueInfo = {
/**
* Do we have any known real value?
*/
hasValue: boolean

/**
* The fiat value, to the best of our information
*/
value?: string

/**
* The fiat currency used to express the value
*/
fiatCurrency: string

/**
* Have we used CoinGecko for calculating this value?
*/
hasUsedCoinGecko: boolean

/**
* Is the value of one of the tokens still being loaded?
*/
loading: boolean

/**
* Any tokens for which we don't know the value?
*/
unknownTickers: Ticker[]
}

export const calculateFiatValue = (
balances: RuntimeSdkBalance[] = [],
tokenPrices: AllTokenPrices,
fiatCurrency: string,
): FiatValueInfo => {
let hasValue = false
let value = new BigNumber(0)
let hasUsedCoinGecko = false
let loading = false
const unknown: Ticker[] = []

balances.forEach(balance => {
const priceInfo = tokenPrices[balance.token_symbol as Ticker]
if (priceInfo) {
if (priceInfo.isLoading) {
loading = true
} else {
hasUsedCoinGecko = hasUsedCoinGecko || priceInfo.hasUsedCoinGecko
if (!priceInfo.isFree) {
const tokenFiatValue = priceInfo.price
hasValue = true
if (tokenFiatValue === undefined) {
unknown.push(balance.token_symbol as Ticker)
} else {
value = value.plus(new BigNumber(balance.balance).multipliedBy(tokenFiatValue))
}
}
}
} else {
unknown.push(balance.token_symbol as Ticker)
hasValue = true
}
})

return {
hasValue,
hasUsedCoinGecko,
loading,
unknownTickers: unknown,
value: value.toFixed(),
fiatCurrency,
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { FC } from 'react'
import logo from '../../../../public/logo512.png'

export const SmallLogo: FC<{ size?: number }> = ({ size = 25 }) => (
export const SmallOasisLogo: FC<{ size?: number }> = ({ size = 25 }) => (
<img src={logo} height={size} width={size} alt="" />
)
15 changes: 15 additions & 0 deletions src/app/components/logo/SmallTokenLogo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { FC } from 'react'
import { Ticker } from '../../../types/ticker'
import { SmallOasisLogo } from './SmallOasisLogo'
import euroELogo from '../../../../public/euroe.png'

export const SmallTokenLogo: FC<{ ticker: Ticker }> = ({ ticker }) => {
switch (ticker) {
case 'ROSE':
return <SmallOasisLogo />
case 'EUROe':
return <img src={euroELogo} width={25} alt={ticker} />
default:
return null
}
}
8 changes: 4 additions & 4 deletions src/app/pages/AccountDetailsPage/AccountDetailsCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@ import { SubPageCard } from '../../components/SubPageCard'
import { useTranslation } from 'react-i18next'
import { EvmToken, RuntimeAccount } from '../../../oasis-nexus/api'
import { AccountDetailsView } from './AccountDetailsView'
import { TokenPriceInfo } from '../../../coin-gecko/api'
import { AllTokenPrices } from '../../../coin-gecko/api'

type AccountDetailsProps = {
isLoading: boolean
isError: boolean
isContract: boolean
account: RuntimeAccount | undefined
token: EvmToken | undefined
tokenPriceInfo: TokenPriceInfo
tokenPrices: AllTokenPrices
}

export const AccountDetailsCard: FC<AccountDetailsProps> = ({
Expand All @@ -20,7 +20,7 @@ export const AccountDetailsCard: FC<AccountDetailsProps> = ({
isContract,
account,
token,
tokenPriceInfo,
tokenPrices,
}) => {
const { t } = useTranslation()
return (
Expand All @@ -34,7 +34,7 @@ export const AccountDetailsCard: FC<AccountDetailsProps> = ({
isError={isError}
account={account}
token={token}
tokenPriceInfo={tokenPriceInfo}
tokenPrices={tokenPrices}
/>
</SubPageCard>
)
Expand Down
Loading
Loading