diff --git a/.github/workflows/deploy-libre.yaml b/.github/workflows/deploy-libre.yaml index 4ec53693..80fdb762 100644 --- a/.github/workflows/deploy-libre.yaml +++ b/.github/workflows/deploy-libre.yaml @@ -39,8 +39,8 @@ jobs: REACT_APP_DEFAULT_PRODUCER_LOGO: 'https://antelope.tools/images/libre.png' REACT_APP_FOOTER_LINKS: '[{ "text": "Libre Website", "src": "https://libre.org/" },{ "text": "Libre Block Explorer", "src": "https://www.libreblocks.io/" },{"text": "Documentation","src": "https://libre-chain.gitbook.io/"},{"text": "Libre Network Monitor","src": "https://libre.antelope.tools"}]' REACT_APP_EOS_RATE_LINK: '' - REACT_APP_USE_REWARDS: 'false' - REACT_APP_USE_VOTES: 'false' + REACT_APP_USE_REWARDS: 'true' + REACT_APP_USE_VOTES: 'true' REACT_APP_HASURA_URL: 'https://graphql-libre.antelope.tools/v1/graphql' REACT_APP_EOS_API_NETWORK_NAME: 'libre' REACT_APP_EOS_API_NETWORK_LABEL: 'Libre Mainnet' @@ -56,9 +56,9 @@ jobs: REACT_APP_SYNC_TOLERANCE_INTERVAL: 180000 REACT_APP_TOKEN_SYMBOL: 'LIBRE' REACT_APP_NETWORK_URL: '[{"label":"EOS","value":"https://eos.antelope.tools","mainnet":true,"pair":"eos","icon":"eos","order":1},{"label":"Proton","value":"https://proton.antelope.tools","mainnet":true,"pair":"proton","icon":"proton","order":2},{"label":"WAX","value":"https://wax.antelope.tools","mainnet":true,"pair":"wax","icon":"wax","order":3},{"label":"Telos","value":"https://telos.antelope.tools","mainnet":true,"pair":"telos","icon":"telos","order":4},{"label":"Libre","value":"https://libre.antelope.tools","mainnet":true,"pair":"libre","icon":"libre","order":5},{"label":"LACChain EOSIO","value":"https://lacchain.antelope.tools","mainnet":true,"pair":null,"icon":"lacchain","order":6},{"label":"Jungle4 Testnet","value":"https://jungle.antelope.tools","mainnet":false,"pair":"eos","icon":"jungle","order":1},{"label":"Proton Testnet","value":"https://proton-testnet.antelope.tools","mainnet":false,"pair":"proton","icon":"proton","order":2},{"label":"WAX Testnet","value":"https://wax-testnet.antelope.tools","mainnet":false,"pair":"wax","icon":"wax","order":3},{"label":"Telos Testnet","value":"https://telos-testnet.antelope.tools","mainnet":false,"pair":"telos","icon":"telos","order":4},{"label":"Libre Testnet","value":"https://libre-testnet.antelope.tools","mainnet":false,"pair":"libre","icon":"libre","order":5},{"label":"Ultra Testnet","value":"https://ultra-testnet.antelope.tools","mainnet":false,"pair":"ultra","icon":"ultra","order":6}]' - REACT_APP_DISABLED_MENU_ITEMS: '["/missed-blocks","/block-distribution","/stress-test"]' + REACT_APP_DISABLED_MENU_ITEMS: '["/missed-blocks","/stress-test"]' REACT_APP_BLOCK_EXPLORER_URL: 'https://www.libreblocks.io/tx/(transaction)' - REACT_APP_STATE_HISTORY_ENABLED=: 'false' + REACT_APP_STATE_HISTORY_ENABLED: 'true' REACT_APP_GOOGLE_ANALITIC_PAGE_ID: 'G-E6Y0EC9FT8' REACT_APP_PUBLIC_RE_CAPTCHA_KEY: ${{ secrets.REACT_APP_PUBLIC_RE_CAPTCHA_KEY }} @@ -82,6 +82,11 @@ jobs: # hapi HAPI_EOS_API_NETWORK_NAME: libre HAPI_EOS_API_ENDPOINTS: '["https://libre.edenia.cloud","https://libre.eosusa.io","https://api.libre.cryptolions.io"]' + HAPI_EOS_STATE_HISTORY_PLUGIN_ENDPOINT: 'ws://api-node.libre:8080' + HAPI_EOS_MISSED_BLOCKS_ENABLED: 'false' + HAPI_EOS_BLOCK_HISTORY_DAYS: 90 + HAPI_EOS_MAX_CPU_BLOCK: 100000 + HAPI_EOS_MAX_NET_BLOCK: 1048576 HAPI_EOS_API_CHAIN_ID: 38b1d7815474d0c60683ecbea321d723e83f5da6ae5f1c1f9fecc69d9ba96465 HAPI_EOS_BASE_ACCOUNT: ${{ secrets.HAPI_EOS_BASE_ACCOUNT }} HAPI_EOS_BASE_ACCOUNT_PASSWORD: ${{ secrets.HAPI_EOS_BASE_ACCOUNT_PASSWORD }} @@ -104,6 +109,7 @@ jobs: HAPI_SYNC_PRODUCER_CPU_INTERVAL: '6' HAPI_SYNC_PRODUCER_INFO_INTERVAL: '1' HAPI_SYNC_SCHEDULE_HISTORY_INTERVAL: 86400 + HAPI_SYNC_STATS_INTERVAL: 3600 HAPI_EOS_EXCHANGE_RATE_API: 'https://dashboard-api.libre.org/exchange-rates' HAPI_COINGECKO_API_TOKEN_ID: LIBRE HAPI_REWARDS_TOKEN: LIBRE diff --git a/.github/workflows/deploy-wax-testnet.yaml b/.github/workflows/deploy-wax-testnet.yaml index d9b1c0dc..c06f2a98 100644 --- a/.github/workflows/deploy-wax-testnet.yaml +++ b/.github/workflows/deploy-wax-testnet.yaml @@ -39,7 +39,7 @@ jobs: REACT_APP_DEFAULT_PRODUCER_LOGO: 'https://antelope.tools/images/wax.jpg' REACT_APP_FOOTER_LINKS: '[{ "text": "WAX Website", "src": "https://www.wax.io/" },{ "text": "WAX Block Explorer", "src": "https://wax-test.bloks.io/" },{"text": "Developer Guidelines","src": "https://developer.wax.io/"},{"text": "WAX Network Monitor","src": "https://antelope.tools"}]' REACT_APP_EOS_RATE_LINK: '' - REACT_APP_USE_REWARDS: 'false' + REACT_APP_USE_REWARDS: 'true' REACT_APP_USE_VOTES: 'true' REACT_APP_HASURA_URL: 'https://graphql-wax-testnet.antelope.tools/v1/graphql' REACT_APP_EOS_API_NETWORK_NAME: 'wax-testnet' diff --git a/.github/workflows/deploy-wax.yaml b/.github/workflows/deploy-wax.yaml index 816920cd..372b3959 100644 --- a/.github/workflows/deploy-wax.yaml +++ b/.github/workflows/deploy-wax.yaml @@ -39,7 +39,7 @@ jobs: REACT_APP_DEFAULT_PRODUCER_LOGO: 'https://antelope.tools/images/wax.jpg' REACT_APP_FOOTER_LINKS: '[{ "text": "WAX Website", "src": "https://www.wax.io/" },{ "text": "WAX Block Explorer", "src": "https://wax.bloks.io" },{"text": "Developer Guidelines","src": "https://developer.wax.io/"},{"text": "WAX Network Monitor","src": "https://antelope.tools"}]' REACT_APP_EOS_RATE_LINK: '' - REACT_APP_USE_REWARDS: 'false' + REACT_APP_USE_REWARDS: 'true' REACT_APP_USE_VOTES: 'true' REACT_APP_HASURA_URL: 'https://graphql-wax.antelope.tools/v1/graphql' REACT_APP_EOS_API_NETWORK_NAME: 'wax' diff --git a/hapi/src/routes/create-faucet-account.route.js b/hapi/src/routes/create-faucet-account.route.js index ef672b01..958b687b 100644 --- a/hapi/src/routes/create-faucet-account.route.js +++ b/hapi/src/routes/create-faucet-account.route.js @@ -6,7 +6,8 @@ const { eosUtil, axiosUtil, googleRecaptchaEnterpriseUtil, - getCreateAccountDataUtil + getCreateAccountDataUtil, + sleepFor } = require('../utils') module.exports = { @@ -21,7 +22,7 @@ module.exports = { throw Boom.badRequest('Are you a human?') } - await eosUtil.transact( + const transaction = await eosUtil.transact( [ { authorization: [ @@ -43,25 +44,35 @@ module.exports = { eosConfig.faucet.password ) + await sleepFor(1) + const { - data: { account_name: account, permissions } + data: { accounts } } = await axiosUtil.instance.post( - `${eosConfig.apiEndpoint}/v1/chain/get_account`, + `${eosConfig.apiEndpoint}/v1/chain/get_accounts_by_authorizers`, { - account_name: input.name + keys: [input.public_key] } ) - const keys = permissions[0]?.required_auth?.keys || [] - const key = keys[0]?.key - - if (account === input.name && key === input.public_key) { - return { account } + if (!input.name) { + input.name = transaction?.processed?.action_traces[0]?.act?.data?.name } - return Boom.badData('Wrong key format') + const newAccount = accounts.find( + (account) => account.account_name === input.name || !input.name + ) + + if (!newAccount) throw new Error('Account creation failed') + + return { account: newAccount.account_name } } catch (err) { - throw Boom.badRequest(err.message) + const errorDetail = + err?.response?.data?.error?.details[0]?.message?.substring(0, 11) + + return errorDetail === 'unknown key' + ? Boom.badRequest('Account creation failed') + : Boom.badRequest(err.message) } }, options: { diff --git a/webapp/src/components/ContractActionForm/index.js b/webapp/src/components/ContractActionForm/index.js index 229f566e..92bc50ad 100644 --- a/webapp/src/components/ContractActionForm/index.js +++ b/webapp/src/components/ContractActionForm/index.js @@ -227,7 +227,7 @@ const ContractActionForm = ({ accountName, action, abi, onSubmitAction }) => { InputProps={{ endAdornment: ( - + ), }} diff --git a/webapp/src/components/ContractTables/index.js b/webapp/src/components/ContractTables/index.js index 5ee5d728..f65c57ad 100644 --- a/webapp/src/components/ContractTables/index.js +++ b/webapp/src/components/ContractTables/index.js @@ -2,11 +2,10 @@ import React, { useState, useEffect, useCallback } from 'react' import PropTypes from 'prop-types' import { useTranslation } from 'react-i18next' import { makeStyles } from '@mui/styles' -import MenuItem from '@mui/material/MenuItem' -import Select from '@mui/material/Select' -import FormControl from '@mui/material/FormControl' +import Autocomplete from '@mui/material/Autocomplete' +import Checkbox from '@mui/material/Checkbox' +import FormControlLabel from '@mui/material/FormControlLabel' import TextField from '@mui/material/TextField' -import InputLabel from '@mui/material/InputLabel' import Button from '@mui/material/Button' import RefreshIcon from '@mui/icons-material/Refresh' @@ -24,26 +23,42 @@ const ContractTables = ({ onGetTableRows, tableName, }) => { + const keysTypes = [ + 'i64', + 'i128', + 'i256', + 'float64', + 'float128', + 'sha256', + 'ripemd160', + 'name', + ] const initData = { table: '', scope: '', + index: '', + keyType: keysTypes[0], lowerBound: null, upperBound: null, limit: 10, + reverse: false, } const formFields = [ { name: 'scope', type: 'text' }, - { name: 'lowerBound', type: 'number' }, - { name: 'upperBound', type: 'number' }, + { name: 'index', type: 'text' }, + { name: 'keyType', type: 'text', component: 'select', options: keysTypes }, + { name: 'lowerBound', type: 'text' }, + { name: 'upperBound', type: 'text' }, { name: 'limit', type: 'number' }, ] - const DELAY = 300 + const DELAY = 500 const { t } = useTranslation('contractTablesComponent') const classes = useStyles() const [tables, setTables] = useState([]) const [fields, setFields] = useState([]) const [filters, setFilters] = useState(initData) + const [selectedTable, setSelectedTable] = useState(tableName) const debouncedFilter = useDebounceState(filters, DELAY) const getValidValue = (value, type) => { @@ -78,6 +93,9 @@ const ContractTables = ({ table: filters.table, code: accountName, json: true, + key_type: filters.keyType, + index_position: filters.index, + reverse: filters.reverse, lower_bound: nextKey || filters.lowerBound, upper_bound: filters.upperBound, loadMore: !!nextKey, @@ -86,6 +104,19 @@ const ContractTables = ({ [accountName, onGetTableRows, filters], ) + const handleTableSelect = (_event, value) => { + setSelectedTable(value || '') + if (tables.includes(value)) { + handleTableChange(value) + } + } + + const handleFilterSelect = (newValue, name, type) => { + handleOnChange({ + target: { name, type, value: newValue || '' }, + }) + } + useEffect(() => { if (!abi) { setTables([]) @@ -103,10 +134,12 @@ const ContractTables = ({ return } + setSelectedTable(filters.table) + const tableType = abi.tables.find( - (item) => item.name === filters.table, + item => item.name === filters.table, )?.type - const struct = abi.structs.find((struct) => struct.name === tableType) + const struct = abi.structs.find(struct => struct.name === tableType) setFields(struct?.fields || []) }, [filters.table, abi]) @@ -127,52 +160,86 @@ const ContractTables = ({ }, [debouncedFilter]) return ( -
+ <>
- - {t('table')} - - - - {formFields.map(({ name, type }, index) => ( - handleOnChange(event)} +
+ ( + + )} + noOptionsText={t('noOptions')} /> - ))} - - {filters.table && ( - - )} + + {formFields.map(({ name, type, component, options }, index) => + component === 'select' ? ( + { + handleFilterSelect(value, name, type) + }} + onInputChange={(_e, value) => { + handleFilterSelect(value, name, type) + }} + renderInput={params => ( + + )} + noOptionsText={t('noOptions')} + /> + ) : ( + handleOnChange(event)} + /> + ), + )} + + { + setFilters(prev => ({ + ...prev, + reverse: event.target.checked, + })) + }} + /> + } + label={t('reverse')} + labelPlacement="top" + /> +
+ +
+ {filters.table && ( + + )} +
-
+ ) } diff --git a/webapp/src/components/ContractTables/styles.js b/webapp/src/components/ContractTables/styles.js index 4b6fdc3a..be477cd8 100644 --- a/webapp/src/components/ContractTables/styles.js +++ b/webapp/src/components/ContractTables/styles.js @@ -4,15 +4,22 @@ export default (theme) => ({ display: 'flex', alignItems: 'center', flexWrap: 'wrap', + gap: theme.spacing(1), + [theme.breakpoints.down('md')]: { + flexDirection: 'column' + } }, formControl: { display: 'block', - width: '100%', - minWidth: '180px', - [theme.breakpoints.up('md')]: { - width: '15%', - marginRight: theme.spacing(2), + width: '12%', + marginRight: theme.spacing(2), + [theme.breakpoints.down('lg')]: { + width: '10%', }, + [theme.breakpoints.down('md')]: { + width: '100%', + minWidth: '180px', + } }, refreshButton: { [theme.breakpoints.up('md')]: { @@ -28,9 +35,30 @@ export default (theme) => ({ padding: theme.spacing(4), }, tableEmpty: { - [theme.breakpoints.up('md')]: { - width: '150px !important', + [theme.breakpoints.down('lg')]: { + width: '100px !important', }, + [theme.breakpoints.down('md')]: { + width: '100% !important', + }, + width: '120px !important', display: 'inline-block', }, + fieldsContainer: { + display: 'flex', + flexWrap: 'wrap', + width: '80%', + gap: '8px', + }, + textField: { + width: '200px', + [theme.breakpoints.down('md')]: { + width: '100%' + } + }, + checkBox: { + '& .MuiCheckbox-root': { + padding: `${theme.spacing(1)} !important`, + } + } }) diff --git a/webapp/src/components/EndpointsTable/styles.js b/webapp/src/components/EndpointsTable/styles.js index c5e8ebf5..34b7ad03 100644 --- a/webapp/src/components/EndpointsTable/styles.js +++ b/webapp/src/components/EndpointsTable/styles.js @@ -3,6 +3,14 @@ export default (theme) => ({ display: 'flex', justifyContent: 'space-between', maxWidth: '250px', + wordBreak: 'break-all', + [theme.breakpoints.down('lg')]: { + maxWidth: '300px', + wordBreak: 'normal', + }, + [theme.breakpoints.up('xl')]: { + maxWidth: '350px', + } }, titleCell: { display: 'flex', diff --git a/webapp/src/components/ProducersChart/index.js b/webapp/src/components/ProducersChart/index.js index 0df0d56f..ba02b5b2 100644 --- a/webapp/src/components/ProducersChart/index.js +++ b/webapp/src/components/ProducersChart/index.js @@ -10,7 +10,7 @@ import { useTranslation } from 'react-i18next' import { formatWithThousandSeparator, onImgError } from '../../utils' import isLogoValid from '../../utils/validate-image' -import { generalConfig } from '../../config' +import { eosConfig, generalConfig } from '../../config' import styles from './styles' @@ -192,7 +192,9 @@ const CustomTooltip = memo(({ active, payload }) => { {t('rewards')}:{' '} {' '} - {formatWithThousandSeparator(payload[0].payload.rewards, 2)} + {`${formatWithThousandSeparator(payload[0].payload.rewards, 2)} ${ + eosConfig.tokenSymbol + }`} )} diff --git a/webapp/src/components/TransactionsHistory/index.js b/webapp/src/components/TransactionsHistory/index.js index fc124f95..fba5e5e4 100644 --- a/webapp/src/components/TransactionsHistory/index.js +++ b/webapp/src/components/TransactionsHistory/index.js @@ -17,17 +17,17 @@ import styles from './styles' const useStyles = makeStyles(styles) -const BodyGraphValue = ({ loading, value, classes, href }) => { +const BodyGraphValue = ({ loading, value, classes, links }) => { if (loading) return return ( {value} - {href && ( - + {links && links.map((href, index) => ( + - )} + ))} ) } @@ -60,9 +60,7 @@ const TransactionsHistory = ({ t, nodesChildren }) => { value={data?.stats[0]?.tps_all_time_high?.transactions_count} loading={loading} classes={classes} - href={getBlockNumUrl( - data?.stats?.[0]?.tps_all_time_high?.blocks[0], - )} + links={data?.stats?.[0]?.tps_all_time_high?.blocks.map(block => getBlockNumUrl(block))} /> @@ -70,9 +68,7 @@ const TransactionsHistory = ({ t, nodesChildren }) => { getBlockNumUrl(block))} loading={loading} /> @@ -81,9 +77,7 @@ const TransactionsHistory = ({ t, nodesChildren }) => { getBlockNumUrl(block))} loading={loading} /> diff --git a/webapp/src/gql/faucet.gql.js b/webapp/src/gql/faucet.gql.js index ad1670dc..8ea73834 100644 --- a/webapp/src/gql/faucet.gql.js +++ b/webapp/src/gql/faucet.gql.js @@ -1,8 +1,12 @@ import gql from 'graphql-tag' -export const CREATE_ACCOUNT_MUTATION = gql` - mutation ($token: String!, $public_key: String!, $name: String) { - createAccount(token: $token, public_key: $public_key, name: $name) { +export const CREATE_ACCOUNT_MUTATION = (includeName = true) => gql` + mutation ($token: String!, $public_key: String! ${ + includeName ? ', $name: String' : '' + }) { + createAccount(token: $token, public_key: $public_key ${ + includeName ? ', name: $name' : '' + }) { account } } diff --git a/webapp/src/language/en.json b/webapp/src/language/en.json index ad171e7e..220a3c84 100644 --- a/webapp/src/language/en.json +++ b/webapp/src/language/en.json @@ -139,8 +139,8 @@ "tpsAllTimeHigh": "TPS All Time High", "cpuUtilization": "CPU Usage", "netUtilization": "NET Usage", - "cpuUtilizationAllTimeHigh": "CPU Usage All Time High", - "networkUtilizationAllTimeHigh": "NET Usage All Time High", + "cpuUtilizationAllTimeHigh": "CPU Usage in TPS All Time High", + "networkUtilizationAllTimeHigh": "NET Usage in TPS All Time High", "transactionsPerSecond": "Transactions per Second", "transactionsPerBlock": "Transactions per Block", "nodes": "Nodes", @@ -328,6 +328,8 @@ "upperBound": "Upper Bound", "limit": "Limit", "refreshData": "Refresh Data", + "index": "Index Position", + "keyType": "Index Type", "loadMore": "Load More", "emptyTable": "Empty Table" }, diff --git a/webapp/src/language/es.json b/webapp/src/language/es.json index c834bdd4..2ca8080e 100644 --- a/webapp/src/language/es.json +++ b/webapp/src/language/es.json @@ -143,8 +143,8 @@ "secondsAgo": "Hace Segundos", "cpuUtilization": "Uso de CPU", "netUtilization": "Uso de NET", - "cpuUtilizationAllTimeHigh": "Máxima uso de CPU", - "networkUtilizationAllTimeHigh": "Máxima uso de NET", + "cpuUtilizationAllTimeHigh": "Uso de CPU en TPS máximo histórico", + "networkUtilizationAllTimeHigh": "Uso de NET en TPS máximo histórico", "tpsAllTimeHigh": "TPS máximo histórico", "transactionsPerSecond": "Transacciones por segundo", "transactionsPerBlock": "Transacciones por bloque", @@ -331,9 +331,11 @@ }, "contractTablesComponent": { "table": "Tabla", - "scope": "Alcance", + "scope": "Ámbito", "lowerBound": "Límite inferior", "upperBound": "Límite superior", + "index": "Posición del Índice", + "keyType": "Tipo del Índice", "limit": "Límite", "refreshData": "Actualizar Datos", "loadMore": "Carga Más", diff --git a/webapp/src/routes/Accounts/useAccountState.js b/webapp/src/routes/Accounts/useAccountState.js index c55fd0bb..bab77ba5 100644 --- a/webapp/src/routes/Accounts/useAccountState.js +++ b/webapp/src/routes/Accounts/useAccountState.js @@ -61,7 +61,9 @@ const useAccountState = () => { try { const actionError = JSON.parse(error?.message).error.details[0].message - return actionError ? `${resultRequested} ${t('notFound')}` : '' + return actionError?.substring(0, 11) === 'unknown key' + ? `${resultRequested} ${t('notFound')}` + : actionError } catch (error) { return t('unknownError') } diff --git a/webapp/src/routes/Faucet/index.js b/webapp/src/routes/Faucet/index.js index 5e46d716..ce4c5236 100644 --- a/webapp/src/routes/Faucet/index.js +++ b/webapp/src/routes/Faucet/index.js @@ -30,7 +30,7 @@ const Faucet = () => { const [createAccountValues, setCreateAccountValues] = useState({}) const [transferTokensTransaction, setTransferTokensTransaction] = useState('') const [createFaucetAccount, { loading: loadingCreateAccount }] = useMutation( - CREATE_ACCOUNT_MUTATION, + CREATE_ACCOUNT_MUTATION(eosConfig.networkName !== 'ultra-testnet'), ) const [transferFaucetTokens, { loading: loadingTransferFaucetTokens }] = useMutation(TRANFER_FAUCET_TOKENS_MUTATION) @@ -39,14 +39,25 @@ const Faucet = () => { const createAccount = async () => { const reCaptchaToken = await executeRecaptcha?.('submit') - if (eosConfig.networkName === 'libre-testnet') { - if ( - !reCaptchaToken || - (!createAccountValues.publicKey && !createAccountValues.accountName) - ) - return - } else { - if (!reCaptchaToken || !createAccountValues.publicKey) return + if ( + !reCaptchaToken || + !createAccountValues.publicKey || + (eosConfig.networkName !== 'ultra-testnet' && + !createAccountValues.accountName) + ) { + showMessage({ + type: 'error', + content: 'Fill out the fields to create an account', + }) + return + } + + if (!isValidAccountName(createAccountValues.accountName)) { + showMessage({ + type: 'error', + content: 'Please enter a valid account name', + }) + return } try { @@ -58,7 +69,7 @@ const Faucet = () => { variables: { token: reCaptchaToken, public_key: createAccountValues.publicKey, - name: createAccountValues.accountName || '', + name: createAccountValues.accountName, }, }) @@ -71,10 +82,9 @@ const Faucet = () => { ), }) } catch (err) { - const errorMessage = err.message.replace( - 'GraphQL error: assertion failure with message: ', - '', - ) + const errorMessage = err.message + .replace('GraphQL error:', '') + .replace('assertion failure with message: ', '') showMessage({ type: 'error', @@ -91,6 +101,14 @@ const Faucet = () => { if (!reCaptchaToken || !account) return + if (!isValidAccountName(account)) { + showMessage({ + type: 'error', + content: 'Please enter a valid account name', + }) + return + } + try { const result = await transferFaucetTokens({ variables: { @@ -128,6 +146,12 @@ const Faucet = () => { setAccount('') } + const isValidAccountName = name => { + const regex = /^[.12345abcdefghijklmnopqrstuvwxyz]+$/i + + return name?.length < 13 && regex.test(name) + } + return (
@@ -147,9 +171,11 @@ const Faucet = () => { ...{ publicKey: e.target.value }, }) } + error={!createAccountValues.publicKey} + required />
- {eosConfig.networkName === 'libre-testnet' && ( + {eosConfig.networkName !== 'ultra-testnet' && (
{ ...{ accountName: e.target.value }, }) } + error={!isValidAccountName(createAccountValues.accountName)} + required />
)} @@ -195,10 +223,12 @@ const Faucet = () => {
setAccount(e.target.value)} + error={!isValidAccountName(account)} + required />
diff --git a/webapp/src/utils/get-blocknum-url.js b/webapp/src/utils/get-blocknum-url.js index dbe9c1d6..3c7f8062 100644 --- a/webapp/src/utils/get-blocknum-url.js +++ b/webapp/src/utils/get-blocknum-url.js @@ -7,9 +7,10 @@ export const getBlockNumUrl = (blockNum) => { return ( eosConfig.blockExplorerUrl + .replace('(transaction)', blockNum) + .replace('tx', 'block') .replace('transaction', 'block') // eslint-disable-next-line - .replace('(transaction)', blockNum) ) }