Skip to content

Commit

Permalink
Merge pull request #1265 from oasisprotocol/csillag/special-ticker-fo…
Browse files Browse the repository at this point in the history
…r-paratime

Add support for using different tokens on ParaTimes
  • Loading branch information
csillag committed Feb 21, 2024
2 parents c89f536 + 3520ce1 commit a69897d
Show file tree
Hide file tree
Showing 30 changed files with 456 additions and 210 deletions.
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[] = []) =>
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

0 comments on commit a69897d

Please sign in to comment.