Celo: show gas fees paid in ERC20 tokens (#2061)
* component for displaying tx fee * config for celo alfajores * display tx fee and gas price in celo token * adjustments for stability fee * clean up * update screenshots * change envs for demo * more screenshot updates * Removing unneeded network id var from review deploy * rollback envs for demo * fix currency symbol in tx table header * lol 🤦♂️ --------- Co-authored-by: Nick Zenchik <n.zenchik@gmail.com>
@@ -12,6 +12,7 @@ on:
|
||||
- none
|
||||
- arbitrum
|
||||
- base
|
||||
- celo_alfajores
|
||||
- gnosis
|
||||
- eth
|
||||
- eth_sepolia
|
||||
|
||||
@@ -339,7 +339,9 @@
|
||||
"main",
|
||||
"main.L2",
|
||||
"localhost",
|
||||
"arbitrum",
|
||||
"base",
|
||||
"celo_alfajores",
|
||||
"gnosis",
|
||||
"eth",
|
||||
"eth_goerli",
|
||||
|
||||
@@ -15,6 +15,7 @@ const chain = Object.freeze({
|
||||
secondaryCoin: {
|
||||
symbol: getEnvValue('NEXT_PUBLIC_NETWORK_SECONDARY_COIN_SYMBOL'),
|
||||
},
|
||||
hasMultipleGasCurrencies: getEnvValue('NEXT_PUBLIC_NETWORK_MULTIPLE_GAS_CURRENCIES') === 'true',
|
||||
tokenStandard: getEnvValue('NEXT_PUBLIC_NETWORK_TOKEN_STANDARD_NAME') || 'ERC',
|
||||
rpcUrl: getEnvValue('NEXT_PUBLIC_NETWORK_RPC_URL'),
|
||||
isTestnet: getEnvValue('NEXT_PUBLIC_IS_TESTNET') === 'true',
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
# Set of ENVs for Celo Alfajjores network explorer
|
||||
# https://celo-alfajores.blockscout.com/
|
||||
|
||||
# app configuration
|
||||
NEXT_PUBLIC_APP_PROTOCOL=http
|
||||
NEXT_PUBLIC_APP_HOST=localhost
|
||||
NEXT_PUBLIC_APP_PORT=3000
|
||||
|
||||
# blockchain parameters
|
||||
NEXT_PUBLIC_NETWORK_NAME=Celo Alfajores
|
||||
NEXT_PUBLIC_NETWORK_SHORT_NAME=Alfajores
|
||||
NEXT_PUBLIC_NETWORK_ID=44787
|
||||
NEXT_PUBLIC_NETWORK_CURRENCY_NAME=Celo
|
||||
NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL=CELO
|
||||
NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS=18
|
||||
NEXT_PUBLIC_NETWORK_MULTIPLE_GAS_CURRENCIES=true
|
||||
NEXT_PUBLIC_NETWORK_RPC_URL=https://alfajores-forno.celo-testnet.org
|
||||
NEXT_PUBLIC_NETWORK_VERIFICATION_TYPE=validation
|
||||
NEXT_PUBLIC_IS_TESTNET=true
|
||||
|
||||
# api configuration
|
||||
NEXT_PUBLIC_API_HOST=celo-alfajores.blockscout.com
|
||||
NEXT_PUBLIC_API_BASE_PATH=/
|
||||
|
||||
# ui config
|
||||
## homepage
|
||||
NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs']
|
||||
NEXT_PUBLIC_HOMEPAGE_PLATE_TEXT_COLOR=rgba(0,0,0,1)
|
||||
NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND=rgba(252,255,82,1)
|
||||
## sidebar
|
||||
NEXT_PUBLIC_NETWORK_LOGO=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-logos/celo-logo-light.svg
|
||||
NEXT_PUBLIC_NETWORK_LOGO_DARK=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-logos/celo-logo-dark.svg
|
||||
NEXT_PUBLIC_NETWORK_ICON=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-icons/celo-icon-light.svg
|
||||
NEXT_PUBLIC_NETWORK_ICON_DARK=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-icons/celo-icon-dark.svg
|
||||
## footer
|
||||
##views
|
||||
NEXT_PUBLIC_VIEWS_NFT_MARKETPLACES=[{'name':'LooksRare','collection_url':'https://goerli.looksrare.org/collections/{hash}','instance_url':'https://goerli.looksrare.org/collections/{hash}/{id}','logo_url':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/nft-marketplace-logos/looks-rare.png'}]
|
||||
## misc
|
||||
NEXT_PUBLIC_NETWORK_EXPLORERS=[{'title':'Bitquery','baseUrl':'https://explorer.bitquery.io/','paths':{'tx':'/goerli/tx','address':'/goerli/address','token':'/goerli/token','block':'/goerli/block'}},{'title':'Etherscan','baseUrl':'https://goerli.etherscan.io/','paths':{'tx':'/tx','address':'/address','token':'/token','block':'/block'}}]
|
||||
## views
|
||||
NEXT_PUBLIC_CONTRACT_CODE_IDES=[{'title':'Remix IDE','url':'https://remix.ethereum.org/?address={hash}&blockscout={domain}','icon_url':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/ide-icons/remix.png'}]
|
||||
NEXT_PUBLIC_VIEWS_CONTRACT_SOLIDITYSCAN_ENABLED=true
|
||||
|
||||
# app features
|
||||
NEXT_PUBLIC_APP_ENV=development
|
||||
NEXT_PUBLIC_GRAPHIQL_TRANSACTION=0xf7d4972356e6ae44ae948d0cf19ef2beaf0e574c180997e969a2837da15e349d
|
||||
NEXT_PUBLIC_API_SPEC_URL=https://raw.githubusercontent.com/blockscout/blockscout-api-v2-swagger/main/swagger.yaml
|
||||
NEXT_PUBLIC_GAS_TRACKER_ENABLED=false
|
||||
NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED=true
|
||||
NEXT_PUBLIC_AUTH_URL=http://localhost:3000
|
||||
NEXT_PUBLIC_LOGOUT_URL=https://blockscoutcom.us.auth0.com/v2/logout
|
||||
NEXT_PUBLIC_MARKETPLACE_ENABLED=true
|
||||
NEXT_PUBLIC_MARKETPLACE_CONFIG_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/dev/configs/marketplace/eth-goerli.json
|
||||
NEXT_PUBLIC_MARKETPLACE_SECURITY_REPORTS_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/marketplace-security-reports/default.json
|
||||
NEXT_PUBLIC_MARKETPLACE_CATEGORIES_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/marketplace-categories/default.json
|
||||
NEXT_PUBLIC_MARKETPLACE_SUGGEST_IDEAS_FORM=https://airtable.com/appiy5yijZpMMSKjT/pag3t82DUCyhGRZZO/form
|
||||
NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM=https://airtable.com/appiy5yijZpMMSKjT/shr6uMGPKjj1DK7NL
|
||||
NEXT_PUBLIC_TRANSACTION_INTERPRETATION_PROVIDER=blockscout
|
||||
NEXT_PUBLIC_METADATA_SERVICE_API_HOST=https://metadata.services.blockscout.com
|
||||
# NEXT_PUBLIC_STATS_API_HOST=https://stats-goerli.k8s-dev.blockscout.com
|
||||
NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.k8s-dev.blockscout.com
|
||||
NEXT_PUBLIC_CONTRACT_INFO_API_HOST=https://contracts-info-test.k8s-dev.blockscout.com
|
||||
NEXT_PUBLIC_ADMIN_SERVICE_API_HOST=https://admin-rs-test.k8s-dev.blockscout.com
|
||||
NEXT_PUBLIC_OG_IMAGE_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/og-images/celo.png
|
||||
|
||||
|
||||
@@ -491,6 +491,7 @@ const schema = yup
|
||||
NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL: yup.string(),
|
||||
NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS: yup.number().integer().positive(),
|
||||
NEXT_PUBLIC_NETWORK_SECONDARY_COIN_SYMBOL: yup.string(),
|
||||
NEXT_PUBLIC_NETWORK_MULTIPLE_GAS_CURRENCIES: yup.boolean(),
|
||||
NEXT_PUBLIC_NETWORK_VERIFICATION_TYPE: yup.string<NetworkVerificationType>().oneOf([ 'validation', 'mining' ]),
|
||||
NEXT_PUBLIC_NETWORK_TOKEN_STANDARD_NAME: yup.string(),
|
||||
NEXT_PUBLIC_IS_TESTNET: yup.boolean(),
|
||||
|
||||
@@ -47,6 +47,7 @@ NEXT_PUBLIC_NETWORK_CURRENCY_NAME=Ether
|
||||
NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL=ETH
|
||||
NEXT_PUBLIC_NETWORK_EXPLORERS=[{'title':'Explorer','baseUrl':'https://example.com/','paths':{'tx':'/tx','address':'/address','token':'/token','block':'/block'}}]
|
||||
NEXT_PUBLIC_NETWORK_SECONDARY_COIN_SYMBOL=GNO
|
||||
NEXT_PUBLIC_NETWORK_MULTIPLE_GAS_CURRENCIES=true
|
||||
NEXT_PUBLIC_NETWORK_ICON=https://example.com/icon.png
|
||||
NEXT_PUBLIC_NETWORK_ICON_DARK=https://example.com/icon.png
|
||||
NEXT_PUBLIC_NETWORK_LOGO=https://example.com/logo.png
|
||||
|
||||
@@ -68,7 +68,6 @@ frontend:
|
||||
NEXT_PUBLIC_LOGOUT_URL: https://blockscoutcom.us.auth0.com/v2/logout
|
||||
NEXT_PUBLIC_HOMEPAGE_CHARTS: "['daily_txs','coin_price','market_cap']"
|
||||
NEXT_PUBLIC_NETWORK_RPC_URL: https://eth-sepolia.public.blastapi.io
|
||||
NEXT_PUBLIC_NETWORK_ID: '11155111'
|
||||
NEXT_PUBLIC_NETWORK_EXPLORERS: "[{'title':'Bitquery','baseUrl':'https://explorer.bitquery.io/','paths':{'tx':'/goerli/tx','address':'/goerli/address','token':'/goerli/token','block':'/goerli/block'}},{'title':'Etherscan','logo':'https://github.com/blockscout/frontend-configs/blob/main/configs/explorer-logos/etherscan.png?raw=true','baseUrl':'https://goerli.etherscan.io/','paths':{'tx':'/tx','address':'/address','token':'/token','block':'/block'}}]"
|
||||
NEXT_PUBLIC_MARKETPLACE_CATEGORIES_URL: https://raw.githubusercontent.com/blockscout/frontend-configs/dev/configs/marketplace-categories/default.json
|
||||
NEXT_PUBLIC_MARKETPLACE_FEATURED_APP: zkbob-wallet
|
||||
@@ -77,10 +76,7 @@ frontend:
|
||||
NEXT_PUBLIC_GRAPHIQL_TRANSACTION: 0xf7d4972356e6ae44ae948d0cf19ef2beaf0e574c180997e969a2837da15e349d
|
||||
NEXT_PUBLIC_WEB3_WALLETS: "['token_pocket','coinbase','metamask']"
|
||||
NEXT_PUBLIC_VIEWS_ADDRESS_IDENTICON_TYPE: gradient_avatar
|
||||
NEXT_PUBLIC_VIEWS_ADDRESS_HIDDEN_VIEWS: "['top_accounts']"
|
||||
NEXT_PUBLIC_VIEWS_CONTRACT_SOLIDITYSCAN_ENABLED: true
|
||||
NEXT_PUBLIC_VIEWS_TX_HIDDEN_FIELDS: "['value','fee_currency','gas_price','gas_fees','burnt_fees']"
|
||||
NEXT_PUBLIC_VIEWS_TX_ADDITIONAL_FIELDS: "['fee_per_gas']"
|
||||
NEXT_PUBLIC_USE_NEXT_JS_PROXY: true
|
||||
NEXT_PUBLIC_VIEWS_NFT_MARKETPLACES: "[{'name':'LooksRare','collection_url':'https://goerli.looksrare.org/collections/{hash}','instance_url':'https://goerli.looksrare.org/collections/{hash}/{id}','logo_url':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/nft-marketplace-logos/looks-rare.png'}]"
|
||||
NEXT_PUBLIC_HAS_USER_OPS: true
|
||||
@@ -89,7 +85,6 @@ frontend:
|
||||
NEXT_PUBLIC_HAS_CONTRACT_AUDIT_REPORTS: true
|
||||
NEXT_PUBLIC_AD_BANNER_PROVIDER: slise
|
||||
NEXT_PUBLIC_DATA_AVAILABILITY_ENABLED: true
|
||||
NEXT_PUBLIC_OG_ENHANCED_DATA_ENABLED: true
|
||||
NEXT_PUBLIC_NAVIGATION_HIGHLIGHTED_ROUTES: "['/apps']"
|
||||
envFromSecret:
|
||||
NEXT_PUBLIC_SENTRY_DSN: ref+vault://deployment-values/blockscout/dev/review?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_SENTRY_DSN
|
||||
|
||||
@@ -91,6 +91,7 @@ Please be aware that all environment variables prefixed with `NEXT_PUBLIC_` will
|
||||
| NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL | `string` | Network currency symbol | - | - | `ETH` | v1.0.x+ |
|
||||
| NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS | `string` | Network currency decimals | - | `18` | `6` | v1.0.x+ |
|
||||
| NEXT_PUBLIC_NETWORK_SECONDARY_COIN_SYMBOL | `string` | Network secondary coin symbol. | - | - | `GNO` | v1.29.0+ |
|
||||
| NEXT_PUBLIC_NETWORK_MULTIPLE_GAS_CURRENCIES | `boolean` | Set to `true` for networks where users can pay transaction fees in either the native coin or ERC-20 tokens. | - | `false` | `true` | v1.33.0+ |
|
||||
| NEXT_PUBLIC_NETWORK_VERIFICATION_TYPE | `validation` or `mining` | Verification type in the network | - | `mining` | `validation` | v1.0.x+ |
|
||||
| NEXT_PUBLIC_NETWORK_TOKEN_STANDARD_NAME | `string` | Name of the standard for creating tokens | - | `ERC` | `BEP` | v1.31.0+ |
|
||||
| NEXT_PUBLIC_IS_TESTNET | `boolean`| Set to true if network is testnet | - | `false` | `true` | v1.0.x+ |
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<svg viewBox="0 0 21 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M17.139 20H4.83a3.847 3.847 0 0 1-3.846-3.845V5.39a.769.769 0 0 1 .77-.769h15.384a3.847 3.847 0 0 1 3.846 3.845v7.69A3.844 3.844 0 0 1 17.139 20ZM2.524 6.16v9.996a2.307 2.307 0 0 0 2.307 2.307H17.14a2.308 2.308 0 0 0 2.308-2.307v-7.69a2.306 2.306 0 0 0-2.308-2.306H2.524Z" fill="currentColor"/>
|
||||
<path d="M18.677 6.159a.77.77 0 0 1-.769-.77V3.276a1.79 1.79 0 0 0-.6-1.438 1.493 1.493 0 0 0-1.284-.246l-12.9 2.93a.77.77 0 0 0-.6.769.769.769 0 0 1-1.539 0A2.306 2.306 0 0 1 2.778 3.02L15.685.091a3 3 0 0 1 2.585.546 3.338 3.338 0 0 1 1.177 2.638V5.39a.77.77 0 0 1-.77.769ZM20.216 15.386H14.83a3.077 3.077 0 0 1-2.175-5.25c.577-.578 1.36-.902 2.175-.902h5.385a.77.77 0 0 1 .769.77v4.613a.77.77 0 0 1-.77.769Zm-5.385-4.614a1.539 1.539 0 1 0 0 3.076h4.616v-3.076H14.83Z" fill="currentColor"/>
|
||||
<path d="M17.139 20H4.83a3.847 3.847 0 0 1-3.846-3.845V5.39a.769.769 0 0 1 .77-.769h15.384a3.847 3.847 0 0 1 3.846 3.845v7.69A3.844 3.844 0 0 1 17.139 20ZM2.524 6.16v9.996a2.307 2.307 0 0 0 2.307 2.307H17.14a2.308 2.308 0 0 0 2.308-2.307v-7.69A2.306 2.306 0 0 0 17.14 6.16H2.524Z" fill="currentColor"/>
|
||||
<path d="M18.677 6.159a.77.77 0 0 1-.769-.77V3.276a1.79 1.79 0 0 0-.6-1.438 1.493 1.493 0 0 0-1.284-.246l-12.9 2.93a.77.77 0 0 0-.6.769.769.769 0 0 1-1.539 0A2.306 2.306 0 0 1 2.778 3.02L15.685.091a3 3 0 0 1 2.585.546 3.338 3.338 0 0 1 1.177 2.638V5.39a.77.77 0 0 1-.77.769Zm1.539 9.227H14.83a3.077 3.077 0 0 1-2.175-5.25 3.074 3.074 0 0 1 2.175-.902h5.385a.77.77 0 0 1 .769.77v4.613a.77.77 0 0 1-.77.769Zm-5.385-4.614a1.539 1.539 0 1 0 0 3.076h4.616v-3.076H14.83Z" fill="currentColor"/>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 880 B After Width: | Height: | Size: 875 B |
@@ -299,7 +299,7 @@ export const stabilityTx: Transaction = {
|
||||
decimals: '18',
|
||||
exchange_rate: '123.567',
|
||||
holders: '92',
|
||||
icon_url: null,
|
||||
icon_url: 'https://example.com/icon.png',
|
||||
name: 'Stability Gas',
|
||||
symbol: 'GAS',
|
||||
total_supply: '10000000000000000000000000',
|
||||
@@ -321,6 +321,24 @@ export const stabilityTx: Transaction = {
|
||||
},
|
||||
};
|
||||
|
||||
export const celoTxn: Transaction = {
|
||||
...base,
|
||||
celo: {
|
||||
gas_token: {
|
||||
address: '0x874069Fa1Eb16D44d622F2e0Ca25eeA172369bC1',
|
||||
circulating_market_cap: null,
|
||||
decimals: '18',
|
||||
exchange_rate: '0.42',
|
||||
holders: '205738',
|
||||
icon_url: 'https://example.com/icon.png',
|
||||
name: 'Celo Dollar',
|
||||
symbol: 'cUSD',
|
||||
total_supply: '7145754483836626799435133',
|
||||
type: 'ERC-20',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const base2 = {
|
||||
...base,
|
||||
hash: '0x02d597ebcf3e8d60096dd0363bc2f0f5e2df27ba1dacd696c51aa7c9409f3193',
|
||||
|
||||
@@ -77,6 +77,10 @@ export type Transaction = {
|
||||
validator_address: AddressParam;
|
||||
validator_fee: string;
|
||||
};
|
||||
// Celo fields
|
||||
celo?: {
|
||||
gas_token: TokenInfo<'ERC-20'> | null;
|
||||
};
|
||||
// zkEvm fields
|
||||
zkevm_verify_hash?: string;
|
||||
zkevm_batch_number?: number;
|
||||
|
||||
@@ -17,7 +17,7 @@ import { currencyUnits } from 'lib/units';
|
||||
import AddressFromTo from 'ui/shared/address/AddressFromTo';
|
||||
import TxEntity from 'ui/shared/entities/tx/TxEntity';
|
||||
import TxStatus from 'ui/shared/statusTag/TxStatus';
|
||||
import TxFeeStability from 'ui/shared/tx/TxFeeStability';
|
||||
import TxFee from 'ui/shared/tx/TxFee';
|
||||
import TxWatchListTags from 'ui/shared/tx/TxWatchListTags';
|
||||
import TxAdditionalInfo from 'ui/txs/TxAdditionalInfo';
|
||||
import TxType from 'ui/txs/TxType';
|
||||
@@ -96,13 +96,7 @@ const LatestTxsItem = ({ tx, isLoading }: Props) => {
|
||||
{ !config.UI.views.tx.hiddenFields?.tx_fee && (
|
||||
<Skeleton isLoaded={ !isLoading } display="flex" whiteSpace="pre" my="3px">
|
||||
<Text as="span">Fee </Text>
|
||||
{ tx.stability_fee ? (
|
||||
<TxFeeStability data={ tx.stability_fee } accuracy={ 5 } color="text_secondary" hideUsd/>
|
||||
) : (
|
||||
<Text as="span" variant="secondary">
|
||||
{ tx.fee.value ? `${ getValueWithUnit(tx.fee.value).dp(5).toFormat() } ${ currencyUnits.ether }` : '-' }
|
||||
</Text>
|
||||
) }
|
||||
<TxFee tx={ tx } accuracy={ 5 } color="text_secondary"/>
|
||||
</Skeleton>
|
||||
) }
|
||||
</Flex>
|
||||
|
||||
@@ -16,7 +16,7 @@ import { currencyUnits } from 'lib/units';
|
||||
import AddressFromTo from 'ui/shared/address/AddressFromTo';
|
||||
import TxEntity from 'ui/shared/entities/tx/TxEntity';
|
||||
import TxStatus from 'ui/shared/statusTag/TxStatus';
|
||||
import TxFeeStability from 'ui/shared/tx/TxFeeStability';
|
||||
import TxFee from 'ui/shared/tx/TxFee';
|
||||
import TxWatchListTags from 'ui/shared/tx/TxWatchListTags';
|
||||
import TxAdditionalInfo from 'ui/txs/TxAdditionalInfo';
|
||||
import TxType from 'ui/txs/TxType';
|
||||
@@ -76,18 +76,14 @@ const LatestTxsItem = ({ tx, isLoading }: Props) => {
|
||||
/>
|
||||
{ !config.UI.views.tx.hiddenFields?.value && (
|
||||
<Skeleton isLoaded={ !isLoading } mb={ 2 } fontSize="sm" w="fit-content">
|
||||
<Text as="span">Value { currencyUnits.ether } </Text>
|
||||
<Text as="span" variant="secondary">{ getValueWithUnit(tx.value).dp(5).toFormat() }</Text>
|
||||
<Text as="span">Value </Text>
|
||||
<Text as="span" variant="secondary">{ getValueWithUnit(tx.value).dp(5).toFormat() } { currencyUnits.ether }</Text>
|
||||
</Skeleton>
|
||||
) }
|
||||
{ !config.UI.views.tx.hiddenFields?.tx_fee && (
|
||||
<Skeleton isLoaded={ !isLoading } fontSize="sm" w="fit-content" display="flex" whiteSpace="pre">
|
||||
<Text as="span">Fee { !config.UI.views.tx.hiddenFields?.fee_currency ? `${ currencyUnits.ether } ` : '' }</Text>
|
||||
{ tx.stability_fee ? (
|
||||
<TxFeeStability data={ tx.stability_fee } accuracy={ 5 } color="text_secondary" hideUsd/>
|
||||
) : (
|
||||
<Text as="span" variant="secondary">{ tx.fee.value ? getValueWithUnit(tx.fee.value).dp(5).toFormat() : '-' }</Text>
|
||||
) }
|
||||
<Text as="span">Fee </Text>
|
||||
<TxFee tx={ tx } accuracy={ 5 } color="text_secondary"/>
|
||||
</Skeleton>
|
||||
) }
|
||||
</Box>
|
||||
|
||||
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 54 KiB |
|
Before Width: | Height: | Size: 115 KiB After Width: | Height: | Size: 115 KiB |
|
Before Width: | Height: | Size: 65 KiB After Width: | Height: | Size: 65 KiB |
@@ -1,4 +1,4 @@
|
||||
import { Box, Text, chakra, Skeleton } from '@chakra-ui/react';
|
||||
import { chakra, Skeleton } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
|
||||
import getCurrencyValue from 'lib/getCurrencyValue';
|
||||
@@ -23,20 +23,20 @@ const CurrencyValue = ({ value, currency = '', decimals, exchangeRate, className
|
||||
|
||||
if (value === undefined || value === null) {
|
||||
return (
|
||||
<Box as="span" className={ className }>
|
||||
<Text>N/A</Text>
|
||||
</Box>
|
||||
<chakra.span className={ className }>
|
||||
-
|
||||
</chakra.span>
|
||||
);
|
||||
}
|
||||
const { valueStr: valueResult, usd: usdResult } = getCurrencyValue({ value, accuracy, accuracyUsd, exchangeRate, decimals });
|
||||
|
||||
return (
|
||||
<Box as="span" className={ className } display="inline-flex" rowGap={ 3 } columnGap={ 1 }>
|
||||
<Text display="inline-block">
|
||||
<chakra.span className={ className } display="inline-flex" rowGap={ 3 } columnGap={ 1 }>
|
||||
<chakra.span display="inline-block">
|
||||
{ valueResult }{ currency ? ` ${ currency }` : '' }
|
||||
</Text>
|
||||
{ usdResult && <Text as="span" variant="secondary" fontWeight={ 400 }>(${ usdResult })</Text> }
|
||||
</Box>
|
||||
</chakra.span>
|
||||
{ usdResult && <chakra.span color="text_secondary" fontWeight={ 400 }>(${ usdResult })</chakra.span> }
|
||||
</chakra.span>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Box, DarkMode, Popover, PopoverBody, PopoverContent, PopoverTrigger, Portal, useColorModeValue, Flex, PopoverArrow } from '@chakra-ui/react';
|
||||
import { Box, DarkMode, PopoverBody, PopoverContent, PopoverTrigger, Portal, useColorModeValue, Flex, PopoverArrow } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
|
||||
import Popover from 'ui/shared/chakra/Popover';
|
||||
import * as EntityBase from 'ui/shared/entities/base/components';
|
||||
|
||||
import type { ContentProps } from './AddressEntity';
|
||||
@@ -23,7 +24,7 @@ const AddressEntityContentProxy = (props: ContentProps) => {
|
||||
const implementationName = implementations.length === 1 && implementations[0].name ? implementations[0].name : undefined;
|
||||
|
||||
return (
|
||||
<Popover trigger="hover" isLazy>
|
||||
<Popover trigger="hover" isLazy gutter={ 8 }>
|
||||
<PopoverTrigger>
|
||||
<Box display="inline-flex" w="100%">
|
||||
<EntityBase.Content
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
import React from 'react';
|
||||
|
||||
import * as txMock from 'mocks/txs/tx';
|
||||
import { test, expect } from 'playwright/lib';
|
||||
|
||||
import TxFee from './TxFee';
|
||||
|
||||
test.use({ viewport: { width: 300, height: 100 } });
|
||||
|
||||
test('base view', async({ render }) => {
|
||||
const component = await render(<TxFee tx={ txMock.base } withUsd/>);
|
||||
await expect(component).toHaveScreenshot();
|
||||
});
|
||||
|
||||
test('no usd value', async({ render }) => {
|
||||
const component = await render(<TxFee tx={ txMock.base } accuracy={ 3 }/>);
|
||||
await expect(component).toHaveScreenshot();
|
||||
});
|
||||
|
||||
test('celo gas token', async({ render, mockAssetResponse }) => {
|
||||
await mockAssetResponse(txMock.celoTxn.celo?.gas_token?.icon_url as string, './playwright/mocks/image_svg.svg');
|
||||
const component = await render(<TxFee tx={ txMock.celoTxn } withUsd accuracyUsd={ 3 }/>);
|
||||
await expect(component).toHaveScreenshot();
|
||||
});
|
||||
|
||||
test('stability token', async({ render, mockAssetResponse }) => {
|
||||
await mockAssetResponse(txMock.stabilityTx.stability_fee?.token.icon_url as string, './playwright/mocks/image_svg.svg');
|
||||
const component = await render(<TxFee tx={ txMock.stabilityTx } withUsd accuracyUsd={ 3 }/>);
|
||||
await expect(component).toHaveScreenshot();
|
||||
});
|
||||
@@ -0,0 +1,77 @@
|
||||
import { chakra, Skeleton } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
|
||||
import type { Transaction } from 'types/api/transaction';
|
||||
|
||||
import config from 'configs/app';
|
||||
import getCurrencyValue from 'lib/getCurrencyValue';
|
||||
import { currencyUnits } from 'lib/units';
|
||||
import CurrencyValue from 'ui/shared/CurrencyValue';
|
||||
import TokenEntity from 'ui/shared/entities/token/TokenEntity';
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
isLoading?: boolean;
|
||||
tx: Transaction;
|
||||
withCurrency?: boolean;
|
||||
withUsd?: boolean;
|
||||
accuracy?: number;
|
||||
accuracyUsd?: number;
|
||||
}
|
||||
|
||||
const TxFee = ({ className, tx, accuracy, accuracyUsd, isLoading, withCurrency = true, withUsd }: Props) => {
|
||||
|
||||
if (tx.celo?.gas_token) {
|
||||
const token = tx.celo.gas_token;
|
||||
const { valueStr, usd } = getCurrencyValue({
|
||||
value: tx.fee.value || '0',
|
||||
exchangeRate: token.exchange_rate,
|
||||
decimals: token.decimals,
|
||||
accuracy,
|
||||
accuracyUsd,
|
||||
});
|
||||
return (
|
||||
<Skeleton whiteSpace="pre-wrap" wordBreak="break-word" isLoaded={ !isLoading } display="flex" flexWrap="wrap" className={ className }>
|
||||
<span>{ valueStr } </span>
|
||||
<TokenEntity token={ token } noCopy onlySymbol w="auto" ml={ 1 }/>
|
||||
{ usd && withUsd && <chakra.span color="text_secondary"> (${ usd })</chakra.span> }
|
||||
</Skeleton>
|
||||
);
|
||||
}
|
||||
|
||||
if (tx.stability_fee) {
|
||||
const token = tx.stability_fee.token;
|
||||
const { valueStr, usd } = getCurrencyValue({
|
||||
value: tx.stability_fee.total_fee,
|
||||
exchangeRate: token.exchange_rate,
|
||||
decimals: token.decimals,
|
||||
accuracy,
|
||||
accuracyUsd,
|
||||
});
|
||||
|
||||
return (
|
||||
<Skeleton whiteSpace="pre" isLoaded={ !isLoading } display="flex" className={ className }>
|
||||
<span>{ valueStr } </span>
|
||||
{ valueStr !== '0' && <TokenEntity token={ token } noCopy onlySymbol w="auto" ml={ 1 }/> }
|
||||
{ usd && withUsd && <chakra.span color="text_secondary"> (${ usd })</chakra.span> }
|
||||
</Skeleton>
|
||||
);
|
||||
}
|
||||
|
||||
const showCurrency = withCurrency && !config.UI.views.tx.hiddenFields?.fee_currency;
|
||||
|
||||
return (
|
||||
<CurrencyValue
|
||||
value={ tx.fee.value }
|
||||
currency={ showCurrency ? currencyUnits.ether : '' }
|
||||
exchangeRate={ withUsd ? tx.exchange_rate : null }
|
||||
accuracy={ accuracy }
|
||||
accuracyUsd={ accuracyUsd }
|
||||
flexWrap="wrap"
|
||||
className={ className }
|
||||
isLoading={ isLoading }
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(chakra(TxFee));
|
||||
@@ -1,36 +0,0 @@
|
||||
import { Skeleton, chakra } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
|
||||
import type { Transaction } from 'types/api/transaction';
|
||||
import type { ExcludeUndefined } from 'types/utils';
|
||||
|
||||
import getCurrencyValue from 'lib/getCurrencyValue';
|
||||
import TokenEntity from 'ui/shared/entities/token/TokenEntity';
|
||||
|
||||
interface Props {
|
||||
data: ExcludeUndefined<Transaction['stability_fee']>;
|
||||
isLoading?: boolean;
|
||||
hideUsd?: boolean;
|
||||
accuracy?: number;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const TxFeeStability = ({ data, isLoading, hideUsd, accuracy, className }: Props) => {
|
||||
|
||||
const { valueStr, usd } = getCurrencyValue({
|
||||
value: data.total_fee,
|
||||
exchangeRate: data.token.exchange_rate,
|
||||
decimals: data.token.decimals,
|
||||
accuracy,
|
||||
});
|
||||
|
||||
return (
|
||||
<Skeleton whiteSpace="pre" isLoaded={ !isLoading } display="flex" className={ className }>
|
||||
<span>{ valueStr } </span>
|
||||
{ valueStr !== '0' && <TokenEntity token={ data.token } noCopy onlySymbol w="auto" ml={ 1 }/> }
|
||||
{ usd && !hideUsd && <chakra.span color="text_secondary"> (${ usd })</chakra.span> }
|
||||
</Skeleton>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(chakra(TxFeeStability));
|
||||
|
After Width: | Height: | Size: 4.9 KiB |
|
After Width: | Height: | Size: 4.6 KiB |
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 4.7 KiB |
@@ -14,6 +14,7 @@ import AddressEntity from 'ui/shared/entities/address/AddressEntity';
|
||||
import TxEntity from 'ui/shared/entities/tx/TxEntity';
|
||||
import LogDecodedInputData from 'ui/shared/logs/LogDecodedInputData';
|
||||
import RawInputData from 'ui/shared/RawInputData';
|
||||
import TxFee from 'ui/shared/tx/TxFee';
|
||||
import TxDetailsGasPrice from 'ui/tx/details/TxDetailsGasPrice';
|
||||
import TxDetailsOther from 'ui/tx/details/TxDetailsOther';
|
||||
|
||||
@@ -80,11 +81,7 @@ const TxDetailsWrapped = ({ data }: Props) => {
|
||||
Transaction fee
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<CurrencyValue
|
||||
value={ data.fee.value }
|
||||
currency={ currencyUnits.ether }
|
||||
flexWrap="wrap"
|
||||
/>
|
||||
<TxFee tx={ data } withUsd/>
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
) }
|
||||
|
||||
@@ -2,21 +2,47 @@ import { Skeleton } from '@chakra-ui/react';
|
||||
import BigNumber from 'bignumber.js';
|
||||
import React from 'react';
|
||||
|
||||
import type { TokenInfo } from 'types/api/token';
|
||||
|
||||
import config from 'configs/app';
|
||||
import { WEI, WEI_IN_GWEI } from 'lib/consts';
|
||||
import { currencyUnits } from 'lib/units';
|
||||
import * as DetailsInfoItem from 'ui/shared/DetailsInfoItem';
|
||||
import TokenEntity from 'ui/shared/entities/token/TokenEntity';
|
||||
|
||||
interface Props {
|
||||
gasToken?: TokenInfo<'ERC-20'> | null;
|
||||
gasPrice: string | null;
|
||||
isLoading?: boolean;
|
||||
}
|
||||
|
||||
const TxDetailsGasPrice = ({ gasPrice, isLoading }: Props) => {
|
||||
const TxDetailsGasPrice = ({ gasPrice, gasToken, isLoading }: Props) => {
|
||||
if (config.UI.views.tx.hiddenFields?.gas_price || !gasPrice) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const content = (() => {
|
||||
if (gasToken) {
|
||||
return (
|
||||
<Skeleton isLoaded={ !isLoading } display="flex">
|
||||
<span>{ BigNumber(gasPrice).dividedBy(WEI).toFixed() }</span>
|
||||
<TokenEntity token={ gasToken } noCopy onlySymbol w="auto" ml={ 1 }/>
|
||||
</Skeleton>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Skeleton isLoaded={ !isLoading } mr={ 1 }>
|
||||
{ BigNumber(gasPrice).dividedBy(WEI).toFixed() } { currencyUnits.ether }
|
||||
</Skeleton>
|
||||
<Skeleton isLoaded={ !isLoading } color="text_secondary">
|
||||
<span>({ BigNumber(gasPrice).dividedBy(WEI_IN_GWEI).toFixed() } { currencyUnits.gwei })</span>
|
||||
</Skeleton>
|
||||
</>
|
||||
);
|
||||
})();
|
||||
|
||||
return (
|
||||
<>
|
||||
<DetailsInfoItem.Label
|
||||
@@ -26,12 +52,7 @@ const TxDetailsGasPrice = ({ gasPrice, isLoading }: Props) => {
|
||||
Gas price
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
<Skeleton isLoaded={ !isLoading } mr={ 1 }>
|
||||
{ BigNumber(gasPrice).dividedBy(WEI).toFixed() } { currencyUnits.ether }
|
||||
</Skeleton>
|
||||
<Skeleton isLoaded={ !isLoading } color="text_secondary">
|
||||
<span>({ BigNumber(gasPrice).dividedBy(WEI_IN_GWEI).toFixed() } { currencyUnits.gwei })</span>
|
||||
</Skeleton>
|
||||
{ content }
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -46,7 +46,7 @@ import RawInputData from 'ui/shared/RawInputData';
|
||||
import StatusTag from 'ui/shared/statusTag/StatusTag';
|
||||
import TxStatus from 'ui/shared/statusTag/TxStatus';
|
||||
import TextSeparator from 'ui/shared/TextSeparator';
|
||||
import TxFeeStability from 'ui/shared/tx/TxFeeStability';
|
||||
import TxFee from 'ui/shared/tx/TxFee';
|
||||
import Utilization from 'ui/shared/Utilization/Utilization';
|
||||
import VerificationSteps from 'ui/shared/verificationSteps/VerificationSteps';
|
||||
import TxDetailsActions from 'ui/tx/details/txDetailsActions/TxDetailsActions';
|
||||
@@ -555,17 +555,7 @@ const TxInfo = ({ data, isLoading, socketStatus }: Props) => {
|
||||
Transaction fee
|
||||
</DetailsInfoItem.Label>
|
||||
<DetailsInfoItem.Value>
|
||||
{ data.stability_fee ? (
|
||||
<TxFeeStability data={ data.stability_fee } isLoading={ isLoading }/>
|
||||
) : (
|
||||
<CurrencyValue
|
||||
value={ data.fee.value }
|
||||
currency={ config.UI.views.tx.hiddenFields?.fee_currency ? '' : currencyUnits.ether }
|
||||
exchangeRate={ data.exchange_rate }
|
||||
flexWrap="wrap"
|
||||
isLoading={ isLoading }
|
||||
/>
|
||||
) }
|
||||
<TxFee tx={ data } isLoading={ isLoading } withUsd/>
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
) }
|
||||
@@ -606,7 +596,7 @@ const TxInfo = ({ data, isLoading, socketStatus }: Props) => {
|
||||
</>
|
||||
) }
|
||||
|
||||
<TxDetailsGasPrice gasPrice={ data.gas_price } isLoading={ isLoading }/>
|
||||
<TxDetailsGasPrice gasPrice={ data.gas_price } gasToken={ data.celo?.gas_token } isLoading={ isLoading }/>
|
||||
|
||||
<TxDetailsFeePerGas txFee={ data.fee.value } gasUsed={ data.gas_used } isLoading={ isLoading }/>
|
||||
|
||||
|
||||
@@ -9,11 +9,10 @@ import { route } from 'nextjs-routes';
|
||||
import config from 'configs/app';
|
||||
import getValueWithUnit from 'lib/getValueWithUnit';
|
||||
import { currencyUnits } from 'lib/units';
|
||||
import CurrencyValue from 'ui/shared/CurrencyValue';
|
||||
import BlobEntity from 'ui/shared/entities/blob/BlobEntity';
|
||||
import LinkInternal from 'ui/shared/links/LinkInternal';
|
||||
import TextSeparator from 'ui/shared/TextSeparator';
|
||||
import TxFeeStability from 'ui/shared/tx/TxFeeStability';
|
||||
import TxFee from 'ui/shared/tx/TxFee';
|
||||
import Utilization from 'ui/shared/Utilization/Utilization';
|
||||
|
||||
const TxAdditionalInfoContent = ({ tx }: { tx: Transaction }) => {
|
||||
@@ -60,20 +59,7 @@ const TxAdditionalInfoContent = ({ tx }: { tx: Transaction }) => {
|
||||
{ (tx.stability_fee !== undefined || tx.fee.value !== null) && (
|
||||
<>
|
||||
<Text { ...sectionTitleProps }>Transaction fee</Text>
|
||||
{ tx.stability_fee ? (
|
||||
<TxFeeStability data={ tx.stability_fee }/>
|
||||
) : (
|
||||
<Flex>
|
||||
<CurrencyValue
|
||||
value={ tx.fee.value }
|
||||
currency={ config.UI.views.tx.hiddenFields?.fee_currency ? '' : currencyUnits.ether }
|
||||
exchangeRate={ tx.exchange_rate }
|
||||
accuracyUsd={ 2 }
|
||||
flexWrap="wrap"
|
||||
rowGap={ 0 }
|
||||
/>
|
||||
</Flex>
|
||||
) }
|
||||
<TxFee tx={ tx } withUsd accuracyUsd={ 2 } rowGap={ 0 }/>
|
||||
</>
|
||||
) }
|
||||
</Box>
|
||||
|
||||
@@ -17,7 +17,7 @@ import BlockEntity from 'ui/shared/entities/block/BlockEntity';
|
||||
import TxEntity from 'ui/shared/entities/tx/TxEntity';
|
||||
import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile';
|
||||
import TxStatus from 'ui/shared/statusTag/TxStatus';
|
||||
import TxFeeStability from 'ui/shared/tx/TxFeeStability';
|
||||
import TxFee from 'ui/shared/tx/TxFee';
|
||||
import TxWatchListTags from 'ui/shared/tx/TxWatchListTags';
|
||||
import TxAdditionalInfo from 'ui/txs/TxAdditionalInfo';
|
||||
import TxType from 'ui/txs/TxType';
|
||||
@@ -111,14 +111,7 @@ const TxsListItem = ({ tx, isLoading, showBlockInfo, currentAddress, enableTimeI
|
||||
{ (tx.stability_fee !== undefined || tx.fee.value !== null) && (
|
||||
<>
|
||||
<Skeleton isLoaded={ !isLoading } display="inline-block" whiteSpace="pre">Fee</Skeleton>
|
||||
{ tx.stability_fee ? (
|
||||
<TxFeeStability data={ tx.stability_fee } isLoading={ isLoading } hideUsd/>
|
||||
) : (
|
||||
<Skeleton isLoaded={ !isLoading } display="inline-block" variant="text_secondary" whiteSpace="pre">
|
||||
{ getValueWithUnit(tx.fee.value || 0).toFormat() }
|
||||
{ config.UI.views.tx.hiddenFields?.fee_currency ? '' : ` ${ currencyUnits.ether }` }
|
||||
</Skeleton>
|
||||
) }
|
||||
<TxFee tx={ tx } isLoading={ isLoading }/>
|
||||
</>
|
||||
) }
|
||||
</Flex>
|
||||
|
||||
@@ -43,6 +43,10 @@ const TxsTable = ({
|
||||
}: Props) => {
|
||||
const { cutRef, renderedItemsNum } = useLazyRenderedList(txs, !isLoading);
|
||||
|
||||
const feeCurrency = config.UI.views.tx.hiddenFields?.fee_currency || config.chain.hasMultipleGasCurrencies ?
|
||||
'' :
|
||||
' ' + currencyUnits.ether;
|
||||
|
||||
return (
|
||||
<AddressHighlightProvider>
|
||||
<Table variant="simple" minWidth="950px" size="xs">
|
||||
@@ -68,7 +72,7 @@ const TxsTable = ({
|
||||
<Link onClick={ sort('fee') } display="flex" justifyContent="end">
|
||||
{ sorting === 'fee-asc' && <IconSvg boxSize={ 5 } name="arrows/east" transform="rotate(-90deg)"/> }
|
||||
{ sorting === 'fee-desc' && <IconSvg boxSize={ 5 } name="arrows/east" transform="rotate(90deg)"/> }
|
||||
{ `Fee${ config.UI.views.tx.hiddenFields?.fee_currency ? '' : ` ${ currencyUnits.ether }` }` }
|
||||
{ `Fee${ feeCurrency }` }
|
||||
</Link>
|
||||
</Th>
|
||||
) }
|
||||
|
||||
@@ -17,7 +17,7 @@ import CurrencyValue from 'ui/shared/CurrencyValue';
|
||||
import BlockEntity from 'ui/shared/entities/block/BlockEntity';
|
||||
import TxEntity from 'ui/shared/entities/tx/TxEntity';
|
||||
import TxStatus from 'ui/shared/statusTag/TxStatus';
|
||||
import TxFeeStability from 'ui/shared/tx/TxFeeStability';
|
||||
import TxFee from 'ui/shared/tx/TxFee';
|
||||
import TxWatchListTags from 'ui/shared/tx/TxWatchListTags';
|
||||
import TxAdditionalInfo from 'ui/txs/TxAdditionalInfo';
|
||||
|
||||
@@ -109,12 +109,13 @@ const TxsTableItem = ({ tx, showBlockInfo, currentAddress, enableTimeIncrement,
|
||||
) }
|
||||
{ !config.UI.views.tx.hiddenFields?.tx_fee && (
|
||||
<Td isNumeric>
|
||||
{ /* eslint-disable-next-line no-nested-ternary */ }
|
||||
{ tx.stability_fee ? (
|
||||
<TxFeeStability data={ tx.stability_fee } isLoading={ isLoading } accuracy={ 8 } justifyContent="end" hideUsd/>
|
||||
) : (
|
||||
tx.fee.value ? <CurrencyValue value={ tx.fee.value } accuracy={ 8 } isLoading={ isLoading }/> : '-'
|
||||
) }
|
||||
<TxFee
|
||||
tx={ tx }
|
||||
accuracy={ 8 }
|
||||
isLoading={ isLoading }
|
||||
withCurrency={ Boolean(tx.celo || tx.stability_fee) }
|
||||
justifyContent="end"
|
||||
/>
|
||||
</Td>
|
||||
) }
|
||||
</Tr>
|
||||
|
||||