Skip to content

Commit

Permalink
TVL stats and Rootstock locked BTC (#1297)
Browse files Browse the repository at this point in the history
* Handle new TVL property in /api/v2/stats/charts/market endpoint

Fixes #1250

* Support stats widget for Rootstock

* fix envs validation

* fix tests
tom2drum authored Oct 18, 2023
1 parent 4b42b71 commit 7335000
Showing 29 changed files with 89 additions and 18 deletions.
2 changes: 1 addition & 1 deletion deploy/tools/envs-validator/index.ts
Original file line number Diff line number Diff line change
@@ -64,7 +64,7 @@ async function validateEnvs(appEnvs: Record<string, string>) {

async function getExternalJsonContent(envName: string): Promise<string | void> {
return new Promise((resolve, reject) => {
const fileName = `./public${ buildExternalAssetFilePath(envName, '.json') }`;
const fileName = `./public${ buildExternalAssetFilePath(envName, 'https://foo.bar/baz.json') }`;

fs.readFile(path.resolve(__dirname, fileName), 'utf8', (err, data) => {
if (err) {
2 changes: 1 addition & 1 deletion deploy/tools/envs-validator/schema.ts
Original file line number Diff line number Diff line change
@@ -339,7 +339,7 @@ const schema = yup
.array()
.transform(replaceQuotes)
.json()
.of(yup.string<ChainIndicatorId>().oneOf([ 'daily_txs', 'coin_price', 'market_cap' ])),
.of(yup.string<ChainIndicatorId>().oneOf([ 'daily_txs', 'coin_price', 'market_cap', 'tvl' ])),
NEXT_PUBLIC_HOMEPAGE_PLATE_TEXT_COLOR: yup.string(),
NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND: yup.string(),
NEXT_PUBLIC_HOMEPAGE_SHOW_GAS_TRACKER: yup.boolean(),
2 changes: 1 addition & 1 deletion docs/ENVS.md
Original file line number Diff line number Diff line change
@@ -92,7 +92,7 @@ The app instance could be customized by passing following variables to NodeJS en

| Variable | Type| Description | Compulsoriness | Default value | Example value |
| --- | --- | --- | --- | --- | --- |
| NEXT_PUBLIC_HOMEPAGE_CHARTS | `Array<'daily_txs' \| 'coin_price' \| 'market_cap'>` | List of charts displayed on the home page | - | - | `['daily_txs','coin_price','market_cap']` |
| NEXT_PUBLIC_HOMEPAGE_CHARTS | `Array<'daily_txs' \| 'coin_price' \| 'market_cap' \| 'tvl'>` | List of charts displayed on the home page | - | - | `['daily_txs','coin_price','market_cap']` |
| NEXT_PUBLIC_HOMEPAGE_PLATE_TEXT_COLOR | `string` | Text color of the hero plate on the homepage (escape "#" symbol if you use HEX color codes or use rgba-value instead) | - | `white` | `\#DCFE76` |
| NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND | `string` | Background css value for hero plate on the homepage (escape "#" symbol if you use HEX color codes or use rgba-value instead) | - | `radial-gradient(103.03% 103.03% at 0% 0%, rgba(183, 148, 244, 0.8) 0%, rgba(0, 163, 196, 0.8) 100%), var(--chakra-colors-blue-400)` | `radial-gradient(at 15% 86%, hsla(350,65%,70%,1) 0px, transparent 50%)` \| `no-repeat bottom 20% right 0px/100% url(https://placekitten/1400/200)` |
| NEXT_PUBLIC_HOMEPAGE_SHOW_GAS_TRACKER | `boolean` | Set to false if network doesn't have gas tracker | - | `true` | `false` |
4 changes: 4 additions & 0 deletions icons/coins/bitcoin.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions icons/lock.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions mocks/stats/index.ts
Original file line number Diff line number Diff line change
@@ -17,4 +17,10 @@ export const base: HomeStats = {
total_gas_used: '0',
total_transactions: '82258122',
transactions_today: '26815',
tvl: '1767425.102766552',
};

export const withBtcLocked: HomeStats = {
...base,
rootstock_locked_btc: '3337493406696977561374',
};
1 change: 1 addition & 0 deletions stubs/stats.ts
Original file line number Diff line number Diff line change
@@ -17,6 +17,7 @@ export const HOMEPAGE_STATS: HomeStats = {
total_gas_used: '0',
total_transactions: '193823272',
transactions_today: '0',
tvl: '1767425.102766552',
};

export const STATS_CHARTS_SECTION: StatsChartsSection = {
1 change: 1 addition & 0 deletions types/api/charts.ts
Original file line number Diff line number Diff line change
@@ -7,6 +7,7 @@ export interface ChartMarketItem {
date: string;
closing_price: string;
market_cap?: string;
tvl?: string | null;
}

export interface ChartTransactionResponse {
2 changes: 2 additions & 0 deletions types/api/stats.ts
Original file line number Diff line number Diff line change
@@ -11,6 +11,8 @@ export type HomeStats = {
static_gas_price: string | null;
market_cap: string;
network_utilization_percentage: number;
tvl: string | null;
rootstock_locked_btc?: string | null;
}

export type GasPrices = {
2 changes: 1 addition & 1 deletion types/homepage.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export type ChainIndicatorId = 'daily_txs' | 'coin_price' | 'market_cap';
export type ChainIndicatorId = 'daily_txs' | 'coin_price' | 'market_cap' | 'tvl';
2 changes: 1 addition & 1 deletion ui/home/Stats.pw.tsx
Original file line number Diff line number Diff line change
@@ -18,7 +18,7 @@ test.describe('all items', () => {
test.beforeEach(async({ page, mount }) => {
await page.route(API_URL, (route) => route.fulfill({
status: 200,
body: JSON.stringify(statsMock.base),
body: JSON.stringify(statsMock.withBtcLocked),
}));

component = await mount(
13 changes: 13 additions & 0 deletions ui/home/Stats.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
import { Grid } from '@chakra-ui/react';
import BigNumber from 'bignumber.js';
import React from 'react';

import { route } from 'nextjs-routes';

import config from 'configs/app';
import blockIcon from 'icons/block.svg';
import clockIcon from 'icons/clock-light.svg';
import bitcoinIcon from 'icons/coins/bitcoin.svg';
import gasIcon from 'icons/gas.svg';
import txIcon from 'icons/transactions.svg';
import walletIcon from 'icons/wallet.svg';
import useApiQuery from 'lib/api/useApiQuery';
import { WEI } from 'lib/consts';
import { HOMEPAGE_STATS } from 'stubs/stats';

import StatsGasPrices from './StatsGasPrices';
@@ -39,6 +42,7 @@ const Stats = () => {

if (data) {
!data.gas_prices && itemsCount--;
data.rootstock_locked_btc && itemsCount++;
const isOdd = Boolean(itemsCount % 2);
const gasLabel = hasGasTracker && data.gas_prices ? <StatsGasPrices gasPrices={ data.gas_prices }/> : null;

@@ -83,6 +87,15 @@ const Stats = () => {
isLoading={ isPlaceholderData }
/>
) }
{ data.rootstock_locked_btc && (
<StatsItem
icon={ bitcoinIcon }
title="BTC Locked in 2WP"
value={ `${ BigNumber(data.rootstock_locked_btc).div(WEI).dp(0).toFormat() } RBTC` }
_last={ isOdd ? lastItemTouchStyle : undefined }
isLoading={ isPlaceholderData }
/>
) }
</>
);
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 9 additions & 1 deletion ui/home/indicators/ChainIndicators.pw.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { test, expect } from '@playwright/experimental-ct-react';
import { test as base, expect } from '@playwright/experimental-ct-react';
import type { Locator } from '@playwright/test';
import React from 'react';

import * as dailyTxsMock from 'mocks/stats/daily_txs';
import * as statsMock from 'mocks/stats/index';
import contextWithEnvs from 'playwright/fixtures/contextWithEnvs';
import TestApp from 'playwright/TestApp';
import buildApiUrl from 'playwright/utils/buildApiUrl';

@@ -12,6 +13,13 @@ import ChainIndicators from './ChainIndicators';
const STATS_API_URL = buildApiUrl('homepage_stats');
const TX_CHART_API_URL = buildApiUrl('homepage_chart_txs');

const test = base.extend({
context: contextWithEnvs([
{ name: 'NEXT_PUBLIC_HOMEPAGE_CHARTS', value: '["daily_txs","coin_price","market_cap","tvl"]' },
// eslint-disable-next-line @typescript-eslint/no-explicit-any
]) as any,
});

test.describe('daily txs chart', () => {
let component: Locator;

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
25 changes: 25 additions & 0 deletions ui/home/indicators/utils/indicators.tsx
Original file line number Diff line number Diff line change
@@ -5,6 +5,7 @@ import type { TChainIndicator } from '../types';

import config from 'configs/app';
import globeIcon from 'icons/globe.svg';
import lockIcon from 'icons/lock.svg';
import txIcon from 'icons/transactions.svg';
import { sortByDateDesc } from 'ui/shared/chart/utils/sorts';
import * as TokenEntity from 'ui/shared/entities/token/TokenEntity';
@@ -76,10 +77,34 @@ const marketPriceIndicator: TChainIndicator<'homepage_chart_market'> = {
},
};

const tvlIndicator: TChainIndicator<'homepage_chart_market'> = {
id: 'tvl',
title: 'Total value locked',
value: (stats) => '$' + Number(stats.tvl).toLocaleString(undefined, { maximumFractionDigits: 2, notation: 'compact' }),
icon: <Icon as={ lockIcon } boxSize={ 6 } bgColor="#517FDB" borderRadius="base" color="white"/>,
// eslint-disable-next-line max-len
hint: 'Total value of digital assets locked or staked in a chain',
api: {
resourceName: 'homepage_chart_market',
dataFn: (response) => ([ {
items: response.chart_data
.map((item) => (
{
date: new Date(item.date),
value: item.tvl ? Number(item.tvl) : 0,
}))
.sort(sortByDateDesc),
name: 'TVL',
valueFormatter: (x: number) => '$' + x.toLocaleString(undefined, { maximumFractionDigits: 2, notation: 'compact' }),
} ]),
},
};

const INDICATORS = [
dailyTxsIndicator,
coinPriceIndicator,
marketPriceIndicator,
tvlIndicator,
];

export default INDICATORS;
3 changes: 2 additions & 1 deletion ui/pages/SearchResults.pw.tsx
Original file line number Diff line number Diff line change
@@ -6,6 +6,7 @@ import { apps as appsMock } from 'mocks/apps/apps';
import * as searchMock from 'mocks/search/index';
import contextWithEnvs from 'playwright/fixtures/contextWithEnvs';
import TestApp from 'playwright/TestApp';
import * as app from 'playwright/utils/app';
import buildApiUrl from 'playwright/utils/buildApiUrl';
import * as configs from 'playwright/utils/configs';
import LayoutMainColumn from 'ui/shared/layout/components/MainColumn';
@@ -182,7 +183,7 @@ test('search by tx hash +@mobile', async({ mount, page }) => {
});

test.describe('with apps', () => {
const MARKETPLACE_CONFIG_URL = buildExternalAssetFilePath('NEXT_PUBLIC_MARKETPLACE_CONFIG_URL', 'https://marketplace-config.json') || '';
const MARKETPLACE_CONFIG_URL = app.url + buildExternalAssetFilePath('NEXT_PUBLIC_MARKETPLACE_CONFIG_URL', 'https://marketplace-config.json') || '';
const extendedTest = test.extend({
context: contextWithEnvs([
{ name: 'NEXT_PUBLIC_MARKETPLACE_CONFIG_URL', value: MARKETPLACE_CONFIG_URL },
3 changes: 2 additions & 1 deletion ui/snippets/footer/Footer.pw.tsx
Original file line number Diff line number Diff line change
@@ -6,12 +6,13 @@ import { buildExternalAssetFilePath } from 'configs/app/utils';
import { FOOTER_LINKS } from 'mocks/config/footerLinks';
import contextWithEnvs from 'playwright/fixtures/contextWithEnvs';
import TestApp from 'playwright/TestApp';
import * as app from 'playwright/utils/app';
import buildApiUrl from 'playwright/utils/buildApiUrl';
import * as configs from 'playwright/utils/configs';

import Footer from './Footer';

const FOOTER_LINKS_URL = buildExternalAssetFilePath('NEXT_PUBLIC_FOOTER_LINKS', 'https://localhost:3000/footer-links.json') || '';
const FOOTER_LINKS_URL = app.url + buildExternalAssetFilePath('NEXT_PUBLIC_FOOTER_LINKS', 'https://localhost:3000/footer-links.json') || '';

const BACKEND_VERSION_API_URL = buildApiUrl('config_backend_version');
const INDEXING_ALERT_API_URL = buildApiUrl('homepage_indexing_status');
3 changes: 2 additions & 1 deletion ui/snippets/header/Burger.pw.tsx
Original file line number Diff line number Diff line change
@@ -6,10 +6,11 @@ import { FEATURED_NETWORKS_MOCK } from 'mocks/config/network';
import authFixture from 'playwright/fixtures/auth';
import contextWithEnvs, { createContextWithEnvs } from 'playwright/fixtures/contextWithEnvs';
import TestApp from 'playwright/TestApp';
import * as app from 'playwright/utils/app';

import Burger from './Burger';

const FEATURED_NETWORKS_URL = buildExternalAssetFilePath('NEXT_PUBLIC_FEATURED_NETWORKS', 'https://localhost:3000/featured-networks.json') || '';
const FEATURED_NETWORKS_URL = app.url + buildExternalAssetFilePath('NEXT_PUBLIC_FEATURED_NETWORKS', 'https://localhost:3000/featured-networks.json') || '';
const LOGO_URL = 'https://localhost:3000/my-logo.png';

base.use({ viewport: devices['iPhone 13 Pro'].viewport });
2 changes: 1 addition & 1 deletion ui/snippets/navigation/NavigationDesktop.pw.tsx
Original file line number Diff line number Diff line change
@@ -21,7 +21,7 @@ const hooksConfig = {
},
};

const FEATURED_NETWORKS_URL = buildExternalAssetFilePath('NEXT_PUBLIC_FEATURED_NETWORKS', 'https://localhost:3000/featured-networks.json') || '';
const FEATURED_NETWORKS_URL = app.url + buildExternalAssetFilePath('NEXT_PUBLIC_FEATURED_NETWORKS', 'https://localhost:3000/config.json') || '';

const test = base.extend({
context: contextWithEnvs([
13 changes: 7 additions & 6 deletions ui/snippets/networkMenu/NetworkLogo.pw.tsx
Original file line number Diff line number Diff line change
@@ -5,6 +5,7 @@ import React from 'react';
import { buildExternalAssetFilePath } from 'configs/app/utils';
import contextWithEnvs from 'playwright/fixtures/contextWithEnvs';
import TestApp from 'playwright/TestApp';
import * as app from 'playwright/utils/app';
import * as configs from 'playwright/utils/configs';

import NetworkLogo from './NetworkLogo';
@@ -44,8 +45,8 @@ base.describe('placeholder logo', () => {
});

base.describe('custom logo', () => {
const LOGO_URL = buildExternalAssetFilePath('NEXT_PUBLIC_NETWORK_LOGO', 'https://localhost:3000/my-logo.png') || '';
const ICON_URL = buildExternalAssetFilePath('NEXT_PUBLIC_NETWORK_ICON', 'https://localhost:3000/my-icon.png') || '';
const LOGO_URL = app.url + buildExternalAssetFilePath('NEXT_PUBLIC_NETWORK_LOGO', 'https://localhost:3000/my-logo.png') || '';
const ICON_URL = app.url + buildExternalAssetFilePath('NEXT_PUBLIC_NETWORK_ICON', 'https://localhost:3000/my-icon.png') || '';
const test = base.extend({
context: contextWithEnvs([
{ name: 'NEXT_PUBLIC_NETWORK_LOGO', value: LOGO_URL },
@@ -91,10 +92,10 @@ base.describe('custom logo', () => {
});

base.describe('custom logo with dark option -@default +@dark-mode', () => {
const LOGO_URL = buildExternalAssetFilePath('NEXT_PUBLIC_NETWORK_LOGO', 'https://localhost:3000/my-logo.png') || '';
const LOGO_URL_DARK = buildExternalAssetFilePath('NEXT_PUBLIC_NETWORK_LOGO_DARK', 'https://localhost:3000/my-logo.png') || '';
const ICON_URL = buildExternalAssetFilePath('NEXT_PUBLIC_NETWORK_ICON', 'https://localhost:3000/my-icon.png') || '';
const ICON_URL_DARK = buildExternalAssetFilePath('NEXT_PUBLIC_NETWORK_ICON_DARK', 'https://localhost:3000/my-icon.png') || '';
const LOGO_URL = app.url + buildExternalAssetFilePath('NEXT_PUBLIC_NETWORK_LOGO', 'https://localhost:3000/my-logo.png') || '';
const LOGO_URL_DARK = app.url + buildExternalAssetFilePath('NEXT_PUBLIC_NETWORK_LOGO_DARK', 'https://localhost:3000/my-logo.png') || '';
const ICON_URL = app.url + buildExternalAssetFilePath('NEXT_PUBLIC_NETWORK_ICON', 'https://localhost:3000/my-icon.png') || '';
const ICON_URL_DARK = app.url + buildExternalAssetFilePath('NEXT_PUBLIC_NETWORK_ICON_DARK', 'https://localhost:3000/my-icon.png') || '';
const test = base.extend({
context: contextWithEnvs([
{ name: 'NEXT_PUBLIC_NETWORK_LOGO', value: LOGO_URL },
3 changes: 2 additions & 1 deletion ui/snippets/networkMenu/NetworkMenu.pw.tsx
Original file line number Diff line number Diff line change
@@ -5,10 +5,11 @@ import { buildExternalAssetFilePath } from 'configs/app/utils';
import { FEATURED_NETWORKS_MOCK } from 'mocks/config/network';
import contextWithEnvs from 'playwright/fixtures/contextWithEnvs';
import TestApp from 'playwright/TestApp';
import * as app from 'playwright/utils/app';

import NetworkMenu from './NetworkMenu';

const FEATURED_NETWORKS_URL = buildExternalAssetFilePath('NEXT_PUBLIC_FEATURED_NETWORKS', 'https://localhost:3000/featured-networks.json') || '';
const FEATURED_NETWORKS_URL = app.url + buildExternalAssetFilePath('NEXT_PUBLIC_FEATURED_NETWORKS', 'https://localhost:3000/featured-networks.json') || '';

const extendedTest = test.extend({
context: contextWithEnvs([
3 changes: 2 additions & 1 deletion ui/snippets/searchBar/SearchBar.pw.tsx
Original file line number Diff line number Diff line change
@@ -8,6 +8,7 @@ import { apps as appsMock } from 'mocks/apps/apps';
import * as searchMock from 'mocks/search/index';
import contextWithEnvs from 'playwright/fixtures/contextWithEnvs';
import TestApp from 'playwright/TestApp';
import * as app from 'playwright/utils/app';
import buildApiUrl from 'playwright/utils/buildApiUrl';

import SearchBar from './SearchBar';
@@ -275,7 +276,7 @@ test('recent keywords suggest +@mobile', async({ mount, page }) => {
});

base.describe('with apps', () => {
const MARKETPLACE_CONFIG_URL = buildExternalAssetFilePath('NEXT_PUBLIC_MARKETPLACE_CONFIG_URL', 'https://marketplace-config.json') || '';
const MARKETPLACE_CONFIG_URL = app.url + buildExternalAssetFilePath('NEXT_PUBLIC_MARKETPLACE_CONFIG_URL', 'https://marketplace-config.json') || '';
const test = base.extend({
context: contextWithEnvs([
{ name: 'NEXT_PUBLIC_MARKETPLACE_CONFIG_URL', value: MARKETPLACE_CONFIG_URL },

0 comments on commit 7335000

Please sign in to comment.