chore: delete unused liquidity-provision-dashboard project
This commit is contained in:
parent
1b8892c445
commit
9150b8e811
@ -1,11 +0,0 @@
|
||||
{
|
||||
"presets": [
|
||||
[
|
||||
"@nx/react/babel",
|
||||
{
|
||||
"runtime": "automatic"
|
||||
}
|
||||
]
|
||||
],
|
||||
"plugins": []
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
# This file is used by:
|
||||
# 1. autoprefixer to adjust CSS to support the below specified browsers
|
||||
# 2. babel preset-env to adjust included polyfills
|
||||
#
|
||||
# For additional information regarding the format and rule options, please see:
|
||||
# https://github.com/browserslist/browserslist#queries
|
||||
#
|
||||
# If you need to support different browsers in production, you may tweak the list below.
|
||||
|
||||
last 1 Chrome version
|
||||
last 1 Firefox version
|
||||
last 2 Edge major versions
|
||||
last 2 Safari major version
|
||||
last 2 iOS major versions
|
||||
Firefox ESR
|
||||
not IE 9-11 # For IE 9-11 support, remove 'not'.
|
@ -1,28 +0,0 @@
|
||||
# React Environment Variables
|
||||
# https://facebook.github.io/create-react-app/docs/adding-custom-environment-variables#expanding-environment-variables-in-env
|
||||
|
||||
# Netlify Environment Variables
|
||||
# https://www.netlify.com/docs/continuous-deployment/#environment-variables
|
||||
NX_VERSION=\$npm_package_version
|
||||
NX_REPOSITORY_URL=\$REPOSITORY_URL
|
||||
NX_BRANCH=\$BRANCH
|
||||
NX_PULL_REQUEST=\$PULL_REQUEST
|
||||
NX_HEAD=\$HEAD
|
||||
NX_COMMIT_REF=\$COMMIT_REF
|
||||
NX_CONTEXT=\$CONTEXT
|
||||
NX_REVIEW_ID=\$REVIEW_ID
|
||||
NX_INCOMING_HOOK_TITLE=\$INCOMING_HOOK_TITLE
|
||||
NX_INCOMING_HOOK_URL=\$INCOMING_HOOK_URL
|
||||
NX_INCOMING_HOOK_BODY=\$INCOMING_HOOK_BODY
|
||||
NX_URL=\$URL
|
||||
NX_DEPLOY_URL=\$DEPLOY_URL
|
||||
NX_DEPLOY_PRIME_URL=\$DEPLOY_PRIME_URL
|
||||
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/fairground/vegawallet-fairground.toml
|
||||
NX_VEGA_ENV = 'TESTNET'
|
||||
NX_VEGA_URL="https://api.n07.testnet.vega.xyz/graphql"
|
||||
NX_VEGA_WALLET_URL=http://localhost:1789
|
||||
NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
|
||||
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
|
||||
NX_VEGA_NETWORKS={\"TESTNET\":\"https://console.fairground.wtf\",\"STAGNET1\":\"https://trading.stagnet1.vega.rocks\"}
|
||||
NX_VEGA_EXPLORER_URL=https://explorer.fairground.wtf
|
||||
NX_VEGA_CONSOLE_URL=https://console.fairground.wtf
|
@ -1,3 +0,0 @@
|
||||
# App configuration variables
|
||||
NX_VEGA_URL=http://localhost:3008/graphql
|
||||
NX_VEGA_ENV=LOCAL
|
@ -1,8 +0,0 @@
|
||||
# App configuration variables
|
||||
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/devnet1/vegawallet-devnet1.toml
|
||||
NX_VEGA_URL=https://api.n04.d.vega.xyz/graphql
|
||||
NX_VEGA_ENV=DEVNET
|
||||
NX_VEGA_NETWORKS={\"TESTNET\":\"https://console.fairground.wtf\",\"STAGNET1\":\"https://trading.stagnet1.vega.rocks\"}
|
||||
NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
|
||||
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
|
||||
NX_VEGA_EXPLORER_URL=#
|
@ -1,9 +0,0 @@
|
||||
# App configuration variables
|
||||
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks/master/mainnet1/mainnet1.toml
|
||||
NX_VEGA_URL=https://api.vega.community/graphql
|
||||
NX_VEGA_ENV=MAINNET
|
||||
NX_VEGA_NETWORKS={\"TESTNET\":\"https://console.fairground.wtf\"}
|
||||
NX_ETHEREUM_PROVIDER_URL=https://mainnet.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
|
||||
NX_ETHERSCAN_URL=https://etherscan.io
|
||||
NX_VEGA_EXPLORER_URL=https://explorer.vega.xyz
|
||||
NX_VEGA_CONSOLE_URL=https://console.vega.xyz
|
@ -1,9 +0,0 @@
|
||||
# App configuration variables
|
||||
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/stagnet1/vegawallet-stagnet1.toml
|
||||
NX_VEGA_URL=https://api.n00.stagnet1.vega.xyz/graphql
|
||||
NX_VEGA_ENV=STAGNET1
|
||||
NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
|
||||
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
|
||||
NX_VEGA_EXPLORER_URL=https://explorer.stagnet1.vega.rocks
|
||||
NX_VEGA_NETWORKS={\"TESTNET\":\"https://console.fairground.wtf\",\"STAGNET1\":\"https://trading.stagnet1.vega.rocks\"}
|
||||
|
@ -1,9 +0,0 @@
|
||||
# App configuration variables
|
||||
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/fairground/vegawallet-fairground.toml
|
||||
NX_VEGA_URL=https://api.n07.testnet.vega.xyz/graphql
|
||||
NX_VEGA_ENV=TESTNET
|
||||
NX_VEGA_NETWORKS={\"TESTNET\":\"https://console.fairground.wtf\"}
|
||||
NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
|
||||
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
|
||||
NX_VEGA_EXPLORER_URL=https://explorer.fairground.wtf
|
||||
NX_VEGA_CONSOLE_URL=https://console.fairground.wtf
|
@ -1,18 +0,0 @@
|
||||
{
|
||||
"extends": ["plugin:@nx/react", "../../.eslintrc.json"],
|
||||
"ignorePatterns": ["!**/*", "__generated__"],
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
|
||||
"rules": {}
|
||||
},
|
||||
{
|
||||
"files": ["*.ts", "*.tsx"],
|
||||
"rules": {}
|
||||
},
|
||||
{
|
||||
"files": ["*.js", "*.jsx"],
|
||||
"rules": {}
|
||||
}
|
||||
]
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
/* eslint-disable */
|
||||
export default {
|
||||
displayName: 'liquidity-provision-dashboard',
|
||||
preset: '../../jest.preset.js',
|
||||
transform: {
|
||||
'^(?!.*\\.(js|jsx|ts|tsx|css|json)$)': '@nx/react/plugins/jest',
|
||||
'^.+\\.[tj]sx?$': ['babel-jest', { presets: ['@nx/next/babel'] }],
|
||||
},
|
||||
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
|
||||
coverageDirectory: '../../coverage/apps/liquidity-provision-dashboard',
|
||||
};
|
@ -1,10 +0,0 @@
|
||||
const { join } = require('path');
|
||||
|
||||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {
|
||||
config: join(__dirname, 'tailwind.config.js'),
|
||||
},
|
||||
autoprefixer: {},
|
||||
},
|
||||
};
|
@ -1,94 +0,0 @@
|
||||
{
|
||||
"name": "liquidity-provision-dashboard",
|
||||
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
||||
"sourceRoot": "apps/liquidity-provision-dashboard/src",
|
||||
"projectType": "application",
|
||||
"targets": {
|
||||
"build": {
|
||||
"executor": "@nx/webpack:webpack",
|
||||
"outputs": ["{options.outputPath}"],
|
||||
"defaultConfiguration": "production",
|
||||
"options": {
|
||||
"compiler": "babel",
|
||||
"outputPath": "dist/apps/liquidity-provision-dashboard",
|
||||
"index": "apps/liquidity-provision-dashboard/src/index.html",
|
||||
"baseHref": "/",
|
||||
"main": "apps/liquidity-provision-dashboard/src/main.tsx",
|
||||
"polyfills": "apps/liquidity-provision-dashboard/src/polyfills.ts",
|
||||
"tsConfig": "apps/liquidity-provision-dashboard/tsconfig.app.json",
|
||||
"assets": [
|
||||
"apps/liquidity-provision-dashboard/src/favicon.ico",
|
||||
"apps/liquidity-provision-dashboard/src/assets"
|
||||
],
|
||||
"styles": ["apps/liquidity-provision-dashboard/src/styles.scss"],
|
||||
"scripts": [],
|
||||
"webpackConfig": "@nx/react/plugins/webpack"
|
||||
},
|
||||
"configurations": {
|
||||
"development": {
|
||||
"extractLicenses": false,
|
||||
"optimization": false,
|
||||
"sourceMap": true,
|
||||
"vendorChunk": true
|
||||
},
|
||||
"production": {
|
||||
"fileReplacements": [
|
||||
{
|
||||
"replace": "apps/liquidity-provision-dashboard/src/environments/environment.ts",
|
||||
"with": "apps/liquidity-provision-dashboard/src/environments/environment.prod.ts"
|
||||
}
|
||||
],
|
||||
"optimization": true,
|
||||
"outputHashing": "all",
|
||||
"sourceMap": false,
|
||||
"namedChunks": false,
|
||||
"extractLicenses": true,
|
||||
"vendorChunk": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"serve": {
|
||||
"executor": "@nx/webpack:dev-server",
|
||||
"options": {
|
||||
"buildTarget": "liquidity-provision-dashboard:build",
|
||||
"hmr": true,
|
||||
"port": 4201
|
||||
},
|
||||
"configurations": {
|
||||
"development": {
|
||||
"buildTarget": "liquidity-provision-dashboard:build:development"
|
||||
},
|
||||
"production": {
|
||||
"buildTarget": "liquidity-provision-dashboard:build:production",
|
||||
"hmr": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"executor": "@nx/eslint:lint",
|
||||
"outputs": ["{options.outputFile}"],
|
||||
"options": {
|
||||
"lintFilePatterns": [
|
||||
"apps/liquidity-provision-dashboard/**/*.{ts,tsx,js,jsx}"
|
||||
]
|
||||
}
|
||||
},
|
||||
"test": {
|
||||
"executor": "@nx/jest:jest",
|
||||
"outputs": [
|
||||
"{workspaceRoot}/coverage/apps/liquidity-provision-dashboard"
|
||||
],
|
||||
"options": {
|
||||
"jestConfig": "apps/liquidity-provision-dashboard/jest.config.ts"
|
||||
}
|
||||
},
|
||||
"build-spec": {
|
||||
"executor": "nx:run-commands",
|
||||
"outputs": [],
|
||||
"options": {
|
||||
"command": "yarn tsc --project ./apps/liquidity-provision-dashboard/tsconfig.spec.json"
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": []
|
||||
}
|
@ -1,47 +0,0 @@
|
||||
import type { InMemoryCacheConfig } from '@apollo/client';
|
||||
import { NetworkLoader, useInitializeEnv } from '@vegaprotocol/environment';
|
||||
import { useRoutes } from 'react-router-dom';
|
||||
|
||||
import '../styles.scss';
|
||||
import { Navbar } from './components/navbar';
|
||||
|
||||
import { routerConfig } from './routes/router-config';
|
||||
|
||||
const cache: InMemoryCacheConfig = {
|
||||
typePolicies: {
|
||||
Market: {
|
||||
merge: true,
|
||||
},
|
||||
Party: {
|
||||
merge: true,
|
||||
},
|
||||
Query: {},
|
||||
Account: {
|
||||
keyFields: false,
|
||||
fields: {
|
||||
balanceFormatted: {},
|
||||
},
|
||||
},
|
||||
Node: {
|
||||
keyFields: false,
|
||||
},
|
||||
Instrument: {
|
||||
keyFields: false,
|
||||
},
|
||||
},
|
||||
};
|
||||
const AppRouter = () => useRoutes(routerConfig);
|
||||
|
||||
export function App() {
|
||||
useInitializeEnv();
|
||||
return (
|
||||
<NetworkLoader cache={cache}>
|
||||
<div className="max-h-full min-h-full bg-white">
|
||||
<Navbar />
|
||||
<AppRouter />
|
||||
</div>
|
||||
</NetworkLoader>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
@ -1,25 +0,0 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
|
||||
import { Intro } from './intro';
|
||||
import { MarketList } from './market-list';
|
||||
|
||||
export function Dashboard() {
|
||||
return (
|
||||
<>
|
||||
<div className="px-16 pt-20 pb-12 bg-greys-light-100">
|
||||
<div className="max-w-screen-xl mx-auto">
|
||||
<h1 className="font-alpha calt uppercase text-5xl mb-8">
|
||||
{t('Top liquidity opportunities')}
|
||||
</h1>
|
||||
|
||||
<Intro />
|
||||
</div>
|
||||
</div>
|
||||
<div className="px-16 py-6">
|
||||
<div className="max-w-screen-xl mx-auto">
|
||||
<MarketList />
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
@ -1 +0,0 @@
|
||||
export * from './dashboard';
|
@ -1 +0,0 @@
|
||||
export * from './intro';
|
@ -1,53 +0,0 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { ExternalLink } from '@vegaprotocol/ui-toolkit';
|
||||
|
||||
// TODO: add mainnet links once docs have been updated
|
||||
const LINKS = {
|
||||
testnet: [
|
||||
{
|
||||
label: 'Learn about liquidity fees',
|
||||
url: 'https://docs.vega.xyz/testnet/tutorials/providing-liquidity#resources',
|
||||
},
|
||||
{
|
||||
label: 'Provide liquidity',
|
||||
url: 'https://docs.vega.xyz/testnet/tutorials/providing-liquidity#overview',
|
||||
},
|
||||
{
|
||||
label: 'View your liquidity provisions',
|
||||
url: 'https://docs.vega.xyz/testnet/tutorials/providing-liquidity#viewing-existing-liquidity-provisions',
|
||||
},
|
||||
{
|
||||
label: 'Amend or remove liquidity',
|
||||
url: 'https://docs.vega.xyz/testnet/tutorials/providing-liquidity#amending-a-liquidity-commitment',
|
||||
},
|
||||
],
|
||||
mainnet: [],
|
||||
};
|
||||
|
||||
// TODO: update this when network switcher is added
|
||||
type Network = 'testnet' | 'mainnet';
|
||||
|
||||
export const Intro = ({ network = 'testnet' }: { network?: Network }) => {
|
||||
return (
|
||||
<div>
|
||||
<p className="font-alpha calt text-2xl font-medium mb-2">
|
||||
{t(
|
||||
'Become a liquidity provider and earn a cut of the fees paid during trading.'
|
||||
)}
|
||||
</p>
|
||||
<div>
|
||||
<ul className="flex flex-wrap">
|
||||
{LINKS[network].map(
|
||||
({ label, url }: { label: string; url: string }) => (
|
||||
<li key={url} className="mr-6">
|
||||
<ExternalLink href={url} rel="noreferrer">
|
||||
{t(label)}
|
||||
</ExternalLink>
|
||||
</li>
|
||||
)
|
||||
)}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
@ -1 +0,0 @@
|
||||
export * from './market-list';
|
@ -1,296 +0,0 @@
|
||||
import { DApp, useLinks } from '@vegaprotocol/environment';
|
||||
import { type Market } from '@vegaprotocol/liquidity';
|
||||
import {
|
||||
displayChange,
|
||||
formatWithAsset,
|
||||
useMarketsLiquidity,
|
||||
} from '@vegaprotocol/liquidity';
|
||||
import {
|
||||
addDecimalsFormatNumber,
|
||||
formatNumberPercentage,
|
||||
getExpiryDate,
|
||||
toBigNum,
|
||||
} from '@vegaprotocol/utils';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { type VegaValueFormatterParams } from '@vegaprotocol/datagrid';
|
||||
import { PriceChangeCell } from '@vegaprotocol/datagrid';
|
||||
import type * as Schema from '@vegaprotocol/types';
|
||||
import {
|
||||
AsyncRenderer,
|
||||
Icon,
|
||||
HealthBar,
|
||||
TooltipCellComponent,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import {
|
||||
type GetRowIdParams,
|
||||
type RowClickedEvent,
|
||||
type ColDef,
|
||||
} from 'ag-grid-community';
|
||||
import 'ag-grid-community/styles/ag-grid.css';
|
||||
import 'ag-grid-community/styles/ag-theme-alpine.css';
|
||||
import { useCallback, useState, useMemo } from 'react';
|
||||
|
||||
import { Grid } from '../../grid';
|
||||
import { HealthDialog } from '../../health-dialog';
|
||||
import { Status } from '../../status';
|
||||
import { intentForStatus } from '../../../lib/utils';
|
||||
import { formatDistanceToNow } from 'date-fns';
|
||||
import { getAsset } from '@vegaprotocol/markets';
|
||||
|
||||
export const MarketList = () => {
|
||||
const { data, error, loading } = useMarketsLiquidity();
|
||||
const [isHealthDialogOpen, setIsHealthDialogOpen] = useState(false);
|
||||
const consoleLink = useLinks(DApp.Console);
|
||||
|
||||
const getRowId = useCallback(({ data }: GetRowIdParams) => data.id, []);
|
||||
const columnDefs = useMemo<ColDef[]>(
|
||||
() => [
|
||||
{
|
||||
headerName: t('Market (futures)'),
|
||||
field: 'tradableInstrument.instrument.name',
|
||||
cellRenderer: ({ value, data }: { value: string; data: Market }) => {
|
||||
return (
|
||||
<>
|
||||
<span className="leading-3">{value}</span>
|
||||
<span className="leading-3">{getAsset(data).symbol}</span>
|
||||
</>
|
||||
);
|
||||
},
|
||||
minWidth: 100,
|
||||
flex: 1,
|
||||
headerTooltip: t('The market name and settlement asset'),
|
||||
},
|
||||
|
||||
{
|
||||
headerName: t('Market Code'),
|
||||
headerTooltip: t(
|
||||
'The market code is a unique identifier for this market'
|
||||
),
|
||||
field: 'tradableInstrument.instrument.code',
|
||||
},
|
||||
|
||||
{
|
||||
headerName: t('Type'),
|
||||
headerTooltip: t('Type'),
|
||||
field: 'tradableInstrument.instrument.product.__typename',
|
||||
},
|
||||
|
||||
{
|
||||
headerName: t('Last Price'),
|
||||
headerTooltip: t('Latest price for this market'),
|
||||
field: 'data.markPrice',
|
||||
valueFormatter: ({
|
||||
value,
|
||||
data,
|
||||
}: VegaValueFormatterParams<Market, 'data.markPrice'>) =>
|
||||
value && data ? formatWithAsset(value, getAsset(data)) : '-',
|
||||
},
|
||||
|
||||
{
|
||||
headerName: t('Change (24h)'),
|
||||
headerTooltip: t('Change in price over the last 24h'),
|
||||
cellRenderer: ({
|
||||
data,
|
||||
}: VegaValueFormatterParams<Market, 'data.candles'>) => {
|
||||
if (data && data.candles) {
|
||||
const prices = data.candles.map((candle) => candle.close);
|
||||
return (
|
||||
<PriceChangeCell
|
||||
candles={prices}
|
||||
decimalPlaces={data?.decimalPlaces}
|
||||
/>
|
||||
);
|
||||
} else return <div>{t('-')}</div>;
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
headerName: t('Volume (24h)'),
|
||||
field: 'dayVolume',
|
||||
valueFormatter: ({
|
||||
value,
|
||||
data,
|
||||
}: VegaValueFormatterParams<Market, 'dayVolume'>) =>
|
||||
value && data
|
||||
? `${addDecimalsFormatNumber(
|
||||
value,
|
||||
getAsset(data).decimals || 0
|
||||
)} (${displayChange(data.volumeChange)})`
|
||||
: '-',
|
||||
headerTooltip: t('The trade volume over the last 24h'),
|
||||
},
|
||||
|
||||
{
|
||||
headerName: t('Total staked by LPs'),
|
||||
field: 'liquidityCommitted',
|
||||
valueFormatter: ({
|
||||
value,
|
||||
data,
|
||||
}: VegaValueFormatterParams<Market, 'liquidityCommitted'>) =>
|
||||
data && value
|
||||
? formatWithAsset(value.toString(), getAsset(data))
|
||||
: '-',
|
||||
headerTooltip: t('The amount of funds allocated to provide liquidity'),
|
||||
},
|
||||
|
||||
{
|
||||
headerName: t('Target stake'),
|
||||
field: 'target',
|
||||
valueFormatter: ({
|
||||
value,
|
||||
data,
|
||||
}: VegaValueFormatterParams<Market, 'target'>) =>
|
||||
data && value ? formatWithAsset(value, getAsset(data)) : '-',
|
||||
headerTooltip: t(
|
||||
'The ideal committed liquidity to operate the market. If total commitment currently below this level then LPs can set the fee level with new commitment.'
|
||||
),
|
||||
},
|
||||
|
||||
{
|
||||
headerName: t('% Target stake met'),
|
||||
valueFormatter: ({ data }: VegaValueFormatterParams<Market, ''>) => {
|
||||
if (data) {
|
||||
const roundedPercentage =
|
||||
parseInt(
|
||||
(data.liquidityCommitted / parseFloat(data.target)).toFixed(0)
|
||||
) * 100;
|
||||
const display = Number.isNaN(roundedPercentage)
|
||||
? 'N/A'
|
||||
: formatNumberPercentage(toBigNum(roundedPercentage, 0), 0);
|
||||
return display;
|
||||
} else return '-';
|
||||
},
|
||||
headerTooltip: t('% Target stake met'),
|
||||
},
|
||||
|
||||
{
|
||||
headerName: t('Fee levels'),
|
||||
field: 'fees',
|
||||
valueFormatter: ({ value }: VegaValueFormatterParams<Market, 'fees'>) =>
|
||||
value ? `${value.factors.liquidityFee}%` : '-',
|
||||
headerTooltip: t('Fee level for this market'),
|
||||
},
|
||||
|
||||
{
|
||||
headerName: t('Status'),
|
||||
field: 'tradingMode',
|
||||
cellRenderer: ({
|
||||
value,
|
||||
data,
|
||||
}: {
|
||||
value: Schema.MarketTradingMode;
|
||||
data: Market;
|
||||
}) => {
|
||||
return <Status trigger={data.data?.trigger} tradingMode={value} />;
|
||||
},
|
||||
headerTooltip: t(
|
||||
'The current market status - those below the target stake mark are most in need of liquidity'
|
||||
),
|
||||
},
|
||||
|
||||
{
|
||||
headerComponent: () => {
|
||||
return (
|
||||
<div>
|
||||
<span>{t('Health')}</span>{' '}
|
||||
<button
|
||||
onClick={() => setIsHealthDialogOpen(true)}
|
||||
aria-label={t('open tooltip')}
|
||||
>
|
||||
<Icon name="info-sign" />
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
field: 'tradingMode',
|
||||
cellRenderer: ({
|
||||
value,
|
||||
data,
|
||||
}: {
|
||||
value: Schema.MarketTradingMode;
|
||||
data: Market;
|
||||
}) => (
|
||||
<HealthBar
|
||||
target={data.target}
|
||||
decimals={getAsset(data).decimals || 0}
|
||||
levels={data.feeLevels}
|
||||
intent={intentForStatus(value)}
|
||||
/>
|
||||
),
|
||||
sortable: false,
|
||||
cellStyle: { overflow: 'unset' },
|
||||
},
|
||||
{
|
||||
headerName: t('Age'),
|
||||
field: 'marketTimestamps.open',
|
||||
headerTooltip: t('Age of the market'),
|
||||
valueFormatter: ({
|
||||
value,
|
||||
}: VegaValueFormatterParams<Market, 'marketTimestamps.open'>) => {
|
||||
return value ? formatDistanceToNow(new Date(value)) : '-';
|
||||
},
|
||||
},
|
||||
{
|
||||
headerName: t('Closing Time'),
|
||||
field: 'tradableInstrument.instrument.metadata.tags',
|
||||
headerTooltip: t('Closing time of the market'),
|
||||
valueFormatter: ({ data }: VegaValueFormatterParams<Market, ''>) => {
|
||||
let expiry;
|
||||
if (data?.tradableInstrument.instrument.metadata.tags) {
|
||||
expiry = getExpiryDate(
|
||||
data?.tradableInstrument.instrument.metadata.tags,
|
||||
data?.marketTimestamps.close,
|
||||
data?.state
|
||||
);
|
||||
}
|
||||
return expiry ? expiry : '-';
|
||||
},
|
||||
},
|
||||
],
|
||||
[]
|
||||
);
|
||||
|
||||
return (
|
||||
<AsyncRenderer loading={loading} error={error} data={data}>
|
||||
<div
|
||||
className="w-full grow"
|
||||
style={{ minHeight: 500, overflow: 'hidden' }}
|
||||
>
|
||||
<Grid
|
||||
gridOptions={{
|
||||
onRowClicked: ({ data }: RowClickedEvent) => {
|
||||
window.open(
|
||||
liquidityDetailsConsoleLink(data.id, consoleLink),
|
||||
'_blank',
|
||||
'noopener,noreferrer'
|
||||
);
|
||||
},
|
||||
}}
|
||||
rowData={data}
|
||||
defaultColDef={{
|
||||
resizable: true,
|
||||
sortable: true,
|
||||
unSortIcon: true,
|
||||
cellClass: ['flex', 'flex-col', 'justify-center'],
|
||||
tooltipComponent: TooltipCellComponent,
|
||||
}}
|
||||
columnDefs={columnDefs}
|
||||
getRowId={getRowId}
|
||||
isRowClickable
|
||||
tooltipShowDelay={500}
|
||||
/>
|
||||
<HealthDialog
|
||||
isOpen={isHealthDialogOpen}
|
||||
onChange={() => {
|
||||
setIsHealthDialogOpen(!isHealthDialogOpen);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</AsyncRenderer>
|
||||
);
|
||||
};
|
||||
|
||||
const liquidityDetailsConsoleLink = (
|
||||
marketId: string,
|
||||
consoleLink: (url: string | undefined) => string
|
||||
) => consoleLink(`/#/liquidity/${marketId}`);
|
@ -1,103 +0,0 @@
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { makeDerivedDataProvider } from '@vegaprotocol/data-provider';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { useDataProvider } from '@vegaprotocol/data-provider';
|
||||
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
|
||||
|
||||
import {
|
||||
getFeeLevels,
|
||||
sumLiquidityCommitted,
|
||||
lpAggregatedDataProvider,
|
||||
} from '@vegaprotocol/liquidity';
|
||||
import { getAsset, marketWithDataProvider } from '@vegaprotocol/markets';
|
||||
import type { MarketWithData } from '@vegaprotocol/markets';
|
||||
|
||||
import { Market } from './market';
|
||||
import { Header } from './header';
|
||||
import { LPProvidersGrid } from './providers';
|
||||
|
||||
const formatMarket = (market: MarketWithData) => {
|
||||
return {
|
||||
name: market?.tradableInstrument.instrument.name,
|
||||
symbol: getAsset(market).symbol,
|
||||
settlementAsset: getAsset(market),
|
||||
targetStake: market?.data?.targetStake,
|
||||
tradingMode: market?.data?.marketTradingMode,
|
||||
trigger: market?.data?.trigger,
|
||||
};
|
||||
};
|
||||
|
||||
export const lpDataProvider = makeDerivedDataProvider(
|
||||
[marketWithDataProvider, lpAggregatedDataProvider],
|
||||
([market, lpAggregatedData]) => ({
|
||||
market: { ...formatMarket(market) },
|
||||
liquidityProviders: lpAggregatedData || [],
|
||||
})
|
||||
);
|
||||
|
||||
const useMarketDetails = (marketId: string | undefined) => {
|
||||
const { data, loading, error } = useDataProvider({
|
||||
dataProvider: lpDataProvider,
|
||||
skipUpdates: true,
|
||||
variables: { marketId: marketId || '' },
|
||||
});
|
||||
|
||||
const liquidityProviders = data?.liquidityProviders || [];
|
||||
|
||||
return {
|
||||
data: {
|
||||
name: data?.market?.name,
|
||||
symbol: data?.market?.symbol,
|
||||
liquidityProviders: liquidityProviders,
|
||||
feeLevels: getFeeLevels(liquidityProviders),
|
||||
comittedLiquidity: sumLiquidityCommitted(liquidityProviders) || 0,
|
||||
settlementAsset: data?.market?.settlementAsset || {},
|
||||
targetStake: data?.market?.targetStake || '0',
|
||||
tradingMode: data?.market.tradingMode,
|
||||
},
|
||||
error,
|
||||
loading: loading,
|
||||
};
|
||||
};
|
||||
|
||||
type Params = { marketId: string };
|
||||
|
||||
export const Detail = () => {
|
||||
const { marketId } = useParams<Params>();
|
||||
const { data, loading, error } = useMarketDetails(marketId);
|
||||
|
||||
return (
|
||||
<AsyncRenderer loading={loading} error={error} data={data}>
|
||||
<div className="bg-greys-light-100 px-16 pb-12 pt-14">
|
||||
<div className="mx-auto max-w-screen-xl">
|
||||
<Header name={data.name} symbol={data.symbol} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="px-16">
|
||||
<div className="mx-auto max-w-screen-xl">
|
||||
<div className="py-12">
|
||||
{marketId && (
|
||||
<Market
|
||||
marketId={marketId}
|
||||
feeLevels={data.feeLevels}
|
||||
comittedLiquidity={data.comittedLiquidity}
|
||||
settlementAsset={data.settlementAsset}
|
||||
targetStake={data.targetStake}
|
||||
tradingMode={data.tradingMode}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<h2 className="font-alpha calt mb-4 text-2xl">
|
||||
{t('Current Liquidity Provision')}
|
||||
</h2>
|
||||
<LPProvidersGrid
|
||||
liquidityProviders={data.liquidityProviders}
|
||||
settlementAsset={data.settlementAsset}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</AsyncRenderer>
|
||||
);
|
||||
};
|
@ -1,26 +0,0 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Icon } from '@vegaprotocol/ui-toolkit';
|
||||
|
||||
export const Header = ({
|
||||
name,
|
||||
symbol,
|
||||
}: {
|
||||
name?: string;
|
||||
symbol?: string;
|
||||
}) => {
|
||||
return (
|
||||
<div>
|
||||
<div className="mb-6">
|
||||
<Link to="/">
|
||||
<Icon name="chevron-left" className="mr-2" />
|
||||
<span className="underline font-alpha calt text-lg font-medium">
|
||||
{t('Liquidity opportunities')}
|
||||
</span>
|
||||
</Link>
|
||||
</div>
|
||||
<h1 className="font-alpha calt text-5xl mb-6">{name}</h1>
|
||||
<p className="font-alpha calt text-4xl">{symbol}</p>
|
||||
</div>
|
||||
);
|
||||
};
|
@ -1 +0,0 @@
|
||||
export * from './header';
|
@ -1 +0,0 @@
|
||||
export * from './detail';
|
@ -1 +0,0 @@
|
||||
export * from './last-24h-volume';
|
@ -1,108 +0,0 @@
|
||||
import { useState, useMemo, useRef, useCallback } from 'react';
|
||||
import throttle from 'lodash/throttle';
|
||||
import { addDecimalsFormatNumber } from '@vegaprotocol/utils';
|
||||
import { useYesterday } from '@vegaprotocol/react-helpers';
|
||||
import { useDataProvider } from '@vegaprotocol/data-provider';
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
import {
|
||||
calcDayVolume,
|
||||
getChange,
|
||||
displayChange,
|
||||
} from '@vegaprotocol/liquidity';
|
||||
|
||||
import type { Candle } from '@vegaprotocol/markets';
|
||||
import { marketCandlesProvider } from '@vegaprotocol/markets';
|
||||
|
||||
const THROTTLE_UPDATE_TIME = 500;
|
||||
|
||||
export const Last24hVolume = ({
|
||||
marketId,
|
||||
decimals,
|
||||
}: {
|
||||
marketId: string;
|
||||
decimals: number;
|
||||
}) => {
|
||||
const [candleVolume, setCandleVolume] = useState<string>();
|
||||
const [volumeChange, setVolumeChange] = useState<string>(' - ');
|
||||
|
||||
const yesterday = useYesterday();
|
||||
|
||||
const yTimestamp = useMemo(() => {
|
||||
return new Date(yesterday).toISOString();
|
||||
}, [yesterday]);
|
||||
|
||||
const variables = useMemo(
|
||||
() => ({
|
||||
marketId: marketId,
|
||||
interval: Schema.Interval.INTERVAL_I1H,
|
||||
since: yTimestamp,
|
||||
}),
|
||||
[marketId, yTimestamp]
|
||||
);
|
||||
|
||||
const variables24hAgo = {
|
||||
marketId: marketId,
|
||||
interval: Schema.Interval.INTERVAL_I1D,
|
||||
since: yTimestamp,
|
||||
};
|
||||
|
||||
const throttledSetCandles = useRef(
|
||||
throttle((data: Candle[]) => {
|
||||
setCandleVolume(calcDayVolume(data));
|
||||
}, THROTTLE_UPDATE_TIME)
|
||||
).current;
|
||||
|
||||
const update = useCallback(
|
||||
({ data }: { data: Candle[] | null }) => {
|
||||
if (data) {
|
||||
throttledSetCandles(data);
|
||||
}
|
||||
return true;
|
||||
},
|
||||
[throttledSetCandles]
|
||||
);
|
||||
|
||||
const { data, error } = useDataProvider({
|
||||
dataProvider: marketCandlesProvider,
|
||||
variables: variables,
|
||||
update,
|
||||
skip: !marketId,
|
||||
});
|
||||
|
||||
const throttledSetVolumeChange = useRef(
|
||||
throttle((candles: Candle[]) => {
|
||||
const candle24hAgo = candles?.[0];
|
||||
setVolumeChange(getChange(data || [], candle24hAgo?.close));
|
||||
}, THROTTLE_UPDATE_TIME)
|
||||
).current;
|
||||
|
||||
const updateCandle24hAgo = useCallback(
|
||||
({ data }: { data: Candle[] | null }) => {
|
||||
if (data) {
|
||||
throttledSetVolumeChange(data);
|
||||
}
|
||||
return true;
|
||||
},
|
||||
[throttledSetVolumeChange]
|
||||
);
|
||||
|
||||
useDataProvider({
|
||||
dataProvider: marketCandlesProvider,
|
||||
update: updateCandle24hAgo,
|
||||
variables: variables24hAgo,
|
||||
skip: !marketId || !data,
|
||||
});
|
||||
|
||||
return (
|
||||
<div>
|
||||
<span className="text-3xl">
|
||||
{!error && candleVolume
|
||||
? addDecimalsFormatNumber(candleVolume, decimals)
|
||||
: '0'}{' '}
|
||||
</span>
|
||||
<span className="text-lg text-greys-light-400">
|
||||
({displayChange(volumeChange)})
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
};
|
@ -1 +0,0 @@
|
||||
export * from './market';
|
@ -1,118 +0,0 @@
|
||||
import { useState } from 'react';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { Icon, HealthBar } from '@vegaprotocol/ui-toolkit';
|
||||
import { formatWithAsset } from '@vegaprotocol/liquidity';
|
||||
|
||||
import type * as Schema from '@vegaprotocol/types';
|
||||
import { HealthDialog } from '../../health-dialog';
|
||||
import { Last24hVolume } from '../last-24h-volume';
|
||||
import { Status } from '../../status';
|
||||
import { intentForStatus } from '../../../lib/utils';
|
||||
|
||||
interface Levels {
|
||||
fee: string;
|
||||
commitmentAmount: number;
|
||||
}
|
||||
|
||||
interface settlementAsset {
|
||||
symbol?: string;
|
||||
decimals?: number;
|
||||
}
|
||||
|
||||
export const Market = ({
|
||||
marketId,
|
||||
feeLevels,
|
||||
comittedLiquidity,
|
||||
settlementAsset,
|
||||
targetStake,
|
||||
tradingMode,
|
||||
trigger,
|
||||
}: {
|
||||
marketId: string;
|
||||
feeLevels: Levels[];
|
||||
comittedLiquidity: number;
|
||||
targetStake: string;
|
||||
settlementAsset?: settlementAsset;
|
||||
tradingMode?: Schema.MarketTradingMode;
|
||||
trigger?: Schema.AuctionTrigger;
|
||||
}) => {
|
||||
const [isHealthDialogOpen, setIsHealthDialogOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="border border-greys-light-200 rounded-2xl px-2 py-6">
|
||||
<table className="w-full">
|
||||
<thead>
|
||||
<tr
|
||||
className="text-sm text-greys-light-400 text-left font-alpha calt"
|
||||
style={{ fontFeatureSettings: "'liga' off, 'calt' off" }}
|
||||
>
|
||||
<th className="font-medium px-4">{t('Volume (24h)')}</th>
|
||||
<th className="font-medium px-4">{t('Commited Liquidity')}</th>
|
||||
<th className="font-medium px-4">{t('Status')}</th>
|
||||
<th className="font-medium flex items-center px-4">
|
||||
<span>{t('Health')}</span>{' '}
|
||||
<button
|
||||
onClick={() => setIsHealthDialogOpen(true)}
|
||||
aria-label={t('open tooltip')}
|
||||
className="flex ml-1"
|
||||
>
|
||||
<Icon name="info-sign" />
|
||||
</button>
|
||||
</th>
|
||||
<th className="font-medium">{t('Est. APY')}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td className="px-4">
|
||||
<div>
|
||||
{marketId && settlementAsset?.decimals && (
|
||||
<Last24hVolume
|
||||
marketId={marketId}
|
||||
decimals={settlementAsset.decimals}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-4">
|
||||
<span className="text-3xl">
|
||||
{comittedLiquidity && settlementAsset
|
||||
? formatWithAsset(`${comittedLiquidity}`, settlementAsset)
|
||||
: '0'}
|
||||
</span>
|
||||
</td>
|
||||
<td className="px-4">
|
||||
<Status
|
||||
trigger={trigger}
|
||||
tradingMode={tradingMode}
|
||||
size="large"
|
||||
/>
|
||||
</td>
|
||||
<td className="px-4">
|
||||
{tradingMode && settlementAsset?.decimals && feeLevels && (
|
||||
<HealthBar
|
||||
target={targetStake}
|
||||
decimals={settlementAsset.decimals}
|
||||
levels={feeLevels}
|
||||
intent={intentForStatus(tradingMode)}
|
||||
/>
|
||||
)}
|
||||
</td>
|
||||
<td className="px-4">
|
||||
<span className="text-3xl"></span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<HealthDialog
|
||||
isOpen={isHealthDialogOpen}
|
||||
onChange={() => {
|
||||
setIsHealthDialogOpen(!isHealthDialogOpen);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
@ -1 +0,0 @@
|
||||
export * from './providers';
|
@ -1,125 +0,0 @@
|
||||
import { useCallback, useMemo } from 'react';
|
||||
|
||||
import { type GetRowIdParams, type ColDef } from 'ag-grid-community';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
|
||||
import {
|
||||
type LiquidityProviderFeeShareFieldsFragment,
|
||||
type LiquidityProvisionFieldsFragment,
|
||||
} from '@vegaprotocol/liquidity';
|
||||
import { formatWithAsset } from '@vegaprotocol/liquidity';
|
||||
|
||||
import { Grid } from '../../grid';
|
||||
import { TooltipCellComponent } from '@vegaprotocol/ui-toolkit';
|
||||
|
||||
const formatToHours = ({ value }: { value?: string | null }) => {
|
||||
if (!value) {
|
||||
return '-';
|
||||
}
|
||||
|
||||
const MS_IN_HOUR = 1000 * 60 * 60;
|
||||
const created = new Date(value).getTime();
|
||||
const now = new Date().getTime();
|
||||
return `${Math.round(Math.abs(now - created) / MS_IN_HOUR)}h`;
|
||||
};
|
||||
|
||||
export const LPProvidersGrid = ({
|
||||
liquidityProviders,
|
||||
settlementAsset,
|
||||
}: {
|
||||
liquidityProviders: LiquidityProvisionFieldsFragment &
|
||||
LiquidityProviderFeeShareFieldsFragment[];
|
||||
settlementAsset: {
|
||||
decimals?: number;
|
||||
symbol?: string;
|
||||
};
|
||||
}) => {
|
||||
const getRowId = useCallback(({ data }: GetRowIdParams) => data.party.id, []);
|
||||
const columnDefs = useMemo<ColDef[]>(
|
||||
() => [
|
||||
{
|
||||
headerName: t('LPs'),
|
||||
field: 'party.id',
|
||||
flex: 1,
|
||||
minWidth: 100,
|
||||
headerTooltip: t('Liquidity providers'),
|
||||
},
|
||||
{
|
||||
headerName: t('Duration'),
|
||||
valueFormatter: formatToHours,
|
||||
field: 'createdAt',
|
||||
headerTooltip: t('Time in market'),
|
||||
},
|
||||
{
|
||||
headerName: t('Equity-like share'),
|
||||
field: 'equityLikeShare',
|
||||
valueFormatter: ({ value }: { value?: string | null }) => {
|
||||
return value
|
||||
? `${parseFloat(parseFloat(value).toFixed(2)) * 100}%`
|
||||
: '';
|
||||
},
|
||||
headerTooltip: t(
|
||||
'The share of the markets liquidity held - the earlier you commit liquidity the greater % fees you earn'
|
||||
),
|
||||
minWidth: 140,
|
||||
},
|
||||
{
|
||||
headerName: t('committed bond'),
|
||||
field: 'commitmentAmount',
|
||||
valueFormatter: ({ value }: { value?: string | null }) =>
|
||||
value ? formatWithAsset(value, settlementAsset) : '0',
|
||||
headerTooltip: t('The amount of funds allocated to provide liquidity'),
|
||||
minWidth: 140,
|
||||
},
|
||||
{
|
||||
headerName: t('Margin Req.'),
|
||||
field: 'margin',
|
||||
headerTooltip: t(
|
||||
'Margin required for arising positions based on liquidity commitment'
|
||||
),
|
||||
},
|
||||
{
|
||||
headerName: t('24h Fees'),
|
||||
field: 'fees',
|
||||
headerTooltip: t(
|
||||
'Total fees earned by the liquidity provider in the last 24 hours'
|
||||
),
|
||||
},
|
||||
{
|
||||
headerName: t('Fee level'),
|
||||
valueFormatter: ({ value }: { value?: string | null }) => `${value}%`,
|
||||
field: 'fee',
|
||||
headerTooltip: t(
|
||||
"The market's liquidity fee, or the percentage of a trade's value which is collected from the price taker for every trade"
|
||||
),
|
||||
},
|
||||
|
||||
{
|
||||
headerName: t('APY'),
|
||||
field: 'apy',
|
||||
headerTooltip: t(
|
||||
'An annualised estimate based on the total liquidity provision fees and maker fees collected by liquidity providers, the maximum margin needed and maximum commitment (bond) over the course of 7 epochs'
|
||||
),
|
||||
},
|
||||
],
|
||||
[settlementAsset]
|
||||
);
|
||||
|
||||
return (
|
||||
<Grid
|
||||
rowData={liquidityProviders}
|
||||
tooltipShowDelay={500}
|
||||
defaultColDef={{
|
||||
resizable: true,
|
||||
sortable: true,
|
||||
unSortIcon: true,
|
||||
cellClass: ['flex', 'flex-col', 'justify-center'],
|
||||
tooltipComponent: TooltipCellComponent,
|
||||
minWidth: 100,
|
||||
}}
|
||||
columnDefs={columnDefs}
|
||||
getRowId={getRowId}
|
||||
rowHeight={92}
|
||||
/>
|
||||
);
|
||||
};
|
@ -1,50 +0,0 @@
|
||||
.ag-theme-alpine {
|
||||
--ag-line-height: 24px;
|
||||
--ag-row-hover-color: transparent;
|
||||
--ag-header-background-color: transparent;
|
||||
--ag-odd-row-background-color: transparent;
|
||||
--ag-header-foreground-color: #626262;
|
||||
--ag-secondary-foreground-color: #626262;
|
||||
--ag-font-size: 16px;
|
||||
--ag-background-color: transparent;
|
||||
--ag-range-selection-border-color: transparent;
|
||||
|
||||
font-family: AlphaLyrae, Helvetica Neue, -apple-system, BlinkMacSystemFont,
|
||||
Segoe UI, Roboto, Arial, Noto Sans, sans-serif, Apple Color Emoji,
|
||||
Segoe UI Emoji, Segoe UI Symbol, Noto Color Emoji;
|
||||
font-feature-settings: 'liga' off, 'calt' off;
|
||||
}
|
||||
|
||||
.ag-theme-alpine .ag-cell {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.ag-theme-alpine .ag-header {
|
||||
border-bottom: 1px solid #a7a7a7;
|
||||
font-size: 15px;
|
||||
line-height: 1em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.ag-theme-alpine .ag-root-wrapper {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.ag-theme-alpine .ag-header-row {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.ag-theme-alpine .ag-row {
|
||||
border: none;
|
||||
border-bottom: 1px solid #bfccd6;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.ag-theme-alpine .ag-root-wrapper-body.ag-layout-normal {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.ag-theme-alpine.row-hover .ag-row:hover {
|
||||
background: #f0f0f0;
|
||||
cursor: pointer;
|
||||
}
|
@ -1,47 +0,0 @@
|
||||
import { useRef, useCallback, useEffect } from 'react';
|
||||
import { AgGridReact } from 'ag-grid-react';
|
||||
import {
|
||||
type AgGridReactProps,
|
||||
type AgReactUiProps,
|
||||
type AgGridReact as AgGridReactType,
|
||||
} from 'ag-grid-react';
|
||||
import classNames from 'classnames';
|
||||
import 'ag-grid-community/styles/ag-grid.css';
|
||||
import 'ag-grid-community/styles/ag-theme-alpine.css';
|
||||
|
||||
import './grid.scss';
|
||||
|
||||
type Props = (AgGridReactProps | AgReactUiProps) & {
|
||||
isRowClickable?: boolean;
|
||||
style?: React.CSSProperties;
|
||||
};
|
||||
|
||||
export const Grid = ({ isRowClickable, ...props }: Props) => {
|
||||
const gridRef = useRef<AgGridReactType | null>(null);
|
||||
|
||||
const resizeGrid = useCallback(() => {
|
||||
gridRef.current?.api?.sizeColumnsToFit();
|
||||
}, [gridRef]);
|
||||
|
||||
const handleOnGridReady = useCallback(() => {
|
||||
resizeGrid();
|
||||
}, [resizeGrid]);
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener('resize', resizeGrid);
|
||||
return () => window.removeEventListener('resize', resizeGrid);
|
||||
}, [resizeGrid]);
|
||||
|
||||
return (
|
||||
<AgGridReact
|
||||
className={classNames('ag-theme-alpine font-alpha calt h-full', {
|
||||
'row-hover': isRowClickable,
|
||||
})}
|
||||
rowHeight={92}
|
||||
ref={gridRef}
|
||||
onGridReady={handleOnGridReady}
|
||||
suppressRowClickSelection
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
};
|
@ -1 +0,0 @@
|
||||
export * from './grid';
|
@ -1,110 +0,0 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { Dialog, HealthBar } from '@vegaprotocol/ui-toolkit';
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
import classNames from 'classnames';
|
||||
import { intentForStatus } from '../../lib/utils';
|
||||
|
||||
interface HealthDialogProps {
|
||||
isOpen: boolean;
|
||||
onChange: (isOpen: boolean) => void;
|
||||
}
|
||||
|
||||
const ROWS = [
|
||||
{
|
||||
key: '1',
|
||||
title: 'Continuous',
|
||||
copy: 'Markets that have committed liquidity equal or greater than the target stake are trading continuously.',
|
||||
data: {
|
||||
status: Schema.MarketTradingMode.TRADING_MODE_CONTINUOUS,
|
||||
target: '171320',
|
||||
decimals: 5,
|
||||
levels: [
|
||||
{ fee: '0.6', commitmentAmount: 150000 },
|
||||
{ fee: '1', commitmentAmount: 150000 },
|
||||
{ fee: '2', commitmentAmount: 30000 },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
key: '2',
|
||||
title: 'Monitoring auction (liquidity)',
|
||||
copy: 'Markets below the target stake will see trading suspended and go into liquidity auction.',
|
||||
data: {
|
||||
status: Schema.MarketTradingMode.TRADING_MODE_MONITORING_AUCTION,
|
||||
target: '171320',
|
||||
decimals: 5,
|
||||
levels: [
|
||||
{ fee: '0.6', commitmentAmount: 110000 },
|
||||
{ fee: '1', commitmentAmount: 50000 },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
key: '3',
|
||||
title: 'Opening auction',
|
||||
copy: 'A newly created market looking for a target liquidity amount to start trading.',
|
||||
data: {
|
||||
status: Schema.MarketTradingMode.TRADING_MODE_OPENING_AUCTION,
|
||||
target: '171320',
|
||||
decimals: 3,
|
||||
levels: [
|
||||
{ fee: '0.6', commitmentAmount: 110000 },
|
||||
{ fee: '1', commitmentAmount: 50000 },
|
||||
],
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export const HealthDialog = ({ onChange, isOpen }: HealthDialogProps) => {
|
||||
return (
|
||||
<Dialog size="large" open={isOpen} onChange={onChange}>
|
||||
<h1 className="text-2xl mb-5 pr-2 font-medium font-alpha uppercase">
|
||||
{t('Health')}
|
||||
</h1>
|
||||
<p className="text-lg font-medium font-alpha mb-8">
|
||||
{t(
|
||||
'Market health is a representation of market and liquidity status and how close that market is to moving from one fee level to another.'
|
||||
)}
|
||||
</p>
|
||||
|
||||
<table className="table-fixed">
|
||||
<thead className="border-b border-greys-light-300">
|
||||
<th className="w-1/2 text-left font-medium font-alpha text-base pb-4 uppercase">
|
||||
{t('Market status')}
|
||||
</th>
|
||||
<th className="w-1/2 text-lef font-medium font-alpha text-base pb-4 uppercase">
|
||||
{t('Liquidity status')}
|
||||
</th>
|
||||
</thead>
|
||||
<tbody>
|
||||
{ROWS.map((r, index) => {
|
||||
const isFirstRow = index === 0;
|
||||
return (
|
||||
<tr key={r.key}>
|
||||
<td
|
||||
className={classNames('pr-4 pb-10', { 'pt-8': isFirstRow })}
|
||||
>
|
||||
<h2 className="font-medium font-alpha uppercase text-base">
|
||||
{t(r.title)}
|
||||
</h2>
|
||||
<p className="font-medium font-alpha text-lg">{t(r.copy)}</p>
|
||||
</td>
|
||||
<td
|
||||
className={classNames('pl-4 pb-10', { 'pt-8': isFirstRow })}
|
||||
>
|
||||
<HealthBar
|
||||
size="large"
|
||||
levels={r.data.levels}
|
||||
target={r.data.target}
|
||||
decimals={r.data.decimals}
|
||||
intent={intentForStatus(r.data.status)}
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
@ -1 +0,0 @@
|
||||
export * from './health-dialog';
|
@ -1 +0,0 @@
|
||||
export * from './indicator';
|
@ -1,24 +0,0 @@
|
||||
import type * as Schema from '@vegaprotocol/types';
|
||||
|
||||
import { getColorForStatus } from '../../lib/utils';
|
||||
|
||||
export const Indicator = ({
|
||||
status,
|
||||
opacity,
|
||||
}: {
|
||||
status?: Schema.MarketTradingMode;
|
||||
opacity?: number;
|
||||
}) => {
|
||||
const backgroundColor = status ? getColorForStatus(status) : undefined;
|
||||
return (
|
||||
<div className="inline-block w-2 h-2 mr-1 rounded-full bg-white overflow-hidden shrink-0">
|
||||
<div
|
||||
className="h-full bg-black"
|
||||
style={{
|
||||
opacity,
|
||||
backgroundColor,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
@ -1,35 +0,0 @@
|
||||
.ag-theme-alpine {
|
||||
--ag-line-height: 24px;
|
||||
--ag-row-hover-color: transparent;
|
||||
--ag-header-background-color: #f5f5f5;
|
||||
--ag-odd-row-background-color: transparent;
|
||||
--ag-header-foreground-color: #000;
|
||||
--ag-secondary-foreground-color: #fff;
|
||||
--ag-font-family: 'Helvetica Neue';
|
||||
--ag-font-size: 12px;
|
||||
|
||||
font-family: 'Helvetica Neue', -apple-system, BlinkMacSystemFont, 'Segoe UI',
|
||||
Roboto, Oxygen-Sans, Ubuntu, Cantarell, 'Helvetica Neue', sans-serif;
|
||||
}
|
||||
|
||||
.ag-theme-alpine .ag-cell {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.ag-theme-alpine .ag-header {
|
||||
border: 1px solid #bfccd6;
|
||||
}
|
||||
|
||||
.ag-theme-alpine .ag-root-wrapper {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.ag-theme-alpine .ag-row {
|
||||
border: none;
|
||||
border-bottom: 1px solid #bfccd6;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.ag-theme-alpine .ag-root-wrapper-body.ag-layout-normal {
|
||||
height: auto;
|
||||
}
|
@ -1 +0,0 @@
|
||||
export * from './navbar';
|
@ -1,15 +0,0 @@
|
||||
import { Link } from 'react-router-dom';
|
||||
import { VegaLogo } from '@vegaprotocol/ui-toolkit';
|
||||
|
||||
export const Navbar = () => {
|
||||
return (
|
||||
<div className="px-8 py-4 flex items-stretch border-b border-greys-light-200">
|
||||
<div className="flex gap-4 mr-4 items-center h-full">
|
||||
<Link to="/">
|
||||
<VegaLogo />
|
||||
</Link>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 ml-auto"></div>
|
||||
</div>
|
||||
);
|
||||
};
|
@ -1 +0,0 @@
|
||||
export * from './status';
|
@ -1,96 +0,0 @@
|
||||
import { Lozenge, Tooltip } from '@vegaprotocol/ui-toolkit';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
|
||||
import { Indicator } from '../indicator';
|
||||
import type { AuctionTrigger } from '@vegaprotocol/types';
|
||||
|
||||
export const Status = ({
|
||||
tradingMode,
|
||||
trigger,
|
||||
size = 'small',
|
||||
}: {
|
||||
tradingMode?: Schema.MarketTradingMode;
|
||||
trigger?: Schema.AuctionTrigger;
|
||||
size?: 'small' | 'large';
|
||||
}) => {
|
||||
const getStatus = () => {
|
||||
if (!tradingMode) return '';
|
||||
if (
|
||||
tradingMode === Schema.MarketTradingMode.TRADING_MODE_MONITORING_AUCTION
|
||||
) {
|
||||
if (
|
||||
trigger &&
|
||||
trigger !== Schema.AuctionTrigger.AUCTION_TRIGGER_UNSPECIFIED
|
||||
) {
|
||||
return `${Schema.MarketTradingModeMapping[tradingMode]} - ${Schema.AuctionTriggerMapping[trigger]}`;
|
||||
}
|
||||
}
|
||||
return Schema.MarketTradingModeMapping[tradingMode];
|
||||
};
|
||||
|
||||
const status = getStatus();
|
||||
const tooltipDescription =
|
||||
tradingMode && getTooltipDescription(tradingMode, trigger);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Tooltip description={tooltipDescription}>
|
||||
<div
|
||||
className={classNames('inline-flex whitespace-normal', {
|
||||
'text-base': size === 'large',
|
||||
'text-sm': size === 'small',
|
||||
})}
|
||||
>
|
||||
<Lozenge className="border border-greys-light-300 bg-greys-light-100 flex items-center">
|
||||
<Indicator status={tradingMode} />
|
||||
{status}
|
||||
</Lozenge>
|
||||
</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const getTooltipDescription = (
|
||||
status: Schema.MarketTradingMode,
|
||||
trigger?: Schema.AuctionTrigger
|
||||
) => {
|
||||
switch (status) {
|
||||
case Schema.MarketTradingMode.TRADING_MODE_CONTINUOUS:
|
||||
return t(
|
||||
'This is the standard trading mode where trades are executed whenever orders are received'
|
||||
);
|
||||
case Schema.MarketTradingMode.TRADING_MODE_MONITORING_AUCTION:
|
||||
return getMonitoringDescriptionTooltip(trigger);
|
||||
case Schema.MarketTradingMode.TRADING_MODE_OPENING_AUCTION:
|
||||
return t(
|
||||
'This is a new market in an opening auction to determine a fair mid-price before starting continuous trading.'
|
||||
);
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
};
|
||||
|
||||
const getMonitoringDescriptionTooltip = (trigger?: AuctionTrigger) => {
|
||||
switch (trigger) {
|
||||
case Schema.AuctionTrigger.AUCTION_TRIGGER_LIQUIDITY_TARGET_NOT_MET:
|
||||
return t(
|
||||
`This market is in auction until it reaches sufficient liquidity.`
|
||||
);
|
||||
case Schema.AuctionTrigger.AUCTION_TRIGGER_UNABLE_TO_DEPLOY_LP_ORDERS:
|
||||
return t(
|
||||
`This market may have sufficient liquidity but there are not enough priced limit orders in the order book, which are required to deploy liquidity commitment pegged orders.`
|
||||
);
|
||||
case Schema.AuctionTrigger.AUCTION_TRIGGER_PRICE:
|
||||
return t(`This market is in auction due to high price volatility.`);
|
||||
case Schema.AuctionTrigger.AUCTION_TRIGGER_OPENING:
|
||||
return t(
|
||||
`This is a new market in an opening auction to determine a fair mid-price before starting continuous trading`
|
||||
);
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
};
|
@ -1,28 +0,0 @@
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
import { Intent } from '@vegaprotocol/ui-toolkit';
|
||||
|
||||
const marketTradingModeStyle = {
|
||||
[Schema.MarketTradingMode.TRADING_MODE_CONTINUOUS]: '#00D46E',
|
||||
[Schema.MarketTradingMode.TRADING_MODE_MONITORING_AUCTION]: '#CF0064',
|
||||
[Schema.MarketTradingMode.TRADING_MODE_OPENING_AUCTION]: '#0046CD',
|
||||
[Schema.MarketTradingMode.TRADING_MODE_BATCH_AUCTION]: '#CF0064',
|
||||
[Schema.MarketTradingMode.TRADING_MODE_NO_TRADING]: '#CF0064',
|
||||
[Schema.MarketTradingMode.TRADING_MODE_SUSPENDED_VIA_GOVERNANCE]: '#CF0064',
|
||||
};
|
||||
|
||||
export const getColorForStatus = (status: Schema.MarketTradingMode) =>
|
||||
marketTradingModeStyle[status];
|
||||
|
||||
const marketTradingModeIntent = {
|
||||
[Schema.MarketTradingMode.TRADING_MODE_CONTINUOUS]: Intent.Success,
|
||||
[Schema.MarketTradingMode.TRADING_MODE_MONITORING_AUCTION]: Intent.Danger,
|
||||
[Schema.MarketTradingMode.TRADING_MODE_OPENING_AUCTION]: Intent.Primary,
|
||||
[Schema.MarketTradingMode.TRADING_MODE_BATCH_AUCTION]: Intent.Danger,
|
||||
[Schema.MarketTradingMode.TRADING_MODE_NO_TRADING]: Intent.Danger,
|
||||
[Schema.MarketTradingMode.TRADING_MODE_SUSPENDED_VIA_GOVERNANCE]:
|
||||
Intent.Danger,
|
||||
};
|
||||
|
||||
export const intentForStatus = (status: Schema.MarketTradingMode) => {
|
||||
return marketTradingModeIntent[status];
|
||||
};
|
@ -1 +0,0 @@
|
||||
export * from './router-config';
|
@ -1,25 +0,0 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
|
||||
import { Dashboard } from '../components/dashboard';
|
||||
import { Detail } from '../components/detail';
|
||||
|
||||
export const ROUTES = {
|
||||
MARKETS: 'markets',
|
||||
};
|
||||
|
||||
export const routerConfig = [
|
||||
{ path: '/', element: <Dashboard />, icon: '' },
|
||||
{
|
||||
path: ROUTES.MARKETS,
|
||||
name: 'Markets',
|
||||
text: t('Markets'),
|
||||
children: [
|
||||
{
|
||||
path: ':marketId',
|
||||
element: <Detail />,
|
||||
},
|
||||
],
|
||||
icon: 'trade',
|
||||
isNavItem: true,
|
||||
},
|
||||
];
|
@ -1,3 +0,0 @@
|
||||
export const environment = {
|
||||
production: true,
|
||||
};
|
@ -1,6 +0,0 @@
|
||||
// This file can be replaced during build by using the `fileReplacements` array.
|
||||
// When building for production, this file is replaced with `environment.prod.ts`.
|
||||
|
||||
export const environment = {
|
||||
production: false,
|
||||
};
|
Binary file not shown.
Before Width: | Height: | Size: 15 KiB |
@ -1,23 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Liquidity Provision Dashboard</title>
|
||||
<base href="/" />
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="icon" type="image/x-icon" href="favicon.ico" />
|
||||
<link
|
||||
rel="preload"
|
||||
href="https://static.vega.xyz/AlphaLyrae-Medium.woff2"
|
||||
as="font"
|
||||
type="font/woff2"
|
||||
crossorigin="anonymous"
|
||||
/>
|
||||
|
||||
<link rel="stylesheet" href="https://static.vega.xyz/fonts.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="root" class="h-full max-h-full min-h-full"></div>
|
||||
</body>
|
||||
</html>
|
@ -1,15 +0,0 @@
|
||||
import { StrictMode } from 'react';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import { BrowserRouter } from 'react-router-dom';
|
||||
|
||||
import App from './app/app';
|
||||
|
||||
const rootElement = document.getElementById('root');
|
||||
const root = rootElement && createRoot(rootElement);
|
||||
root?.render(
|
||||
<StrictMode>
|
||||
<BrowserRouter>
|
||||
<App />
|
||||
</BrowserRouter>
|
||||
</StrictMode>
|
||||
);
|
@ -1,7 +0,0 @@
|
||||
/**
|
||||
* Polyfill stable language features. These imports will be optimized by `@babel/preset-env`.
|
||||
*
|
||||
* See: https://github.com/zloirock/core-js#babel
|
||||
*/
|
||||
import 'core-js/stable';
|
||||
import 'regenerator-runtime/runtime';
|
@ -1,10 +0,0 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
html,
|
||||
body {
|
||||
@apply h-full;
|
||||
|
||||
font-family: 'Helvetica Neue', Helvetica, sans-serif;
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
const { join } = require('path');
|
||||
const { createGlobPatternsForDependencies } = require('@nx/react/tailwind');
|
||||
const theme = require('../../libs/tailwindcss-config/src/theme-lite');
|
||||
const vegaCustomClasses = require('../../libs/tailwindcss-config/src/vega-custom-classes');
|
||||
const vegaCustomClassesLite = require('../../libs/tailwindcss-config/src/vega-custom-classes-lite');
|
||||
|
||||
module.exports = {
|
||||
content: [
|
||||
join(__dirname, 'src/**/*.{js,ts,jsx,tsx}'),
|
||||
'libs/ui-toolkit/src/utils/shared.ts',
|
||||
...createGlobPatternsForDependencies(__dirname),
|
||||
],
|
||||
darkMode: 'class',
|
||||
theme: {
|
||||
...theme,
|
||||
colors: {
|
||||
...theme.colors,
|
||||
greys: {
|
||||
light: {
|
||||
100: '#F0F0F0',
|
||||
200: '#D2D2D2',
|
||||
300: '#A7A7A7',
|
||||
400: '#626262',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [vegaCustomClasses, vegaCustomClassesLite],
|
||||
};
|
@ -1,27 +0,0 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../dist/out-tsc",
|
||||
"types": [
|
||||
"node",
|
||||
"@nx/react/typings/cssmodule.d.ts",
|
||||
"@nx/react/typings/image.d.ts"
|
||||
]
|
||||
},
|
||||
"files": [
|
||||
"../../node_modules/@nx/react/typings/cssmodule.d.ts",
|
||||
"../../node_modules/@nx/react/typings/image.d.ts"
|
||||
],
|
||||
"exclude": [
|
||||
"jest.config.ts",
|
||||
"**/*.spec.ts",
|
||||
"**/*.test.ts",
|
||||
"**/*.spec.tsx",
|
||||
"**/*.test.tsx",
|
||||
"**/*.spec.js",
|
||||
"**/*.test.js",
|
||||
"**/*.spec.jsx",
|
||||
"**/*.test.jsx"
|
||||
],
|
||||
"include": ["**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx"]
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"jsx": "react-jsx",
|
||||
"allowJs": true,
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"strict": true,
|
||||
"noImplicitOverride": true,
|
||||
"noPropertyAccessFromIndexSignature": false,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true
|
||||
},
|
||||
"include": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.app.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.spec.json"
|
||||
}
|
||||
]
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../dist/out-tsc",
|
||||
"module": "commonjs",
|
||||
"types": [
|
||||
"jest",
|
||||
"node",
|
||||
"@testing-library/jest-dom",
|
||||
"@nx/react/typings/cssmodule.d.ts",
|
||||
"@nx/react/typings/image.d.ts"
|
||||
],
|
||||
"jsx": "react",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"esModuleInterop": true
|
||||
},
|
||||
"include": [
|
||||
"jest.config.ts",
|
||||
"**/*.test.ts",
|
||||
"**/*.spec.ts",
|
||||
"**/*.test.tsx",
|
||||
"**/*.spec.tsx",
|
||||
"**/*.test.js",
|
||||
"**/*.spec.js",
|
||||
"**/*.test.jsx",
|
||||
"**/*.spec.jsx",
|
||||
"**/*.d.ts"
|
||||
],
|
||||
"files": [
|
||||
"../../node_modules/@nx/react/typings/cssmodule.d.ts",
|
||||
"../../node_modules/@nx/react/typings/image.d.ts"
|
||||
]
|
||||
}
|
Loading…
Reference in New Issue
Block a user