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
  • Loading branch information
tom2drum authored Oct 18, 2023
1 parent 4b42b71 commit 7335000
Show file tree
Hide file tree
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
Expand Up @@ -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) {
Expand Down
2 changes: 1 addition & 1 deletion deploy/tools/envs-validator/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down
2 changes: 1 addition & 1 deletion docs/ENVS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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` |
Expand Down
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
Expand Up @@ -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
Expand Up @@ -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 = {
Expand Down
1 change: 1 addition & 0 deletions types/api/charts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export interface ChartMarketItem {
date: string;
closing_price: string;
market_cap?: string;
tvl?: string | null;
}

export interface ChartTransactionResponse {
Expand Down
2 changes: 2 additions & 0 deletions types/api/stats.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down
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
Expand Up @@ -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(
Expand Down
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';
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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 }
/>
) }
</>
);
}
Expand Down
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';

Expand All @@ -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;

Expand Down
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
Expand Up @@ -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';
Expand Down Expand Up @@ -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
Expand Up @@ -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';
Expand Down Expand Up @@ -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 },
Expand Down
3 changes: 2 additions & 1 deletion ui/snippets/footer/Footer.pw.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down
3 changes: 2 additions & 1 deletion ui/snippets/header/Burger.pw.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 });
Expand Down
2 changes: 1 addition & 1 deletion ui/snippets/navigation/NavigationDesktop.pw.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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([
Expand Down
13 changes: 7 additions & 6 deletions ui/snippets/networkMenu/NetworkLogo.pw.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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 },
Expand Down Expand Up @@ -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 },
Expand Down
3 changes: 2 additions & 1 deletion ui/snippets/networkMenu/NetworkMenu.pw.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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([
Expand Down
3 changes: 2 additions & 1 deletion ui/snippets/searchBar/SearchBar.pw.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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 },
Expand Down

0 comments on commit 7335000

Please sign in to comment.