TVL stats and Rootstock locked BTC (#1297)
* Handle new TVL property in /api/v2/stats/charts/market endpoint Fixes #1250 * Support stats widget for Rootstock * fix envs validation * fix tests
@@ -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) {
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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` |
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M11 5.5V7h1V5.5h2V7a3 3 0 0 1 2.236 5A3 3 0 0 1 14 17v1.5h-2V17h-1v1.5H9V17H7.5v-2H9V9H7.5V7H9V5.5h2ZM11 9v2h3a1 1 0 1 0 0-2h-3Zm3 4h-3v2h3a1 1 0 1 0 0-2Z" fill="currentColor"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M1 12C1 5.925 5.925 1 12 1s11 4.925 11 11-4.925 11-11 11S1 18.075 1 12Zm11-9a9 9 0 1 0 0 18 9 9 0 0 0 0-18Z" fill="currentColor"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 491 B |
@@ -0,0 +1,5 @@
|
||||
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M16.729 18.75h-8.1a1.35 1.35 0 0 1-1.35-1.35v-4.95a1.35 1.35 0 0 1 1.35-1.35h8.1a1.35 1.35 0 0 1 1.35 1.35v4.95a1.35 1.35 0 0 1-1.35 1.35ZM8.629 12a.45.45 0 0 0-.45.45v4.95a.45.45 0 0 0 .45.45h8.1a.45.45 0 0 0 .45-.45v-4.95a.45.45 0 0 0-.45-.45h-8.1Z" fill="currentColor" stroke="currentColor" stroke-width=".5"/>
|
||||
<path d="M16.279 12h-7.2a.45.45 0 0 1-.45-.45v-2.7a3.6 3.6 0 0 1 3.6-3.6h.9a3.6 3.6 0 0 1 3.6 3.6v2.7a.45.45 0 0 1-.45.45Zm-6.75-.9h6.3V8.85a2.7 2.7 0 0 0-2.7-2.7h-.9a2.7 2.7 0 0 0-2.7 2.7v2.25Zm3.15 4.05a.9.9 0 1 1 0-1.8.9.9 0 0 1 0 1.8Z" fill="currentColor" stroke="currentColor" stroke-width=".5"/>
|
||||
<path d="M13.129 14.7h-.9v1.8h.9v-1.8Z" fill="currentColor" stroke="currentColor" stroke-width=".5"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 813 B |
@@ -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',
|
||||
};
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -7,6 +7,7 @@ export interface ChartMarketItem {
|
||||
date: string;
|
||||
closing_price: string;
|
||||
market_cap?: string;
|
||||
tvl?: string | null;
|
||||
}
|
||||
|
||||
export interface ChartTransactionResponse {
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -1 +1 @@
|
||||
export type ChainIndicatorId = 'daily_txs' | 'coin_price' | 'market_cap';
|
||||
export type ChainIndicatorId = 'daily_txs' | 'coin_price' | 'market_cap' | 'tvl';
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Grid } from '@chakra-ui/react';
|
||||
import BigNumber from 'bignumber.js';
|
||||
import React from 'react';
|
||||
|
||||
import { route } from 'nextjs-routes';
|
||||
@@ -6,10 +7,12 @@ 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 }
|
||||
/>
|
||||
) }
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 20 KiB |
@@ -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;
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 59 KiB |
|
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 59 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 30 KiB |
@@ -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;
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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 });
|
||||
|
||||
@@ -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([
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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([
|
||||
|
||||
@@ -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 },
|
||||
|
||||