diff --git a/README.md b/README.md index b60dd927..6ccd5db0 100644 --- a/README.md +++ b/README.md @@ -138,7 +138,6 @@ Note that `API_URL` should be set to the publicly-reachable URL where the user's Elements-only configuration: - `IS_ELEMENTS` - set to `1` to indicate this is an Elements-based chain (enables asset issuance and Elements-specific features) -- `SHOW_PEG_DATA` - set to `1` to show dashboard peg data and fetch its API resources (enabled by the Liquid mainnet and regtest flavors; custom pegged chains must opt in) - `SHOW_HIGH_VALUE_ASSETS` - set to `1` to show circulating values for selected assets on the Liquid mainnet dashboard (enabled by the Liquid mainnet flavor) - `NATIVE_ASSET_ID` - the ID of the native asset used to pay fees (defaults to `6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d`, the asset id for BTC) - `BLIND_PREFIX` - the base58 address prefix byte used for confidential addresses (defaults to `12`) diff --git a/client/src/app.js b/client/src/app.js index 326608f4..73060394 100644 --- a/client/src/app.js +++ b/client/src/app.js @@ -9,13 +9,11 @@ import { isBitcoinNetwork } from './lib/network' import getPrivacyAnalysis from './lib/privacy-analysis' import { highValueAssetDefinitions, - nativeAssetId, blockTxsPerPage, blocksPerPage, difficultyPeriod, pollIntervalsMs, showHighValueAssets, - showPegData, blockGridTransactionSelectEvent } from './const' import { @@ -243,14 +241,6 @@ export default function main( hasFocus, pollingEnabled ) - , recoverableReply = cat => O.merge( - reply(cat).map(value => ({ value, succeeded: true })) - , extractErrors(HTTP.select(cat)).mapTo({ succeeded: false })) - .scan((state, result) => result.succeeded - ? { value: result.value, error: false } - : { ...state, error: true } - , { value: null, error: false }) - .startWith({ value: null, error: false }) , on = (sel, ev, opt={}) => DOM.select(sel).events(ev, opt) , click = sel => on(sel, 'click').map(e => e.ownerTarget.dataset) @@ -449,26 +439,6 @@ export default function main( }) // dashboard - , dashboardPegAsset$ = !showPegData - ? O.of({ value: null, error: false }) - : recoverableReply('dashboard-peg-asset') - , dashboardPegChainTxs$ = !showPegData - ? O.of({ value: null, error: false }) - : recoverableReply('dashboard-peg-chain-txs') - , dashboardPegMempoolTxs$ = !showPegData - ? O.of({ value: null, error: false }) - : recoverableReply('dashboard-peg-mempool-txs') - , dashboardPegState$ = O.combineLatest( - dashboardPegAsset$ - , dashboardPegChainTxs$ - , dashboardPegMempoolTxs$ - , (asset, chainTxs, mempoolTxs) => ({ - asset: asset.value - , txs: chainTxs.value != null && mempoolTxs.value != null - ? [ ...mempoolTxs.value, ...chainTxs.value ] - : null - , error: asset.error || chainTxs.error || mempoolTxs.error - })) , dashboardHighValueAssets$ = !showHighValueAssets ? O.of({}) : O.merge(...highValueAssetDefinitions.reduce((replies, asset) => [ @@ -495,12 +465,10 @@ export default function main( , dashboardState$ = O.combineLatest( blocks$, mempoolRecent$, - dashboardPegState$, dashboardHighValueAssets$, - (blks, txs, peg, highValueAssets) => ({ + (blks, txs, highValueAssets) => ({ dashblocks: blks.slice(0, 5), dashTxs: txs.slice(0, 11), - peg, highValueAssets })) @@ -757,21 +725,10 @@ export default function main( ) .mapTo( { category: 'recent', method: 'GET', path: '/mempool/recent', bg: true }) - // refresh pending peg transactions on the standard cadence - , !showPegData ? O.empty() : - pollViewing(pollIntervalsMs.standard, 'dashBoard', view$) - .mapTo({ category: 'dashboard-peg-mempool-txs', method: 'GET', path: `/asset/${nativeAssetId}/txs/mempool`, bg: true }) - // the market chart contains hourly samples and uses the slow cadence , pollViewing(pollIntervalsMs.slow, 'dashBoard', view$) .mapTo({ category: 'bitcoin-market-chart', method: 'GET', path: bitcoinMarketChartUrl, bg: true }) - // confirmed peg state changes only when a new block arrives - , !showPegData ? O.empty() : - dashboardNewBlock$.flatMap(_ => - [{ category: 'dashboard-peg-asset', method: 'GET', path: `/asset/${nativeAssetId}`, bg: true } - , { category: 'dashboard-peg-chain-txs', method: 'GET', path: `/asset/${nativeAssetId}/txs/chain`, bg: true }]) - // Refresh the pending block template while the dashboard remains open. A new // tip resets the cadence and waits for electrs' block cache to refresh. , blockTemplatePoll$ @@ -783,12 +740,6 @@ export default function main( , { category: 'mempool', method: 'GET', path: '/mempool' } , { category: 'bitcoin-market-chart', method: 'GET', path: bitcoinMarketChartUrl, bg: true }]) - // fetch peg data only when opening an Elements dashboard - , !showPegData ? O.empty() : - goHome$.flatMap(_ => [{ category: 'dashboard-peg-asset', method: 'GET', path: `/asset/${nativeAssetId}`, bg: true } - , { category: 'dashboard-peg-chain-txs', method: 'GET', path: `/asset/${nativeAssetId}/txs/chain`, bg: true } - , { category: 'dashboard-peg-mempool-txs', method: 'GET', path: `/asset/${nativeAssetId}/txs/mempool`, bg: true }]) - // fetch asset stats and USD prices only while viewing the Liquid dashboard , !showHighValueAssets ? O.empty() : O.merge(goHome$, pollViewing(pollIntervalsMs.slow, 'dashBoard', view$)) diff --git a/client/src/const.js b/client/src/const.js index bab1ef87..e6b19116 100644 --- a/client/src/const.js +++ b/client/src/const.js @@ -35,7 +35,6 @@ const liquidNativeAssetId = '6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a export const nativeAssetId = process.env.NATIVE_ASSET_ID || liquidNativeAssetId export const nativeAssetLabel = process.env.NATIVE_ASSET_LABEL || 'BTC' export const nativeAssetName = process.env.NATIVE_ASSET_NAME || 'Bitcoin' -export const showPegData = !!process.env.IS_ELEMENTS && process.env.SHOW_PEG_DATA == '1' export const highValueAssetDefinitions = [ { @@ -64,4 +63,3 @@ export const showHighValueAssets = !!process.env.IS_ELEMENTS // Elements only export const assetTxsPerPage = 25 -export const pegTxsPerPage = 25 diff --git a/client/src/lib/peg.js b/client/src/lib/peg.js deleted file mode 100644 index 94206707..00000000 --- a/client/src/lib/peg.js +++ /dev/null @@ -1,27 +0,0 @@ -const isNonNegativeNumber = value => Number.isFinite(value) && value >= 0 - -export const getPegAccounting = (chainStats = {}) => { - const pegInAmount = chainStats.peg_in_amount - , pegOutAmount = chainStats.peg_out_amount - , burnedAmount = chainStats.burned_amount - , federationAssets = isNonNegativeNumber(pegInAmount) && - isNonNegativeNumber(pegOutAmount) && pegInAmount >= pegOutAmount - ? pegInAmount - pegOutAmount - : null - , circulatingLiabilities = isNonNegativeNumber(federationAssets) && - isNonNegativeNumber(burnedAmount) && federationAssets >= burnedAmount - ? federationAssets - burnedAmount - : null - , assetsVsLiabilitiesRatio = isNonNegativeNumber(federationAssets) && circulatingLiabilities > 0 - ? federationAssets / circulatingLiabilities * 100 - : null - - return { - pegInAmount - , pegOutAmount - , burnedAmount - , federationAssets - , circulatingLiabilities - , assetsVsLiabilitiesRatio - } -} diff --git a/client/src/views/home.js b/client/src/views/home.js index 1da796eb..cc83ac3b 100644 --- a/client/src/views/home.js +++ b/client/src/views/home.js @@ -1,12 +1,11 @@ import layout from "./layout"; import { blks } from "./blocks"; import { transactions } from "./transactions"; -import { pegInfo } from "./peg-info"; import { overview } from "./overview"; import { feeMarket } from "./fee-market"; import difficultyAdjustment from "./difficulty-adjustment"; import { isBitcoinNetwork } from "../lib/network"; -import { showHighValueAssets, showPegData } from "../const"; +import { showHighValueAssets } from "../const"; import { highValueAssets } from "../components/high-value-assets"; const isTouch = process.browser && "ontouchstart" in window; @@ -15,19 +14,14 @@ const homeLayout = (body, { t, activeTab, ...S }) => layout(body, { t, isTouch, activeTab, ...S }); export const dashBoard = ({ t, blocks, dashboardState, loading, ...S }) => { - const { dashblocks, dashTxs, peg = {}, highValueAssets: highValueAssetData } = dashboardState || {}; + const { dashblocks, dashTxs, highValueAssets: highValueAssetData } = dashboardState || {}; return homeLayout(
{overview({ blocks: dashblocks, t, ...S })} {blks(dashblocks, true, { t, ...S })} {isBitcoinNetwork ? feeMarket({ t, ...S }) : ""} -
- {transactions(dashTxs, true, { t, ...S })} - {showPegData - ? pegInfo(peg.asset, peg.txs, { t, ...S, error: peg.error }) - : ""} -
+ {transactions(dashTxs, true, { t, ...S })} {!isBitcoinNetwork ? feeMarket({ t, ...S }) : ""} {isBitcoinNetwork ? difficultyAdjustment({ blocks: dashblocks, t, ...S }) diff --git a/client/src/views/peg-info.js b/client/src/views/peg-info.js deleted file mode 100644 index ea86cf7e..00000000 --- a/client/src/views/peg-info.js +++ /dev/null @@ -1,332 +0,0 @@ -import { formatSat, formatNumber, formatTime, truncateTxid } from "./util"; -import loader from "../components/loading"; -import { InfoCard } from "../components/info-card"; -import { ArrowsInSimpleIcon } from "../components/icons"; -import { StatusBadge } from "../components/status-badge"; -import { InfoStat } from "../components/info-stat"; -import { Tooltip } from "../components/tooltip"; -import { getConfEstimate } from "../lib/fees"; -import { getPegAccounting } from "../lib/peg"; -import { calculateFeerates } from "../util"; -import { - nativeAssetId, - satoshisPerBitcoin, - targetBlockIntervalSeconds, -} from "../const"; - -const staticRoot = process.env.STATIC_ROOT || ""; -const ratioMiddleBound = 100; -const minimumRatioHalfRange = 0.1; -const ratioScaleIncrement = 0.1; -const ratioScaleHeadroom = 0.05; -const recentPegTransactionLimit = 4; - -const getPegTypes = (tx) => { - const pegTypes = []; - if (tx.vin && tx.vin.some((vin) => vin.is_pegin)) pegTypes.push("peg-in"); - if (tx.vout && tx.vout.some((vout) => vout.pegout)) pegTypes.push("peg-out"); - return pegTypes; -}; - -const sumValues = (outputs) => - outputs.length && outputs.every((output) => Number.isFinite(output.value)) - ? outputs.reduce((sum, output) => sum + output.value, 0) - : null; - -const getPegInAmount = (tx) => { - const outputs = tx.vout || []; - const regularInputs = (tx.vin || []).filter((vin) => !vin.is_pegin); - const hasUnknownOutputAsset = outputs.some( - (output) => output.assetcommitment && !output.asset, - ); - const hasUnknownInput = regularInputs.some( - (vin) => - !vin.prevout || - (vin.prevout.assetcommitment && !vin.prevout.asset) || - (vin.prevout.asset == nativeAssetId && - !Number.isFinite(vin.prevout.value)), - ); - if (hasUnknownOutputAsset || hasUnknownInput) return null; - - const nativeOutputAmount = sumValues( - outputs.filter((output) => output.asset == nativeAssetId), - ); - const nativeInputAmount = regularInputs - .map((vin) => vin.prevout) - .filter((output) => output.asset == nativeAssetId) - .reduce((sum, output) => sum + output.value, 0); - - return Number.isFinite(nativeOutputAmount) && nativeOutputAmount >= nativeInputAmount - ? nativeOutputAmount - nativeInputAmount - : null; -}; - -const getPegAmount = (tx, pegType) => { - const outputs = tx.vout || []; - return pegType == "peg-in" - ? getPegInAmount(tx) - : sumValues(outputs.filter((output) => output.pegout)); -}; - -const getRatioScale = (ratio) => { - const ratioDistance = Number.isFinite(ratio) - ? Math.abs(ratio - ratioMiddleBound) - : 0; - const halfRange = Math.max( - minimumRatioHalfRange, - Math.ceil( - (ratioDistance + ratioScaleHeadroom) / ratioScaleIncrement, - ) * ratioScaleIncrement, - ); - const lowerBound = ratioMiddleBound - halfRange; - const upperBound = ratioMiddleBound + halfRange; - const fill = Number.isFinite(ratio) - ? Math.min( - 100, - Math.max(0, ((ratio - lowerBound) / (upperBound - lowerBound)) * 100), - ) - : 0; - - return { lowerBound, upperBound, fill }; -}; - -const formatRatioBound = (ratio) => `${ratio.toFixed(1)}%`; - -const getLastConfirmationTime = (txs) => - txs.reduce( - (latest, tx) => - tx.status && Number.isFinite(tx.status.block_time) - ? Math.max(latest, tx.status.block_time) - : latest, - 0, - ); - -const formatStatAmount = (value, t) => - Number.isFinite(value) ? formatSat(value) : t`N/A`; - -const formatFederationAssets = (value, t) => - Number.isFinite(value) ? formatSat(value, "BTC") : t`N/A`; - -const formatVolumeAmount = (value, t) => { - if (!Number.isFinite(value)) return t`N/A`; - - const amount = (value / satoshisPerBitcoin).toFixed(2); - return `~${formatNumber(amount)}`; -}; - -const getConfirmationEta = (tx, feeEst, t) => { - if (!tx.status) return t`N/A`; - if (tx.status.confirmed) return t`Confirmed`; - if (!feeEst) return t`N/A`; - - const { effectiveFeerate } = calculateFeerates(tx, null, feeEst); - if (effectiveFeerate == null) return t`N/A`; - - const confirmationBlocks = getConfEstimate(feeEst, effectiveFeerate); - if (confirmationBlocks == -1) return t`Unknown`; - - const confirmationMinutes = - Number(confirmationBlocks) * targetBlockIntervalSeconds / 60; - return confirmationMinutes < 1 - ? t`< 1 min` - : t`~${Math.ceil(confirmationMinutes)} min`; -}; - -export const pegInfo = (asset, txs, { t, feeEst, error }) => { - if (error && (!asset || !txs)) { - return ( -
-
-
-
- -
-

{t`Peg Information`}

-
-

{t`Peg data is currently unavailable.`}

-
-
- ); - } - if (!asset || !txs) return
{loader()}
; - - const chainStats = asset.chain_stats || {}; - const { - pegInAmount, - pegOutAmount, - federationAssets, - assetsVsLiabilitiesRatio, - } = getPegAccounting(chainStats); - const ratioScale = getRatioScale(assetsVsLiabilitiesRatio); - const allPegTransactions = txs - .reduce( - (entries, tx) => entries.concat( - getPegTypes(tx).map((pegType) => ({ tx, pegType })), - ), - [], - ) - .sort((a, b) => { - const aConfirmed = a.tx.status && a.tx.status.confirmed; - const bConfirmed = b.tx.status && b.tx.status.confirmed; - return aConfirmed != bConfirmed - ? aConfirmed ? 1 : -1 - : ((b.tx.status && b.tx.status.block_time) || 0) - - ((a.tx.status && a.tx.status.block_time) || 0); - }); - const pegTransactions = allPegTransactions.slice(0, recentPegTransactionLimit); - const lastConfirmationTime = getLastConfirmationTime( - allPegTransactions.map(({ tx }) => tx), - ); - - return ( -
-

{t`Proof of Reserves`}

-
- {error ? ( -

- {t`Unable to refresh — showing previous data.`} -

- ) : null} - - - -
-

{formatRatioBound(ratioScale.lowerBound)}

-

{formatRatioBound(ratioMiddleBound)}

-

{formatRatioBound(ratioScale.upperBound)}

-
-
-
-
-

- {Number.isFinite(assetsVsLiabilitiesRatio) - ? `${assetsVsLiabilitiesRatio.toFixed(3)}%` - : t`N/A`} -

-
- } - /> - -
-
-
- -
-

{t`Recent Peg-Ins/Outs`}

-
- -
- - {formatNumber(chainStats.peg_in_count)} - - : t`N/A` - } - /> - - {formatNumber(chainStats.peg_out_count)} - - : t`N/A` - } - /> - - -
- -
-
{t`TYPE`}
-
{t`TXID`}
-
{t`AMOUNT`}
-
{t`BLOCK`}
-
- {t`ETA`} - -
-
- -
- {!pegTransactions.length ? ( -

{t`No recent transactions`}

- ) : ( - pegTransactions.map(({ tx, pegType }) => ( - -
-
-
{t`TYPE`}
-
- - {pegType == "peg-in" ? t`Peg-in` : t`Peg-out`} - -
-
-
-
{t`TXID`}
-
-

{truncateTxid(tx.txid)}

-
-
-
-
{t`AMOUNT`}
-
- {formatStatAmount(getPegAmount(tx, pegType), t)} -
-
-
-
{t`BLOCK`}
-
- {tx.status && Number.isFinite(tx.status.block_height) - ? `#${formatNumber(tx.status.block_height)}` - : t`N/A`} -
-
-
-
{t`ETA`}
-
- {getConfirmationEta(tx, feeEst, t)} -
-
-
-
- )) - )} -
-
-
-
- ); -}; diff --git a/flavors/liquid-mainnet/config.env b/flavors/liquid-mainnet/config.env index d5827f84..db3fc174 100755 --- a/flavors/liquid-mainnet/config.env +++ b/flavors/liquid-mainnet/config.env @@ -6,7 +6,6 @@ export NATIVE_ASSET_ID="6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec export NATIVE_ASSET_LABEL=LBTC export NATIVE_ASSET_NAME='Liquid Bitcoin' export IS_ELEMENTS=1 -export SHOW_PEG_DATA=${SHOW_PEG_DATA:-1} export SHOW_HIGH_VALUE_ASSETS=${SHOW_HIGH_VALUE_ASSETS:-1} export ASSET_MAP_URL=./_data/assets.minimal.json diff --git a/flavors/liquid-regtest/config.env b/flavors/liquid-regtest/config.env index 7f84bfde..2c27d412 100644 --- a/flavors/liquid-regtest/config.env +++ b/flavors/liquid-regtest/config.env @@ -7,7 +7,6 @@ export NATIVE_ASSET_LABEL=rLBTC export NATIVE_ASSET_NAME="Liquid Regtest Bitcoin" export IS_ELEMENTS=1 -export SHOW_PEG_DATA=${SHOW_PEG_DATA:-1} export MENU_ACTIVE='Liquid Regtest' export BASE_HREF=${BASE_HREF:-'/liquidregtest/'} diff --git a/flavors/liquid-testnet/config.env b/flavors/liquid-testnet/config.env index a5cfa12d..e4085367 100644 --- a/flavors/liquid-testnet/config.env +++ b/flavors/liquid-testnet/config.env @@ -7,7 +7,6 @@ export NATIVE_ASSET_LABEL=tLBTC export NATIVE_ASSET_NAME="Liquid Testnet Bitcoin" export IS_ELEMENTS=1 -export SHOW_PEG_DATA=${SHOW_PEG_DATA:-0} export ASSET_MAP_URL=./_data/assets.minimal.json export MENU_ACTIVE='Liquid Testnet' diff --git a/lang/pt-pt.json b/lang/pt-pt.json index 8b27850c..41b157e9 100644 --- a/lang/pt-pt.json +++ b/lang/pt-pt.json @@ -63,11 +63,9 @@ "A balanced fee rate estimated to confirm within %s blocks.": "Uma taxa de comissão equilibrada estimada para confirmação no prazo de %0 blocos.", "A higher-priority fee rate estimated to confirm in the next block.": "Uma taxa de comissão de prioridade mais alta estimada para confirmação no próximo bloco.", "A lower-priority fee rate estimated to confirm within %s blocks.": "Uma taxa de comissão de prioridade mais baixa estimada para confirmação no prazo de %0 blocos.", - "AMOUNT": "MONTANTE", "AVERAGE BLOCK TIME": "TEMPO MÉDIO DO BLOCO", "AVG FEE": "TAXA MÉDIA", "All selected transactions are shown individually.": "Todas as transações selecionadas são apresentadas individualmente.", - "Assets vs Liabilities": "Ativos vs Passivos", "Average": "Média", "Average fee rate and transaction fee in the high-fee portion of this template.": "Taxa de comissão média e taxa de transação na parte de taxas altas deste modelo.", "Average fee rate and transaction fee in the low-fee portion of this template.": "Taxa de comissão média e taxa de transação na parte de taxas baixas deste modelo.", @@ -81,8 +79,6 @@ "Block is %s% full": "O bloco está %0% preenchido", "Block utilization unavailable": "Utilização do bloco indisponível", "Blocks History": "Histórico de Blocos", - "Confirmed federation BTC holdings divided by circulating L-BTC supply.": "Reservas confirmadas de BTC da federação divididas pela oferta de L-BTC em circulação.", - "Confirmed peg-ins minus confirmed peg-outs.": "Peg-ins confirmados menos peg-outs confirmados.", "Copy block number %s": "Copiar número do bloco %0", "Copy transaction id %s": "Copiar ID da transação %0", "Current template weight and serialized size compared with their block limits.": "Peso atual do modelo e tamanho serializado comparados com os respetivos limites do bloco.", @@ -98,10 +94,8 @@ "Elapsed time since the last block confirmed. Bitcoin targets one every ~10 minutes.": "Tempo decorrido desde a confirmação do último bloco. A rede Bitcoin procura confirmar um bloco a cada ~10 minutos.", "Elapsed time since the last block confirmed. Liquid targets one every ~1 minute.": "Tempo decorrido desde a confirmação do último bloco. A rede Liquid procura confirmar um bloco a cada ~1 minuto.", "Estimated computing power securing the network.": "Poder computacional estimado que protege a rede.", - "Estimated time until the peg transaction is confirmed.": "Tempo estimado até à confirmação da transação de peg.", "Exahashes per second": "Exahashes por segundo", "FEE": "TAXA", - "Federation BTC Holdings": "Reservas de BTC da Federação", "Fee Market": "Mercado de Taxas", "Fee rate": "Taxa de comissão", "Fee-rate estimates are unavailable; transactions use a neutral color.": "As estimativas das taxas de comissão estão indisponíveis; as transações utilizam uma cor neutra.", @@ -119,8 +113,6 @@ "Individually rendered transactions": "Transações apresentadas individualmente", "Kilohashes per second": "Kilohashes por segundo", "LOW": "BAIXA", - "Last change N/A": "Última alteração N/D", - "Last change on %s": "Última alteração em %0", "Latest": "Mais Recente", "Latest Blocks": "Blocos Mais Recentes", "Latest Transactions": "Transações Mais Recentes", @@ -141,13 +133,9 @@ "No recent blocks": "Sem blocos recentes", "Overview": "Visão Geral", "PREVIOUS ADJ": "AJUSTE ANTERIOR", - "Peg Information": "Informação de Peg", - "Peg data is currently unavailable.": "Os dados de peg estão atualmente indisponíveis.", "Pending Transactions": "Transações Pendentes", "Pending block transaction grid": "Grelha de transações do bloco pendente", "Petahashes per second": "Petahashes por segundo", - "Proof of Reserves": "Prova de Reservas", - "Recent Peg-Ins/Outs": "Peg-Ins/Outs Recentes", "Recommended Fee": "Taxa Recomendada", "SIZE": "TAMANHO", "See More": "Ver Mais", @@ -157,7 +145,6 @@ "TRANSACTION ID": "ID DA TRANSAÇÃO", "TRANSACTIONS": "TRANSAÇÕES", "TX ID": "ID DA TX", - "TYPE": "TIPO", "Terahashes per second": "Terahashes por segundo", "This panel shows the circulating value of high-value assets on Liquid.": "Este painel apresenta o valor em circulação dos ativos de alto valor na Liquid.", "Time Since Last Block": "Tempo Desde o Último Bloco", @@ -166,12 +153,9 @@ "Transaction Types": "Tipos de Transação", "Transactions currently selected for the pending block, including the coinbase transaction.": "Transações atualmente selecionadas para o bloco pendente, incluindo a transação coinbase.", "Transactions currently waiting in the node's mempool.": "Transações atualmente à espera na mempool do nó.", - "Unable to refresh — showing previous data.": "Não foi possível atualizar — a apresentar os dados anteriores.", "Unknown": "Desconhecido", "Use the arrow keys to inspect transactions and Enter to open one.": "Utilize as teclas de seta para inspecionar transações e Enter para abrir uma.", "VALUE": "VALOR", - "VOLUME IN": "VOLUME DE ENTRADA", - "VOLUME OUT": "VOLUME DE SAÍDA", "View block %s": "Ver bloco %0", "Weight": "Peso", "Yottahashes per second": "Yottahashes por segundo", @@ -183,7 +167,6 @@ "pm": "p.m.", "w": "sem", "y": "a", - "~%s min": "~%0 min", "%s AGO": "HÁ %0", "< 1 MINUTE": "< 1 MINUTO", "DAY": "DIA", diff --git a/lang/pt-pt.po b/lang/pt-pt.po index 2ffa35db..cabb1103 100644 --- a/lang/pt-pt.po +++ b/lang/pt-pt.po @@ -527,9 +527,6 @@ msgstr "há 1 minuto" msgid "< 1 MINUTE PAST EXPECTED INTERVAL" msgstr "< 1 MINUTO APÓS O INTERVALO PREVISTO" -msgid "< 1 min" -msgstr "< 1 min" - msgid "< 1m" msgstr "< 1min" @@ -542,9 +539,6 @@ msgstr "Uma taxa de comissão de prioridade mais alta estimada para confirmaçã msgid "A lower-priority fee rate estimated to confirm within %s blocks." msgstr "Uma taxa de comissão de prioridade mais baixa estimada para confirmação no prazo de %0 blocos." -msgid "AMOUNT" -msgstr "MONTANTE" - msgid "AVERAGE BLOCK TIME" msgstr "TEMPO MÉDIO DO BLOCO" @@ -554,9 +548,6 @@ msgstr "TAXA MÉDIA" msgid "All selected transactions are shown individually." msgstr "Todas as transações selecionadas são apresentadas individualmente." -msgid "Assets vs Liabilities" -msgstr "Ativos vs Passivos" - msgid "Average" msgstr "Média" @@ -599,12 +590,6 @@ msgstr "Utilização do bloco indisponível" msgid "Blocks History" msgstr "Histórico de Blocos" -msgid "Confirmed federation BTC holdings divided by circulating L-BTC supply." -msgstr "Reservas confirmadas de BTC da federação divididas pela oferta de L-BTC em circulação." - -msgid "Confirmed peg-ins minus confirmed peg-outs." -msgstr "Peg-ins confirmados menos peg-outs confirmados." - msgid "Copy block number %s" msgstr "Copiar número do bloco %0" @@ -650,18 +635,12 @@ msgstr "Tempo decorrido desde a confirmação do último bloco. A rede Liquid pr msgid "Estimated computing power securing the network." msgstr "Poder computacional estimado que protege a rede." -msgid "Estimated time until the peg transaction is confirmed." -msgstr "Tempo estimado até à confirmação da transação de peg." - msgid "Exahashes per second" msgstr "Exahashes por segundo" msgid "FEE" msgstr "TAXA" -msgid "Federation BTC Holdings" -msgstr "Reservas de BTC da Federação" - msgid "Fee Market" msgstr "Mercado de Taxas" @@ -713,12 +692,6 @@ msgstr "Kilohashes por segundo" msgid "LOW" msgstr "BAIXA" -msgid "Last change N/A" -msgstr "Última alteração N/D" - -msgid "Last change on %s" -msgstr "Última alteração em %0" - msgid "Latest" msgstr "Mais Recente" @@ -776,24 +749,9 @@ msgstr "Sem blocos recentes" msgid "Overview" msgstr "Visão Geral" -msgid "PEG-IN" -msgstr "PEG-IN" - -msgid "PEG-OUT" -msgstr "PEG-OUT" - msgid "PREVIOUS ADJ" msgstr "AJUSTE ANTERIOR" -msgid "Peg Information" -msgstr "Informação de Peg" - -msgid "Peg data is currently unavailable." -msgstr "Os dados de peg estão atualmente indisponíveis." - -msgid "Peg-in" -msgstr "Peg-in" - msgid "Pending Transactions" msgstr "Transações Pendentes" @@ -803,12 +761,6 @@ msgstr "Grelha de transações do bloco pendente" msgid "Petahashes per second" msgstr "Petahashes por segundo" -msgid "Proof of Reserves" -msgstr "Prova de Reservas" - -msgid "Recent Peg-Ins/Outs" -msgstr "Peg-Ins/Outs Recentes" - msgid "Recommended Fee" msgstr "Taxa Recomendada" @@ -839,9 +791,6 @@ msgstr "TRANSAÇÕES" msgid "TX ID" msgstr "ID DA TX" -msgid "TYPE" -msgstr "TIPO" - msgid "Terahashes per second" msgstr "Terahashes por segundo" @@ -866,9 +815,6 @@ msgstr "Transações atualmente selecionadas para o bloco pendente, incluindo a msgid "Transactions currently waiting in the node's mempool." msgstr "Transações atualmente à espera na mempool do nó." -msgid "Unable to refresh — showing previous data." -msgstr "Não foi possível atualizar — a apresentar os dados anteriores." - msgid "Unknown" msgstr "Desconhecido" @@ -878,12 +824,6 @@ msgstr "Utilize as teclas de seta para inspecionar transações e Enter para abr msgid "VALUE" msgstr "VALOR" -msgid "VOLUME IN" -msgstr "VOLUME DE ENTRADA" - -msgid "VOLUME OUT" -msgstr "VOLUME DE SAÍDA" - msgid "View block %s" msgstr "Ver bloco %0" @@ -926,9 +866,6 @@ msgstr "sem" msgid "y" msgstr "a" -msgid "~%s min" -msgstr "~%0 min" - msgid "%s AGO" msgstr "HÁ %0" diff --git a/lang/strings.txt b/lang/strings.txt index e02cc44f..b4f33734 100644 --- a/lang/strings.txt +++ b/lang/strings.txt @@ -4,7 +4,6 @@ A lower-priority fee rate estimated to confirm within %s blocks. Address Address reuse Address: %s -AMOUNT Amount commitment API Asset @@ -12,7 +11,6 @@ Asset commitment Asset ID Asset name Asset: %s -Assets vs Liabilities Average Bits BLOCK @@ -42,9 +40,7 @@ EXPECTED INTERVAL REACHED compared to bitcoind's suggested fee of %s sat/vB for confirmation within 2 blocks Confidential Confirmed -Confirmed federation BTC holdings divided by circulating L-BTC supply. Confirmed non-confidential tx count -Confirmed peg-ins minus confirmed peg-outs. Confirmed received Confirmed spent Confirmed tx count @@ -57,10 +53,8 @@ Description Details Esplora is currently unavailable, please try again later. ETA -Estimated time until the peg transaction is confirmed. Fee Fee Market -Federation BTC Holdings Go Height High @@ -80,8 +74,6 @@ Issued asset id It is possible to tell the change output apart because you're sending to a different script type than the one you're spending from. lang_id lang_name -Last change N/A -Last change on %s Likely self-transfer Linked domain Loading block... @@ -118,11 +110,6 @@ overpaying by %s% P2SH redeem script P2WSH witness script Page Not Found -PEG-IN -PEG-OUT -Peg Information -Peg data is currently unavailable. -Peg-in Peg-out Peg-out address Peg-out ASM @@ -134,9 +121,7 @@ Previous Previous output address Previous output script Privacy analysis -Proof of Reserves (RBF) -Recent Peg-Ins/Outs Recent transactions Re-issuable Reissuance @@ -198,10 +183,8 @@ Transaction not found Transactions Transaction: %s TXID -TYPE txid:vout Type -Unable to refresh — showing previous data. unconfirmed Unconfirmed Unconfirmed received @@ -218,8 +201,6 @@ Value commitment Version Version bits recorded in the block header. Virtual size -VOLUME IN -VOLUME OUT We encountered an error. Please try again later. Weight Weight (KWU) @@ -227,8 +208,6 @@ Weight units Witness Yes < 1 MINUTE PAST EXPECTED INTERVAL -< 1 min -~%s min %s SELECTED + COINBASE %s added since the last update %s days ago diff --git a/test/app.test.js b/test/app.test.js index 24b8cc7e..96a55449 100644 --- a/test/app.test.js +++ b/test/app.test.js @@ -7,7 +7,6 @@ const { process.env.IS_ELEMENTS = "1"; process.env.MENU_ACTIVE = "Liquid"; -process.env.SHOW_PEG_DATA = "1"; process.env.SHOW_HIGH_VALUE_ASSETS = "1"; process.env.CANONICAL_URL = "https://blockstream.info/liquid/"; @@ -328,10 +327,6 @@ test("uses the configured dashboard request cadences without immediate duplicate assert.deepEqual(requestFrames(requests, "recent"), fastFrames); assert.deepEqual(requestFrames(requests, "mempool"), standardFrames); assert.deepEqual(requestFrames(requests, "fee-est"), standardFrames); - assert.deepEqual( - requestFrames(requests, "dashboard-peg-mempool-txs"), - standardFrames, - ); assert.deepEqual(requestFrames(requests, "blocks"), slowFrames); assert.deepEqual( requestFrames(requests, "bitcoin-market-chart"), @@ -462,7 +457,7 @@ test("treats the first block response of each dashboard visit as a baseline", () assert.deepEqual(newBlocks, ["b", "e"]); }); -test("refreshes Liquid block-dependent data only after a new block response", () => { +test("resets block-template polling only after a new block response", () => { const scheduler = new TestScheduler((actual, expected) => assert.deepEqual(actual, expected)); const blocksResponses = new Subject(); @@ -489,15 +484,6 @@ test("refreshes Liquid block-dependent data only after a new block response", () scheduler.flush(); assert.deepEqual(requestFrames(requests, "blocks"), [0, 3]); - assert.deepEqual(requestFrames(requests, "dashboard-peg-asset"), [0, 4]); - assert.deepEqual( - requestFrames(requests, "dashboard-peg-chain-txs"), - [0, 4], - ); - assert.deepEqual( - requestFrames(requests, "dashboard-peg-mempool-txs"), - [0], - ); assert.deepEqual(requestFrames(requests, "block-template"), [0, 15_004]); }); diff --git a/test/dashboard-localization.test.js b/test/dashboard-localization.test.js index 3bdee741..1ec92fd6 100644 --- a/test/dashboard-localization.test.js +++ b/test/dashboard-localization.test.js @@ -97,7 +97,6 @@ test("localizes elapsed times and formatter-owned fallbacks", () => { test("preserves all-caps dashboard label casing in Portuguese", () => { [ - "AMOUNT", "AVERAGE BLOCK TIME", "AVG FEE", "%s AGO", @@ -118,8 +117,6 @@ test("preserves all-caps dashboard label casing in Portuguese", () => { "MINUTES", "MONTH", "MONTHS", - "PEG-IN", - "PEG-OUT", "PREVIOUS ADJ", "SIZE", "TOTAL FEE COLLECTED", @@ -127,10 +124,7 @@ test("preserves all-caps dashboard label casing in Portuguese", () => { "TRANSACTIONS", "TX ID", "TXID", - "TYPE", "VALUE", - "VOLUME IN", - "VOLUME OUT", "YEAR", "YEARS", ].forEach((key) => { diff --git a/test/peg.test.js b/test/peg.test.js deleted file mode 100644 index a5dac610..00000000 --- a/test/peg.test.js +++ /dev/null @@ -1,261 +0,0 @@ -const test = require("node:test"); -const assert = require("node:assert/strict"); -const render = require("snabbdom-to-html"); - -const { getPegAccounting } = require("../client/src/lib/peg"); -const { pegInfo } = require("../client/src/views/peg-info"); -const l10n = require("../client/src/l10n").default; -const { nativeAssetId } = require("../client/src/const"); - -test("derives federation assets and circulating liabilities from distinct peg fields", () => { - const accounting = getPegAccounting({ - peg_in_amount: 100_000_000_000, - peg_out_amount: 10_000_000_000, - burned_amount: 1_000_000_000, - }); - - assert.equal(accounting.federationAssets, 90_000_000_000); - assert.equal(accounting.circulatingLiabilities, 89_000_000_000); - assert.equal( - accounting.assetsVsLiabilitiesRatio, - 90_000_000_000 / 89_000_000_000 * 100, - ); -}); - -test("keeps burns separate from peg-outs and handles invalid ratios", () => { - const withoutBurns = getPegAccounting({ - peg_in_amount: 1000, - peg_out_amount: 100, - burned_amount: 0, - }); - const withoutLiabilities = getPegAccounting({ - peg_in_amount: 1000, - peg_out_amount: 100, - burned_amount: 900, - }); - const invalidAssets = getPegAccounting({ - peg_in_amount: 100, - peg_out_amount: 101, - burned_amount: 0, - }); - const incompleteStats = getPegAccounting({ - peg_in_amount: 100, - peg_out_amount: 10, - }); - - assert.equal(withoutBurns.pegOutAmount, 100); - assert.equal(withoutBurns.federationAssets, 900); - assert.equal(withoutBurns.circulatingLiabilities, 900); - assert.equal(withoutBurns.assetsVsLiabilitiesRatio, 100); - assert.equal(withoutLiabilities.assetsVsLiabilitiesRatio, null); - assert.equal(invalidAssets.federationAssets, null); - assert.equal(invalidAssets.circulatingLiabilities, null); - assert.equal(invalidAssets.assetsVsLiabilitiesRatio, null); - assert.equal(incompleteStats.federationAssets, 90); - assert.equal(incompleteStats.circulatingLiabilities, null); - assert.equal(incompleteStats.assetsVsLiabilitiesRatio, null); -}); - -test("renders holdings in BTC and aggregate volumes with two decimal places", () => { - const html = render(pegInfo({ - chain_stats: { - peg_in_count: 10, - peg_in_amount: 100_000_000_000, - peg_out_count: 4, - peg_out_amount: 10_000_000_000, - burned_amount: 1_000_000_000, - }, - }, [], { t: l10n.en, feeEst: null })); - - assert.match(html, />900\.00000000 BTC101\.124%<\/p>/); - assert.match(html, /VOLUME IN<\/div>
~1,000\.00
~100\.00 { - const localizedStrings = new Set(); - const t = (parts, ...values) => { - const key = parts.join("%s"); - localizedStrings.add(key); - return parts.reduce( - (result, part, index) => result + part + (values[index] || ""), - "", - ); - }; - const txs = [{ - txid: "a".repeat(64), - vin: [{ is_pegin: true }], - vout: [{ asset: nativeAssetId, value: 600 }], - status: { confirmed: true, block_height: 100, block_time: 1000 }, - }, { - txid: "b".repeat(64), - vin: [], - vout: [{ asset: nativeAssetId, value: 400, pegout: {} }], - status: { confirmed: false }, - }]; - - render(pegInfo({ - chain_stats: { - peg_in_count: 1, - peg_in_amount: 600, - peg_out_count: 1, - peg_out_amount: 400, - burned_amount: 0, - }, - }, txs, { t, feeEst: null })); - - [ - "Proof of Reserves", - "Federation BTC Holdings", - "Confirmed peg-ins minus confirmed peg-outs.", - "Last change on %s", - "Assets vs Liabilities", - "Confirmed federation BTC holdings divided by circulating L-BTC supply.", - "Recent Peg-Ins/Outs", - "PEG-IN", - "PEG-OUT", - "VOLUME IN", - "VOLUME OUT", - "TYPE", - "TXID", - "AMOUNT", - "BLOCK", - "ETA", - "Estimated time until the peg transaction is confirmed.", - "Peg-in", - "Peg-out", - "Confirmed", - "N/A", - ].forEach((message) => assert.ok( - localizedStrings.has(message), - `Missing localized string: ${message}`, - )); -}); - -test("expands a transaction containing both peg directions into two event rows", () => { - const txid = "a".repeat(64); - const html = render(pegInfo({ - chain_stats: { - peg_in_count: 1, - peg_in_amount: 1010, - peg_out_count: 1, - peg_out_amount: 400, - burned_amount: 0, - }, - }, [{ - txid, - vin: [{ is_pegin: true }], - vout: [ - { asset: nativeAssetId, value: 600 }, - { asset: nativeAssetId, value: 400, pegout: {} }, - { asset: nativeAssetId, value: 10, scriptpubkey_type: "fee" }, - ], - status: { confirmed: true, block_height: 100, block_time: 1000 }, - }], { t: l10n.en, feeEst: null })); - - assert.equal((html.match(new RegExp(`href="tx/${txid}"`, "g")) || []).length, 2); - assert.match(html, /Peg-in/); - assert.match(html, /Peg-out/); - assert.match(html, /0\.00001010 BTC/); - assert.match(html, /0\.00000400 BTC/); -}); - -test("subtracts regular native inputs when deriving a peg-in amount", () => { - const html = render(pegInfo({ - chain_stats: { - peg_in_count: 1, - peg_in_amount: 610, - peg_out_count: 0, - peg_out_amount: 0, - burned_amount: 0, - }, - }, [{ - txid: "b".repeat(64), - vin: [ - { is_pegin: true }, - { - is_pegin: false, - prevout: { asset: nativeAssetId, value: 400 }, - }, - ], - vout: [ - { asset: nativeAssetId, value: 1000 }, - { asset: nativeAssetId, value: 10, scriptpubkey_type: "fee" }, - ], - status: { confirmed: true, block_height: 100, block_time: 1000 }, - }], { t: l10n.en, feeEst: null })); - - assert.match(html, /0\.00000610 BTC/); -}); - -test("shows at most four recent peg transactions", () => { - const txs = Array.from({ length: 5 }, (_, index) => ({ - txid: String(index + 1).repeat(64), - vin: [{ is_pegin: true }], - vout: [{ asset: nativeAssetId, value: 100 + index }], - status: { - confirmed: true, - block_height: 100 + index, - block_time: 1000 + index, - }, - })); - const html = render(pegInfo({ - chain_stats: { - peg_in_count: 5, - peg_in_amount: 510, - peg_out_count: 0, - peg_out_amount: 0, - burned_amount: 0, - }, - }, txs, { t: l10n.en, feeEst: null })); - - assert.equal((html.match(/href="tx\//g) || []).length, 4); - assert.doesNotMatch(html, new RegExp(`href="tx/${txs[0].txid}"`)); -}); - -test("keeps the ratio within a parity-centered gauge with headroom", () => { - const html = render(pegInfo({ - chain_stats: { - peg_in_count: 1, - peg_in_amount: 100_000_000_000, - peg_out_count: 1, - peg_out_amount: 50_000_000_000, - burned_amount: 100_000_000, - }, - }, [], { t: l10n.en, feeEst: null })); - - assert.match(html, /99\.7%<\/p>

100\.0%<\/p>

100\.3%/); - assert.doesNotMatch(html, /assets-vs-liabilities-fill" style="width: 100%/); -}); - -test("renders an unavailable state when peg requests fail", () => { - const html = render(pegInfo(null, null, { - t: l10n.en, - feeEst: null, - error: true, - })); - - assert.match(html, /Peg data is currently unavailable\./); - assert.doesNotMatch(html, /loading/); -}); - -test("keeps previous peg data visible when a refresh fails", () => { - const html = render(pegInfo({ - chain_stats: { - peg_in_count: 10, - peg_in_amount: 100_000_000_000, - peg_out_count: 4, - peg_out_amount: 10_000_000_000, - burned_amount: 1_000_000_000, - }, - }, [], { - t: l10n.en, - feeEst: null, - error: true, - })); - - assert.match(html, /Unable to refresh — showing previous data\./); - assert.match(html, />900\.00000000 BTC div { - flex: 1; - min-width: 0; -} - -.peg-info { - display: flex; - flex-direction: column; - gap: var(--page-gap); - margin-top: 12px; -} - -.peg-info-stale { - margin: 0; - padding: 10px 12px; - border: 1px solid rgba(var(--warning-color-rgb), .4); - border-radius: 4px; - background-color: rgba(var(--warning-color-rgb), .1); - color: var(--warning-color); - font-size: 12px; -} - -.peg-info-unavailable { - display: flex; - flex-direction: column; - gap: 12px; - height: 100%; -} - -.federation-btc-holdings { - width: 100%; -} - -.assets-vs-liabilities { - width: 100%; - height: auto; -} - -.assets-vs-liabilities-body { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - margin-top: 13px; - gap: 6px; -} - -.assets-vs-liabilities-scale { - display: flex; - justify-content: space-between; - font-size: 14px; - font-weight: 600; - width: 100%; -} - -.assets-vs-liabilities-bar { - width: 100%; - height: 6px; - border-radius: 9999px; - background-color: var(--surface-secondary-color); -} - -.assets-vs-liabilities-fill { - height: 100%; - background-color: var(--accent-color); - border-radius: 9999px; -} - -.assets-vs-liabilities-ratio { - font-size: 20px; - font-weight: 700; - font-family: "Rigid Square"; -} - -.peg-transaction-table { - display: flex; - flex-direction: column; - gap: 12px; - height: 100%; -} - -.peg-transaction-table-body { - display: flex; - flex-direction: column; - gap: 12px; -} - .asset-page > .transactions { margin-top: 0; } -.peg-transaction-table .info-stats-row { - display: flex; - justify-content: space-between; -} - -.peg-transaction-table .info-stat { - flex: 1; - align-items: center; -} - -.peg-transaction-table-transaction-type { - flex: .75; - color: white; -} - -.peg-transaction-table-transaction-txid, -.peg-transaction-table-transaction-amount { - flex: 1; - color: white; -} - -.peg-transaction-table-transaction-block { - flex: .5; - color: white; -} - -.peg-transaction-table-transaction-eta { - flex: .5; - color: white; - display: flex; - align-items: center; - justify-content: right; - gap: 4px; - text-align: right; -} - -.peg-transaction-table-pegin-badge { - font-weight: 400; -} - -.peg-transaction-eta-badge { - display: flex; - align-items: center; - justify-content: center; - background-color: var(--surface-primary-color); - font-size: 11px; - font-weight: 400; - padding: 2px 8px; - border-radius: var(--max-border-radius); -} - .high-value-assets-table { padding: 24px; border-radius: 12px; @@ -5211,10 +5068,6 @@ a.back-link img{ } @media only screen and (max-width: 1328px) { - .dashboard-transaction-section { - flex-direction: column; - } - .fee-market-body { flex-wrap: wrap; }