Compare commits

...
Author SHA1 Message Date
Madalina Raicu 7318d6088a feat(trading): referral initial state when no sets are available 2023-11-16 17:03:58 +00:00
Ben 8e898cfd78 chore(trading): remove wait for graphql (#5265) 2023-11-16 08:11:48 +00:00
Bartłomiej GłowniaandMatthew Russell f377e07996 feat(trading): use i18next (#5238)
Co-authored-by: Matthew Russell <mattrussell36@gmail.com>
2023-11-15 19:10:39 -08:00
Matthew Russell 090d340364 feat(trading): rewards page (#5222) 2023-11-15 13:46:19 -08:00
Matthew Russell 6d32fa7362 chore(ci): remove unused netlify configs (#5278) 2023-11-15 13:40:26 -08:00
ArtandMadalina Raicu 1a7682c3c9 chore(governance): vesting balances (#5237)
Co-authored-by: Madalina Raicu <madalina@raygroup.uk>
2023-11-15 18:15:05 +00:00
Ben 3294c0cabe chore(trading): fix tests from i18n (#5259) 2023-11-15 13:45:36 +00:00
Edd c10084d441 feat(explorer): simplify party accounts page (#5240) 2023-11-15 11:15:25 +00:00
Edd 96658134ee fix(explorer): improve chain event tx view for contract calls (#5243) 2023-11-15 11:15:07 +00:00
Bartłomiej GłowniaandMatthew Russell a070504d2e feat(trading): i18n (#5126)
Co-authored-by: Matthew Russell <mattrussell36@gmail.com>
2023-11-14 21:10:06 -08:00
Matthew Russell f891caf08b chore(trading,governance,explorer): disable prettier tailwind plugin (#5258) 2023-11-14 18:09:29 -08:00
Matthew Russell 69bdc637e5 chore(trading,governance,explorer): disable prettier tailwind plugin 2023-11-14 17:21:09 -08:00
Matthew Russell 78add88014 chore(trading,governance,explorer): comply with eslint type import rules (#5257) 2023-11-14 16:43:36 -08:00
02c425f304 chore(trading): add playwright and market sim testing framework (#5199)
Co-authored-by: Matthew Russell <mattrussell36@gmail.com>
Co-authored-by: dalebennett1992 <dalebennett1992@hotmail.co.uk>
2023-11-14 10:05:07 -08:00
404 changed files with 14285 additions and 3497 deletions
+2 -1
View File
@@ -73,7 +73,8 @@
"error",
{
"prefer": "type-imports",
"disallowTypeAnnotations": true
"disallowTypeAnnotations": true,
"fixStyle": "inline-type-imports"
}
],
"curly": ["error", "multi-line"]
+1 -1
View File
@@ -205,7 +205,7 @@ jobs:
console-e2e:
needs: [build-sources, check-e2e-needed]
name: '(CI) console python'
name: '(CI) trading e2e python'
uses: ./.github/workflows/console-test-run.yml
secrets: inherit
if: needs.check-e2e-needed.outputs.run-tests == 'true' && contains(needs.build-sources.outputs.projects, 'trading')
+15 -18
View File
@@ -10,7 +10,7 @@ on:
inputs:
console-test-branch:
type: choice
description: 'main: v0.72.14, develop: v0.73.0-preview7'
description: 'main: v0.72.14, develop: v0.73.4'
options:
- main
- develop
@@ -153,25 +153,19 @@ jobs:
run: |
docker load --input /tmp/console-image.tar
docker image ls -a
#----------------------------------------------
# check-out tests repo
# check-out frontend-monorepo
#----------------------------------------------
- name: Checkout console test repo
- name: Checkout frontend-monorepo
uses: actions/checkout@v3
with:
repository: vegaprotocol/console-test
ref: ${{ needs.console-test-branch.outputs.console-branch }}
- name: Load console test envs
id: console-test-env
uses: falti/dotenv-action@v1.0.4
with:
path: '.env.${{ needs.console-test-branch.outputs.console-branch }}'
export-variables: true
keys-case: upper
log-variables: true
ref: ${{ inputs.github-sha || github.sha }}
#----------------------------------------------
# get vega version
#----------------------------------------------
- name: Set VEGA_VERSION from .env
id: set_vega_version
run: echo "VEGA_VERSION=$(grep VEGA_VERSION apps/trading/e2e/.env | cut -d '=' -f2)" >> $GITHUB_ENV
#----------------------------------------------
# ----- Setup python -----
#----------------------------------------------
@@ -194,22 +188,25 @@ jobs:
#----------------------------------------------
- name: Install dependencies
run: poetry install --no-interaction --no-root
working-directory: apps/trading/e2e
#----------------------------------------------
# install vega binaries
#----------------------------------------------
- name: Install vega binaries
run: poetry run python -m vega_sim.tools.load_binaries --force --version ${{ env.VEGA_VERSION }}
working-directory: apps/trading/e2e
#----------------------------------------------
# install playwright
# install playwrightworking-directory: apps/trading/e2e
#----------------------------------------------
- name: install playwright
run: poetry run playwright install --with-deps chromium
working-directory: apps/trading/e2e
#----------------------------------------------
# run tests
#----------------------------------------------
- name: Run tests
run: CONSOLE_IMAGE_NAME=ci/trading:local poetry run pytest -v -s --numprocesses 4 --dist loadfile --durations=15
working-directory: apps/trading/e2e
#----------------------------------------------
# upload traces
#----------------------------------------------
+9 -2
View File
@@ -48,9 +48,16 @@ cypress.env.json
# Next.js
.next
#cypress
# cypress
/apps/**/cypress/reports/
/apps/**/cypress/downloads/
/apps/**/fixtures/wallet/node**
.nx/cache
# apps/trading/e2e
__pycache__/
apps/trading/e2e/logs/
apps/trading/e2e/.pytest_cache/
apps/trading/e2e/traces/
.nx/cache
+12 -1
View File
@@ -1,6 +1,7 @@
# Add files here to ignore them from prettier formatting
/dist
/dist-result
/coverage
__generated__
__generated___
@@ -9,4 +10,14 @@ apps/static/src/assets/devnet-tranches.json
apps/static/src/assets/mainnet-tranches.json
apps/static/src/assets/testnet-tranches.json
/.nx/cache
/apps/**/cypress/reports/
/apps/**/cypress/downloads/
/.nx/cache
# apps/trading/e2e
__pycache__/
apps/trading/e2e/logs/
apps/trading/e2e/.pytest_cache/
apps/trading/e2e/traces/
.pytest_cache/
-1
View File
@@ -1,4 +1,3 @@
{
"plugins": ["prettier-plugin-tailwindcss"],
"singleQuote": true
}
-4
View File
@@ -1,4 +0,0 @@
[[redirects]]
from = "/*"
to = "/index.html"
status = 200
-9
View File
@@ -74,15 +74,6 @@
]
}
},
"build-netlify": {
"executor": "nx:run-commands",
"options": {
"commands": [
"cp apps/explorer/netlify.toml netlify.toml",
"nx build explorer"
]
}
},
"build-spec": {
"executor": "nx:run-commands",
"outputs": [],
@@ -1,5 +1,5 @@
import { useAssetDataProvider } from '@vegaprotocol/assets';
import { addDecimalsFormatNumber } from '@vegaprotocol/utils';
import { addDecimalsFixedFormatNumber } from '@vegaprotocol/utils';
import { AssetLink } from '../links';
export type AssetBalanceProps = {
@@ -23,12 +23,12 @@ const AssetBalance = ({
const label =
!loading && asset && asset.decimals
? addDecimalsFormatNumber(price, asset.decimals)
? addDecimalsFixedFormatNumber(price, asset.decimals)
: price;
return (
<div className="inline-block">
<span>{label}</span>{' '}
<span className="font-mono">{label}</span>{' '}
{showAssetLink && asset?.id ? (
<AssetLink showAssetSymbol={showAssetSymbol} assetId={assetId} />
) : null}
@@ -1,20 +1,25 @@
import { useMemo } from 'react';
import type { AssetFieldsFragment } from '@vegaprotocol/assets';
import { AssetTypeMapping, AssetStatusMapping } from '@vegaprotocol/assets';
import {
useAssetTypeMapping,
useAssetStatusMapping,
type AssetFieldsFragment,
} from '@vegaprotocol/assets';
import { t } from '@vegaprotocol/i18n';
import { ButtonLink } from '@vegaprotocol/ui-toolkit';
import type { AgGridReact } from 'ag-grid-react';
import { type AgGridReact } from 'ag-grid-react';
import { AgGrid } from '@vegaprotocol/datagrid';
import type { VegaICellRendererParams } from '@vegaprotocol/datagrid';
import { type VegaICellRendererParams } from '@vegaprotocol/datagrid';
import { useRef, useLayoutEffect } from 'react';
import { BREAKPOINT_MD } from '../../config/breakpoints';
import { useNavigate } from 'react-router-dom';
import type { RowClickedEvent, ColDef } from 'ag-grid-community';
import { type RowClickedEvent, ColDef } from 'ag-grid-community';
type AssetsTableProps = {
data: AssetFieldsFragment[] | null;
};
export const AssetsTable = ({ data }: AssetsTableProps) => {
const assetTypeMapping = useAssetTypeMapping();
const assetStatusMapping = useAssetStatusMapping();
const navigate = useNavigate();
const ref = useRef<AgGridReact>(null);
const showColumnsOnDesktop = () => {
@@ -47,14 +52,14 @@ export const AssetsTable = ({ data }: AssetsTableProps) => {
field: 'source.__typename',
hide: window.innerWidth < BREAKPOINT_MD,
valueFormatter: ({ value }: { value?: string }) =>
value ? AssetTypeMapping[value].value : '',
value ? assetTypeMapping[value].value : '',
},
{
headerName: t('Status'),
field: 'status',
hide: window.innerWidth < BREAKPOINT_MD,
valueFormatter: ({ value }: { value?: string }) =>
value ? AssetStatusMapping[value].value : '',
value ? assetStatusMapping[value].value : '',
},
{
colId: 'actions',
@@ -69,7 +74,7 @@ export const AssetsTable = ({ data }: AssetsTableProps) => {
}: VegaICellRendererParams<AssetFieldsFragment, 'id'>) =>
value ? (
<ButtonLink
onClick={(e) => {
onClick={() => {
navigate(value);
}}
>
@@ -80,7 +85,7 @@ export const AssetsTable = ({ data }: AssetsTableProps) => {
),
},
],
[navigate]
[navigate, assetStatusMapping, assetTypeMapping]
);
return (
@@ -2,18 +2,18 @@ import { useMemo } from 'react';
import { getAsset, type MarketFieldsFragment } from '@vegaprotocol/markets';
import { t } from '@vegaprotocol/i18n';
import { ButtonLink } from '@vegaprotocol/ui-toolkit';
import type { AgGridReact } from 'ag-grid-react';
import type { ColDef } from 'ag-grid-community';
import { type AgGridReact } from 'ag-grid-react';
import { type ColDef } from 'ag-grid-community';
import { AgGrid } from '@vegaprotocol/datagrid';
import type {
VegaICellRendererParams,
VegaValueGetterParams,
import {
type VegaICellRendererParams,
type VegaValueGetterParams,
} from '@vegaprotocol/datagrid';
import { useRef, useLayoutEffect } from 'react';
import { BREAKPOINT_MD } from '../../config/breakpoints';
import { MarketStateMapping } from '@vegaprotocol/types';
import { useAssetDetailsDialogStore } from '@vegaprotocol/assets';
import type { RowClickedEvent } from 'ag-grid-community';
import { type RowClickedEvent } from 'ag-grid-community';
import { Link, useNavigate } from 'react-router-dom';
type MarketsTableProps = {
@@ -1,14 +1,14 @@
import type { ProposalListFieldsFragment } from '@vegaprotocol/proposals';
import { type ProposalListFieldsFragment } from '@vegaprotocol/proposals';
import { VoteProgress } from '@vegaprotocol/proposals';
import type { AgGridReact } from 'ag-grid-react';
import { type AgGridReact } from 'ag-grid-react';
import { ExternalLink } from '@vegaprotocol/ui-toolkit';
import { AgGrid } from '@vegaprotocol/datagrid';
import type {
VegaICellRendererParams,
VegaValueFormatterParams,
import {
type VegaICellRendererParams,
type VegaValueFormatterParams,
} from '@vegaprotocol/datagrid';
import { useLayoutEffect, useMemo, useRef, useState } from 'react';
import type { RowClickedEvent, ColDef } from 'ag-grid-community';
import { type RowClickedEvent, ColDef } from 'ag-grid-community';
import { getDateTimeFormat } from '@vegaprotocol/utils';
import { t } from '@vegaprotocol/i18n';
import {
@@ -105,7 +105,7 @@ export const ProposalsTable = ({ data }: ProposalsTableProps) => {
? new BigNumber(0)
: yesTokens.multipliedBy(100).dividedBy(totalTokensVoted);
return (
<div className="flex items-center justify-center h-full pt-2 uppercase">
<div className="flex h-full items-center justify-center pt-2 uppercase">
<VoteProgress
threshold={requiredMajorityPercentage}
progress={yesPercentage}
@@ -15,6 +15,7 @@ import isUndefined from 'lodash/isUndefined';
import type { BlockExplorerTransactionResult } from '../../../../routes/types/block-explorer-response';
import { TxDetailsChainEventWithdrawal } from './tx-erc20-withdrawal';
import { TxDetailsChainEventErc20AssetDelist } from './tx-erc20-asset-delist';
import { TxDetailsContractCall } from './tx-contract-call';
interface ChainEventProps {
txData: BlockExplorerTransactionResult | undefined;
@@ -38,7 +39,7 @@ export const ChainEvent = ({ txData }: ChainEventProps) => {
return null;
}
const { builtin, erc20, erc20Multisig, stakingEvent } =
const { builtin, erc20, erc20Multisig, stakingEvent, contractCall } =
txData.command.chainEvent;
// Builtin Asset events
@@ -140,6 +141,10 @@ export const ChainEvent = ({ txData }: ChainEventProps) => {
}
}
if (contractCall) {
return <TxDetailsContractCall contractCall={contractCall} />;
}
// If we hit this return, tx-shared-details should give a basic overview
return null;
};
@@ -0,0 +1,26 @@
import { decodeEthCallResult } from './tx-contract-call';
import { base64 } from 'ethers/lib/utils';
import { defaultAbiCoder } from '@ethersproject/abi';
import { BigNumber } from '@ethersproject/bignumber';
describe('decodeEthCallResult', () => {
it('should decode contractData correctly (mocked)', () => {
const mockContractData = base64.encode(
defaultAbiCoder.encode(['int256'], [BigNumber.from(123)])
);
const result = decodeEthCallResult(mockContractData);
expect(result).toBe('123');
});
it('should decode contractData correctly (known data)', () => {
const mockContractData = 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADH8cueyY=';
const result = decodeEthCallResult(mockContractData);
expect(result).toBe('3435020581670');
});
it('should return "-" when an error occurs', () => {
const mockContractData = 'invalid_data';
const result = decodeEthCallResult(mockContractData);
expect(result).toBe('-');
});
});
@@ -0,0 +1,88 @@
import { TableCell, TableRow } from '../../../table';
import { t } from '@vegaprotocol/i18n';
import {
EthExplorerLink,
EthExplorerLinkTypes,
} from '../../../links/eth-explorer-link/eth-explorer-link';
import type { components } from '../../../../../types/explorer';
import { defaultAbiCoder, base64 } from 'ethers/lib/utils';
import { BigNumber } from 'ethers';
import OracleLink from '../../../links/oracle-link/oracle-link';
import { useExplorerOracleSpecByIdQuery } from '../../../../routes/oracles/__generated__/Oracles';
import { OracleEthSource } from '../../../../routes/oracles/components/oracle-eth-source';
/**
* Decodes the b64/ABIcoded result from an eth cal
* @param data
* @returns
*/
export function decodeEthCallResult(contractData: string): string {
try {
const rawResult = defaultAbiCoder.decode(
['int256'],
base64.decode(contractData)
);
// Finally, convert the resulting BigNumber in to a string
const res = BigNumber.from(rawResult[0]).toString();
return res;
} catch (e) {
return '-';
}
}
interface TxDetailsContractCallProps {
contractCall: components['schemas']['vegaEthContractCallEvent'];
}
export const TxDetailsContractCall = ({
contractCall,
}: TxDetailsContractCallProps) => {
const { data } = useExplorerOracleSpecByIdQuery({
variables: {
id: contractCall.specId || '1',
},
});
if (!contractCall || !contractCall.result) {
return null;
}
return (
<>
{contractCall.specId && (
<TableRow modifier="bordered">
<TableCell>{t('Oracle')}</TableCell>
<TableCell>
<OracleLink
id={contractCall.specId}
hasSeenOracleReports={true}
status={data?.oracleSpec?.dataSourceSpec.spec.status || '-'}
/>
</TableCell>
</TableRow>
)}
{contractCall.blockHeight && (
<TableRow modifier="bordered">
<TableCell>{t('ETH block')}</TableCell>
<TableCell>
<EthExplorerLink
id={contractCall.blockHeight}
type={EthExplorerLinkTypes.block}
/>
</TableCell>
</TableRow>
)}
{data?.oracleSpec?.dataSourceSpec && (
<OracleEthSource
sourceType={data.oracleSpec.dataSourceSpec.spec.data.sourceType}
/>
)}
<TableRow modifier="bordered">
<TableCell>{t('Result')}</TableCell>
<TableCell>{decodeEthCallResult(contractCall.result)}</TableCell>
</TableRow>
</>
);
};
@@ -1,51 +1,11 @@
import { t } from '@vegaprotocol/i18n';
import { TxDetailsShared } from './shared/tx-details-shared';
import { TableWithTbody } from '../../table';
import { defaultAbiCoder, base64 } from 'ethers/lib/utils';
import { ChainEvent } from './chain-events';
import { BigNumber } from 'ethers';
import type { AbiType } from '../../../lib/encoders/abis/abi-types';
import type { BlockExplorerTransactionResult } from '../../../routes/types/block-explorer-response';
import type { TendermintBlocksResponse } from '../../../routes/blocks/tendermint-blocks-response';
interface AbiOutput {
type: AbiType;
internalType: AbiType;
name: string;
}
/**
* Decodes the b64/ABIcoded result from an eth cal
* @param data
* @returns
*/
export function decodeEthCallResult(
data: BlockExplorerTransactionResult
): string {
const ethResult = data.command.chainEvent?.contractCall.result;
try {
// Decode the result string: base64 => uint8array
const data = base64.decode(ethResult);
// Parse the escaped ABI in to an object
const abi = JSON.parse(
'[{"inputs":[],"name":"latestAnswer","outputs":[{"internalType":"int256","name":"","type":"int256"}],"stateMutability":"view","type":"function"}]'
);
// Pull the expected types out of the Oracles ABI
const types: AbiType[] = abi[0].outputs.map((o: AbiOutput) => o.type);
const rawResult = defaultAbiCoder.decode(types, data);
// Finally, convert the resulting BigNumber in to a string
const res = BigNumber.from(rawResult[0]).toString();
return res;
} catch (e) {
return '-';
}
}
interface TxDetailsChainEventProps {
txData: BlockExplorerTransactionResult | undefined;
pubKey: string | undefined;
@@ -3,8 +3,8 @@ import { DATA_SOURCES } from '../../../config';
import { t } from '@vegaprotocol/i18n';
import { useFetch } from '@vegaprotocol/react-helpers';
import { TxDetailsOrder } from './tx-order';
import type { BlockExplorerTransactionResult } from '../../../routes/types/block-explorer-response';
import type { TendermintBlocksResponse } from '../../../routes/blocks/tendermint-blocks-response';
import { type BlockExplorerTransactionResult } from '../../../routes/types/block-explorer-response';
import { type TendermintBlocksResponse } from '../../../routes/blocks/tendermint-blocks-response';
import { TxDetailsHeartbeat } from './tx-hearbeat';
import { TxDetailsGeneric } from './tx-generic';
import { TxDetailsBatch } from './tx-batch';
@@ -1,7 +1,7 @@
import { Table, TableRow } from '../table';
import { t } from '@vegaprotocol/i18n';
import { useFetch } from '@vegaprotocol/react-helpers';
import type { BlockExplorerTransactions } from '../../routes/types/block-explorer-response';
import { type BlockExplorerTransactions } from '../../routes/types/block-explorer-response';
import { getTxsDataUrl } from '../../hooks/get-txs-data-url';
import { AsyncRenderer, Loader } from '@vegaprotocol/ui-toolkit';
import EmptyList from '../empty-list/empty-list';
@@ -22,7 +22,7 @@ export const TxsPerBlock = ({ blockHeight, txCount }: TxsPerBlockProps) => {
return (
<AsyncRenderer data={data} error={error} loading={!!loading}>
{data && data.transactions.length > 0 ? (
<div className="overflow-x-auto whitespace-nowrap mb-28">
<div className="mb-28 overflow-x-auto whitespace-nowrap">
<Table>
<thead>
<TableRow modifier="bordered" className="font-mono">
+5 -5
View File
@@ -1,14 +1,14 @@
import { useSearchParams } from 'react-router-dom';
import type { URLSearchParamsInit } from 'react-router-dom';
import { type URLSearchParamsInit } from 'react-router-dom';
import { useCallback } from 'react';
import { useFetch } from '@vegaprotocol/react-helpers';
import type {
BlockExplorerTransactionResult,
BlockExplorerTransactions,
import {
type BlockExplorerTransactionResult,
type BlockExplorerTransactions,
} from '../routes/types/block-explorer-response';
import isNumber from 'lodash/isNumber';
import { AllFilterOptions } from '../components/txs/tx-filter';
import type { FilterOption } from '../components/txs/tx-filter';
import { type FilterOption } from '../components/txs/tx-filter';
import { BE_TXS_PER_REQUEST, getTxsDataUrl } from './get-txs-data-url';
export function getTypeFilters(filters?: Set<FilterOption>) {
@@ -9,11 +9,13 @@ import { JsonViewerDialog } from '../../components/dialogs/json-viewer-dialog';
import { useState } from 'react';
import { PageTitle } from '../../components/page-helpers/page-title';
type Params = { assetId: string };
export const AssetPage = () => {
useDocumentTitle(['Assets']);
useScrollToLocation();
const { assetId } = useParams<{ assetId: string }>();
const { assetId } = useParams<Params>();
const { data, loading, error } = useAssetDataProvider(assetId || '');
const title = data ? data.name : error ? t('Asset not found') : '';
@@ -41,7 +43,7 @@ export const AssetPage = () => {
loading={loading}
error={error}
>
<div className="h-full relative">
<div className="relative h-full">
<AssetDetailsTable asset={data as AssetFieldsFragment} />
</div>
</AsyncRenderer>
@@ -1,8 +1,8 @@
import { useCallback, useState } from 'react';
import { DATA_SOURCES } from '../../../config';
import type {
BlockMeta,
TendermintBlockchainResponse,
import {
type BlockMeta,
type TendermintBlockchainResponse,
} from '../tendermint-blockchain-response';
import { RouteTitle } from '../../../components/route-title';
import { BlocksRefetch } from '../../../components/blocks';
@@ -17,8 +17,10 @@ import { NodeLink } from '../../../components/links';
import { useDocumentTitle } from '../../../hooks/use-document-title';
import EmptyList from '../../../components/empty-list/empty-list';
type Params = { block: string };
const Block = () => {
const { block } = useParams<{ block: string }>();
const { block } = useParams<Params>();
useDocumentTitle(['Blocks', `Block #${block}`]);
const {
state: { data: blockData, loading, error },
@@ -29,7 +31,7 @@ const Block = () => {
<RouteTitle data-testid="block-header">{t(`BLOCK ${block}`)}</RouteTitle>
<AsyncRenderer data={blockData} error={error} loading={!!loading}>
<>
<div className="grid grid-cols-2 gap-2 mb-8">
<div className="mb-8 grid grid-cols-2 gap-2">
<Link
data-testid="previous-block"
to={`/${Routes.BLOCKS}/${Number(block) - 1}`}
@@ -3,7 +3,7 @@ import { useFetch } from '@vegaprotocol/react-helpers';
import { RouteTitle } from '../../components/route-title';
import { AsyncRenderer, SyntaxHighlighter } from '@vegaprotocol/ui-toolkit';
import { DATA_SOURCES } from '../../config';
import type { TendermintGenesisResponse } from './tendermint-genesis-response';
import { type TendermintGenesisResponse } from './tendermint-genesis-response';
import { useDocumentTitle } from '../../hooks/use-document-title';
const Genesis = () => {
@@ -11,10 +11,12 @@ import { JsonViewerDialog } from '../../components/dialogs/json-viewer-dialog';
import { marketInfoWithDataProvider } from '@vegaprotocol/markets';
import { PageTitle } from '../../components/page-helpers/page-title';
type Params = { marketId: string };
export const MarketPage = () => {
useScrollToLocation();
const { marketId } = useParams<{ marketId: string }>();
const { marketId } = useParams<Params>();
const { data, loading, error } = useDataProvider({
dataProvider: marketInfoWithDataProvider,
@@ -1,8 +1,10 @@
import { getNodes } from '@vegaprotocol/utils';
import { MarketLink } from '../../../components/links';
import { TableRow, TableCell, TableHeader } from '../../../components/table';
import type { ExplorerOracleForMarketsMarketFragment } from '../__generated__/OraclesForMarkets';
import { useExplorerOracleFormMarketsQuery } from '../__generated__/OraclesForMarkets';
import {
useExplorerOracleFormMarketsQuery,
type ExplorerOracleForMarketsMarketFragment,
} from '../__generated__/OraclesForMarkets';
interface OracleMarketsProps {
id: string;
@@ -9,8 +9,10 @@ import { AsyncRenderer, SyntaxHighlighter } from '@vegaprotocol/ui-toolkit';
import filter from 'recursive-key-filter';
import { TruncateInline } from '../../../components/truncate/truncate';
type Params = { id: string };
export const Oracle = () => {
const { id } = useParams<{ id: string }>();
const { id } = useParams<Params>();
useDocumentTitle(['Oracle', `Oracle #${truncateByChars(id || '1', 5, 5)}`]);
@@ -6,8 +6,10 @@ import { useDocumentTitle } from '../../../../hooks/use-document-title';
import { PartyAccounts } from '../components/party-accounts';
type Params = { party: string };
const PartyAccountsByAsset = () => {
const { party } = useParams<{ party: string }>();
const { party } = useParams<Params>();
useDocumentTitle(['Public keys', party || '-']);
const partyId = toNonHex(party ? party : '');
@@ -1,6 +1,7 @@
import { AccountManager } from '@vegaprotocol/accounts';
import { useCallback } from 'react';
import { useAssetDetailsDialogStore } from '@vegaprotocol/assets';
import { useExplorerPartyAssetsQuery } from '../__generated__/Party-assets';
import { AssetLink, MarketLink } from '../../../../components/links';
import AssetBalance from '../../../../components/asset-balance/asset-balance';
import { AccountTypeMapping } from '@vegaprotocol/types';
interface PartyAccountsProps {
partyId: string;
@@ -12,21 +13,71 @@ interface PartyAccountsProps {
* appearing first and... tbd
*/
export const PartyAccounts = ({ partyId }: PartyAccountsProps) => {
const { open: openAssetDetailsDialog } = useAssetDetailsDialogStore();
const onClickAsset = useCallback(
(assetId?: string) => {
assetId && openAssetDetailsDialog(assetId);
},
[openAssetDetailsDialog]
);
const { data } = useExplorerPartyAssetsQuery({
variables: { partyId },
});
const party = data?.partiesConnection?.edges[0]?.node;
const accounts =
party?.accountsConnection?.edges?.filter((edge) => edge?.node) || [];
return (
<div className="block min-h-44 h-60 4 w-full border-red-800 relative">
<AccountManager
partyId={partyId}
onClickAsset={onClickAsset}
isReadOnly={true}
/>
<table>
<thead>
<tr>
<th className="text-right px-4">Balance</th>
<th className="text-left px-4">Type</th>
<th className="text-left px-4">Market</th>
<th className="text-left px-4">Asset</th>
</tr>
</thead>
<tbody>
{accounts
.sort((a, b) => {
// Sort by asset id, then market id, with general accounts first
if (!a) {
return 1;
}
if (!b) {
return -1;
}
if (a.node.asset.id !== b.node.asset.id) {
return a.node.asset.id.localeCompare(b.node.asset.id);
}
if (a.node.type === 'ACCOUNT_TYPE_GENERAL') return -1;
if (b.node.type === 'ACCOUNT_TYPE_GENERAL') return 1;
if (a.node.market && b.node.market) {
return a.node.market.id.localeCompare(b.node.market.id);
} else {
return a.node.type.localeCompare(b.node.type);
}
})
.map((e) => {
if (!e) return null;
const { type, asset, balance, market } = e.node;
return (
<tr className="border-t border-neutral-300 dark:border-neutral-600">
<td className="px-4 text-right">
<AssetBalance
assetId={asset.id}
price={balance}
showAssetSymbol={true}
/>
</td>
<td className="px-4">{AccountTypeMapping[type]}</td>
<td className="px-4">
{market?.id ? <MarketLink id={market.id} /> : '-'}
</td>
<td className="px-4">
<AssetLink assetId={asset.id} />
</td>
</tr>
);
})}
</tbody>
</table>
</div>
);
};
@@ -19,11 +19,13 @@ import type { FilterOption } from '../../../components/txs/tx-filter';
import { AllFilterOptions, TxsFilter } from '../../../components/txs/tx-filter';
import { useSearchParams } from 'react-router-dom';
type Params = { party: string };
const Party = () => {
const [params] = useSearchParams();
const [filters, setFilters] = useState(new Set(AllFilterOptions));
const { party } = useParams<{ party: string }>();
const { party } = useParams<Params>();
useDocumentTitle(['Public keys', party || '-']);
const navigate = useNavigate();
@@ -60,7 +62,7 @@ const Party = () => {
if (!isValidPartyId(partyId)) {
return (
<div className="max-w-sm mx-auto">
<div className="mx-auto max-w-sm">
<Notification
message={t('Invalid party ID')}
intent={Intent.Danger}
@@ -84,7 +86,7 @@ const Party = () => {
truncateEnd={visibleChars}
/>
<div className="grid md:grid-flow-col grid-flow-row md:space-x-4 grid-cols-1 md:grid-cols-2 w-full">
<div className="grid w-full grid-flow-row grid-cols-1 md:grid-flow-col md:grid-cols-2 md:space-x-4">
<PartyBlockAccounts
accountError={AccountError}
accountLoading={AccountLoading}
@@ -4,13 +4,15 @@ import { useFetch } from '@vegaprotocol/react-helpers';
import { DATA_SOURCES } from '../../../config';
import { RenderFetched } from '../../../components/render-fetched';
import { TxDetails } from './tx-details';
import type { BlockExplorerTransaction } from '../../../routes/types/block-explorer-response';
import { type BlockExplorerTransaction } from '../../../routes/types/block-explorer-response';
import { toNonHex } from '../../../components/search/detect-search';
import { PageHeader } from '../../../components/page-header';
import { useDocumentTitle } from '../../../hooks/use-document-title';
type Params = { txHash: string };
const Tx = () => {
const { txHash } = useParams<{ txHash: string }>();
const { txHash } = useParams<Params>();
const hash = txHash ? toNonHex(txHash) : '';
let errorMessage: string | undefined = undefined;
+1 -1
View File
@@ -1,6 +1,6 @@
{
"extends": ["plugin:cypress/recommended", "../../.eslintrc.json"],
"ignorePatterns": ["!**/*"],
"ignorePatterns": ["!**/*", "cypress"],
"overrides": [
{
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
@@ -31,7 +31,7 @@ import {
switchVegaWalletPubKey,
vegaWalletSetSpecifiedApprovalAmount,
} from '../../support/wallet-functions';
import type { testFreeformProposal } from '../../support/common-interfaces';
import { type testFreeformProposal } from '../../support/common-interfaces';
import { formatDateWithLocalTimezone } from '@vegaprotocol/utils';
import {
createGovernanceTransferProposalTxBody,
@@ -34,7 +34,7 @@ import {
vegaWalletTeardown,
} from '../../support/wallet-functions';
import { ethereumWalletConnect } from '../../support/wallet-eth.functions';
import type { testFreeformProposal } from '../../support/common-interfaces';
import { type testFreeformProposal } from '../../support/common-interfaces';
const vegaWalletStakedBalances = 'vega-wallet-balance-staked-validators';
const vegaWalletAssociatedBalance = 'associated-amount';
@@ -1,4 +1,4 @@
import type { testFreeformProposal } from '../../support/common-interfaces';
import { type testFreeformProposal } from '../../support/common-interfaces';
import {
navigateTo,
navigation,
@@ -302,14 +302,17 @@ context(
cy.getByTestId(vegaWalletCurrencyTitle)
.contains(name)
.parent()
.siblings(txTimeout)
.should((elementAmount) => {
const displayedAmount = parseFloat(elementAmount.text());
// @ts-ignore clash between jest and cypress
expect(displayedAmount).be.gte(expectedAmount);
.parent() // back to currency-title
.parent() // back to container
.within(() => {
cy.get(
'[data-account-type="account_type_general"] [data-value]'
).should((elementAmount) => {
const displayedAmount = parseFloat(elementAmount.text());
// @ts-ignore clash between jest and cypress
expect(displayedAmount).be.gte(expectedAmount);
});
});
cy.getByTestId(vegaWalletCurrencyTitle)
.contains(name)
.parent()
@@ -256,10 +256,7 @@ export function validateWalletCurrency(
.parent()
.parent()
.within(() => {
cy.getByTestId('currency-value', txTimeout).should(
'have.text',
expectedAmount
);
cy.get('[data-value]', txTimeout).should('have.text', expectedAmount);
});
}
-4
View File
@@ -49,10 +49,6 @@ There are a few different configuration options offered for this app:
| `NX_ETH_WALLET_MNEMONIC` (optional) | The mnemonic to be used to sign transactions with in browser |
| `NX_LOCAL_PROVIDER_URL` (optional) | The local node to use to send transaction to when signing in browser |
## Example configs:
For example configurations, check out our [netlify.toml](./netlify.toml).
## Testing
To run the minimal set of unit tests, run the following:
-4
View File
@@ -1,4 +0,0 @@
[[redirects]]
from = "/*"
to = "/index.html"
status = 200
-9
View File
@@ -69,15 +69,6 @@
"jestConfig": "apps/governance/jest.config.ts"
}
},
"build-netlify": {
"executor": "nx:run-commands",
"options": {
"commands": [
"cp apps/governance/netlify.toml netlify.toml",
"nx build token"
]
}
},
"build-spec": {
"executor": "nx:run-commands",
"outputs": [],
+10 -9
View File
@@ -4,7 +4,7 @@ import { Splash } from '@vegaprotocol/ui-toolkit';
import { useVegaWallet, useEagerConnect } from '@vegaprotocol/wallet';
import { FLAGS, useEnvironment } from '@vegaprotocol/environment';
import { useWeb3React } from '@web3-react/core';
import React from 'react';
import React, { Suspense } from 'react';
import { useTranslation } from 'react-i18next';
import { SplashError } from './components/splash-error';
@@ -164,13 +164,14 @@ export const AppLoader = ({ children }: { children: React.ReactElement }) => {
);
}
if (!loaded) {
return (
<Splash>
<SplashLoader />
</Splash>
);
}
const loading = (
<Splash>
<SplashLoader />
</Splash>
);
return children;
if (!loaded) {
return loading;
}
return <Suspense fallback={loading}>{children}</Suspense>;
};
+6 -3
View File
@@ -42,6 +42,7 @@ import {
useNodeSwitcherStore,
DocsLinks,
NodeFailure,
AppLoader as Loader,
} from '@vegaprotocol/environment';
import { ENV } from './config';
import type { InMemoryCacheConfig } from '@apollo/client';
@@ -352,9 +353,11 @@ function App() {
useInitializeEnv();
return (
<NetworkLoader cache={cache}>
<AppContainer />
</NetworkLoader>
<React.Suspense fallback={<Loader />}>
<NetworkLoader cache={cache}>
<AppContainer />
</NetworkLoader>
</React.Suspense>
);
}
+1
View File
@@ -0,0 +1 @@
../../../../libs/i18n/src/locales
@@ -10,7 +10,7 @@ import noIcon from '../../images/token-no-icon.png';
import vegaBlack from '../../images/vega_black.png';
import vegaVesting from '../../images/vega_vesting.png';
import { BigNumber } from '../../lib/bignumber';
import type { WalletCardAssetProps } from '../wallet-card';
import { type WalletCardAssetProps } from '../wallet-card';
import { useVegaWallet } from '@vegaprotocol/wallet';
import { useContracts } from '../../contexts/contracts/contracts-context';
import * as Schema from '@vegaprotocol/types';
@@ -21,12 +21,12 @@ import {
toBigNum,
} from '@vegaprotocol/utils';
import { useAppState } from '../../contexts/app-state/app-state-context';
import type {
DelegationsQuery,
DelegationsQueryVariables,
WalletDelegationFieldsFragment,
import {
DelegationsDocument,
type DelegationsQuery,
type DelegationsQueryVariables,
type WalletDelegationFieldsFragment,
} from './__generated__/Delegations';
import { DelegationsDocument } from './__generated__/Delegations';
import { isPartyNotFoundError } from '../../lib/party';
export const usePollForDelegations = () => {
@@ -44,6 +44,7 @@ export const usePollForDelegations = () => {
const [delegatedNodes, setDelegatedNodes] = React.useState<
{
nodeId: string;
// eslint-disable-next-line
name: string;
hasStakePending: boolean;
currentEpochStake?: BigNumber;
@@ -113,6 +114,16 @@ export const usePollForDelegations = () => {
isAssetTypeERC20(a.asset) &&
a.asset.source.contractAddress === vegaToken.address;
const isVesting =
a.type === Schema.AccountType.ACCOUNT_TYPE_VESTED_REWARDS ||
a.type === Schema.AccountType.ACCOUNT_TYPE_VESTING_REWARDS;
let icon = noIcon;
if (isVega) {
if (isVesting) icon = vegaVesting;
else icon = vegaBlack;
}
return {
isVega,
name: a.asset.name,
@@ -123,14 +134,7 @@ export const usePollForDelegations = () => {
balance: new BigNumber(
addDecimal(a.balance, a.asset.decimals)
),
image: isVega
? vegaBlack
: a.type ===
Schema.AccountType.ACCOUNT_TYPE_VESTED_REWARDS ||
a.type ===
Schema.AccountType.ACCOUNT_TYPE_VESTING_REWARDS
? vegaVesting
: noIcon,
image: icon,
border: isVega,
address: isAssetTypeERC20(a.asset)
? a.asset.source.contractAddress
@@ -11,7 +11,10 @@ import { BigNumber } from '../../lib/bignumber';
import { truncateMiddle } from '../../lib/truncate-middle';
import Routes from '../../routes/routes';
import { BulletHeader } from '../bullet-header';
import type { WalletCardAssetProps } from '../wallet-card';
import type {
WalletCardAssetProps,
WalletCardAssetWithMultipleBalancesProps,
} from '../wallet-card';
import {
WalletCard,
WalletCardActions,
@@ -27,6 +30,7 @@ import { Button, ButtonLink } from '@vegaprotocol/ui-toolkit';
import { toBigNum } from '@vegaprotocol/utils';
import { usePendingBalancesStore } from '../../hooks/use-pending-balances-manager';
import { StakingEventType } from '../../hooks/use-get-association-breakdown';
import omit from 'lodash/omit';
export const VegaWallet = () => {
const { t } = useTranslation();
@@ -99,12 +103,29 @@ const VegaWalletAssetList = ({ accounts }: VegaWalletAssetsListProps) => {
if (!accounts.length) {
return null;
}
const groupedByAsset = accounts.reduce((all, a) => {
const foundIndex = all.findIndex((acc) => acc.assetId === a.assetId);
if (foundIndex > -1) {
const found = all[foundIndex];
all[foundIndex] = {
...found,
balances: [...found.balances, { balance: a.balance, type: a.type }],
};
return all;
}
const acc = {
...omit(a, 'balance', 'type'),
balances: [{ balance: a.balance, type: a.type }],
};
return [...all, acc];
}, [] as WalletCardAssetWithMultipleBalancesProps[]);
return (
<>
<WalletCardHeader>
<BulletHeader tag="h2">{t('assets')}</BulletHeader>
</WalletCardHeader>
{accounts.map((a, i) => (
{groupedByAsset.map((a, i) => (
<WalletCardAsset key={i} {...a} />
))}
</>
@@ -182,6 +203,7 @@ const VegaWalletConnected = ({ vegaKeys }: VegaWalletConnectedProps) => {
subheading={t('Associated')}
symbol="VEGA"
balance={currentStakeAvailable}
allowZeroBalance={true}
/>
{totalPending.eq(0) ? null : (
<>
@@ -192,6 +214,7 @@ const VegaWalletConnected = ({ vegaKeys }: VegaWalletConnectedProps) => {
subheading={t('Pending association')}
symbol="VEGA"
balance={totalPending}
allowZeroBalance={true}
/>
<WalletCardAsset
image={vegaWhite}
@@ -200,6 +223,7 @@ const VegaWalletConnected = ({ vegaKeys }: VegaWalletConnectedProps) => {
subheading={t('Total associated after pending')}
symbol="VEGA"
balance={pendingStakeAmount}
allowZeroBalance={true}
/>
</>
)}
@@ -103,7 +103,7 @@ export const WalletCardActions = ({
return <div className="flex justify-end gap-2 mb-4">{children}</div>;
};
export interface WalletCardAssetProps {
export type WalletCardAssetProps = {
image: string;
name: string;
symbol: string;
@@ -113,42 +113,61 @@ export interface WalletCardAssetProps {
border?: boolean;
subheading?: string;
type?: Schema.AccountType;
}
allowZeroBalance?: boolean;
};
export type WalletCardAssetWithMultipleBalancesProps = Omit<
WalletCardAssetProps,
'balance' | 'type'
> & {
balances: { balance: BigNumber; type?: Schema.AccountType }[];
};
export const WalletCardAsset = ({
image,
name,
symbol,
balance,
decimals,
assetId,
border,
subheading,
type,
}: WalletCardAssetProps) => {
const [integers, decimalsPlaces, separator] = useNumberParts(
balance,
decimals
);
const { t } = useTranslation();
const consoleLink = useLinks(DApp.Console);
const transferAssetLink = (assetId: string) =>
consoleLink(CONSOLE_TRANSFER_ASSET.replace(':assetId', assetId));
const { param: baseRate } = useNetworkParam('rewards_vesting_baseRate');
allowZeroBalance = false,
...props
}: WalletCardAssetProps | WalletCardAssetWithMultipleBalancesProps) => {
const balance = 'balance' in props ? props.balance : undefined;
const type = 'type' in props ? props.type : undefined;
const balances =
'balances' in props
? props.balances
: balance
? [{ balance, type }]
: undefined;
const isRedeemable =
type === Schema.AccountType.ACCOUNT_TYPE_VESTED_REWARDS && assetId;
const values =
balances &&
balances.length > 0 &&
balances
.filter((b) => allowZeroBalance || !b.balance.isZero())
.sort((a, b) => {
const order = [
Schema.AccountType.ACCOUNT_TYPE_VESTING_REWARDS,
Schema.AccountType.ACCOUNT_TYPE_VESTED_REWARDS,
Schema.AccountType.ACCOUNT_TYPE_GENERAL,
undefined,
];
return order.indexOf(a.type) - order.indexOf(b.type);
})
.map(({ balance, type }, i) => (
<CurrencyValue
key={i}
balance={balance}
decimals={decimals}
type={type}
assetId={assetId}
/>
));
const accountTypeTooltip = useMemo(() => {
if (type === Schema.AccountType.ACCOUNT_TYPE_VESTED_REWARDS) {
return t('VestedRewardsTooltip');
}
if (type === Schema.AccountType.ACCOUNT_TYPE_VESTING_REWARDS && baseRate) {
return t('VestingRewardsTooltip', { baseRate });
}
return null;
}, [baseRate, t, type]);
if (!values || values.length === 0) return;
return (
<div className="flex flex-nowrap gap-2 mt-2 mb-4">
@@ -169,35 +188,92 @@ export const WalletCardAsset = ({
{subheading || symbol}
</div>
</div>
{type ? (
<div className="mb-[2px] flex gap-2 items-baseline">
<Tooltip description={accountTypeTooltip}>
<span className="px-2 py-1 leading-none text-xs bg-vega-cdark-700 rounded">
{Schema.AccountTypeMapping[type]}
</span>
</Tooltip>
{isRedeemable ? (
<Tooltip description={t('RedeemRewardsTooltip')}>
<AnchorButton
variant="primary"
size="xs"
href={transferAssetLink(assetId)}
target="_blank"
className="px-2 py-1 leading-none text-xs bg-vega-yellow text-black rounded"
>
{t('Redeem')}
</AnchorButton>
</Tooltip>
) : null}
</div>
) : null}
<div className="basis-full font-mono" data-testid="currency-value">
<span>
{integers}
{separator}
</span>
<span className="text-neutral-400">{decimalsPlaces}</span>
{values}
</div>
</div>
);
};
const useAccountTypeTooltip = (type?: Schema.AccountType) => {
const { t } = useTranslation();
const { param: baseRate } = useNetworkParam('rewards_vesting_baseRate');
const accountTypeTooltip = useMemo(() => {
if (type === Schema.AccountType.ACCOUNT_TYPE_VESTED_REWARDS) {
return t('VestedRewardsTooltip');
}
if (type === Schema.AccountType.ACCOUNT_TYPE_VESTING_REWARDS && baseRate) {
return t('VestingRewardsTooltip', { baseRate });
}
return null;
}, [baseRate, t, type]);
return accountTypeTooltip;
};
const CurrencyValue = ({
balance,
decimals,
type,
assetId,
}: {
balance: BigNumber;
decimals: number;
type?: Schema.AccountType;
assetId?: string;
}) => {
const { t } = useTranslation();
const consoleLink = useLinks(DApp.Console);
const transferAssetLink = (assetId: string) =>
consoleLink(CONSOLE_TRANSFER_ASSET.replace(':assetId', assetId));
const [integers, decimalsPlaces, separator] = useNumberParts(
balance,
decimals
);
const accountTypeTooltip = useAccountTypeTooltip(type);
const accountType = type && (
<Tooltip description={accountTypeTooltip}>
<span className="px-2 py-1 leading-none text-xs bg-vega-cdark-700 rounded">
{Schema.AccountTypeMapping[type]}
</span>
</Tooltip>
);
const isRedeemable =
type === Schema.AccountType.ACCOUNT_TYPE_VESTED_REWARDS && assetId;
const redeemBtn = isRedeemable ? (
<Tooltip description={t('RedeemRewardsTooltip')}>
<AnchorButton
variant="primary"
size="xs"
href={transferAssetLink(assetId)}
target="_blank"
className="px-2 py-1 leading-none text-xs bg-vega-yellow text-black rounded"
>
{t('Redeem')}
</AnchorButton>
</Tooltip>
) : null;
return (
<div
className="basis-full font-mono mb-1"
data-account-type={type?.toLowerCase() || 'unspecified'}
data-testid="currency-value"
>
{type && (
<div data-type className="flex gap-1">
{accountType}
{redeemBtn}
</div>
)}
<div data-value>
<span>
{integers}
{separator}
</span>
<span className="text-neutral-400">{decimalsPlaces}</span>
</div>
</div>
);
@@ -9,7 +9,7 @@ import { useWeb3React } from '@web3-react/core';
import React from 'react';
import { SplashLoader } from '../../components/splash-loader';
import type { ContractsContextShape } from './contracts-context';
import { type ContractsContextShape } from './contracts-context';
import { ContractsContext } from './contracts-context';
import { createDefaultProvider } from '../../lib/web3-connectors';
import { useEthereumConfig } from '@vegaprotocol/web3';
+27 -14
View File
@@ -1,29 +1,42 @@
import type { Module } from 'i18next';
import i18n from 'i18next';
import HttpBackend from 'i18next-http-backend';
import LocizeBackend from 'i18next-locize-backend';
import LanguageDetector from 'i18next-browser-languagedetector';
import { initReactI18next } from 'react-i18next';
import dev from './translations/dev.json';
const isInDev = process.env.NODE_ENV === 'development';
const useLocize = isInDev && !!process.env.NX_USE_LOCIZE;
const backend = useLocize
? {
projectId: '96ac1231-4bdd-455a-b9d7-f5322a2e7430',
apiKey: process.env.NX_LOCIZE_API_KEY,
referenceLng: 'en',
}
: {
loadPath: '/assets/locales/{{lng}}/{{ns}}.json',
};
const Backend: Module = useLocize ? LocizeBackend : HttpBackend;
i18n
.use(Backend)
.use(LanguageDetector)
.use(initReactI18next)
.init({
// we init with resources
resources: {
en: {
translations: {
...dev,
},
},
},
lng: undefined,
lng: 'en',
fallbackLng: 'en',
debug: true,
supportedLngs: ['en'],
load: 'languageOnly',
debug: isInDev,
// have a common namespace used around the full app
ns: ['translations'],
defaultNS: 'translations',
ns: ['governance'],
defaultNS: 'governance',
keySeparator: false, // we use content as keys
nsSeparator: false,
backend,
saveMissing: useLocize && !!process.env.NX_LOCIZE_API_KEY,
interpolation: {
escapeValue: false,
},
@@ -1,4 +1,4 @@
import type { CollateralBridge } from '@vegaprotocol/smart-contracts';
import { type CollateralBridge } from '@vegaprotocol/smart-contracts';
import * as Schema from '@vegaprotocol/types';
import { Button } from '@vegaprotocol/ui-toolkit';
import { useBridgeContract, useEthereumTransaction } from '@vegaprotocol/web3';
@@ -88,7 +88,7 @@ export const ListAsset = ({
assetData.erc20ListAssetBundle;
return (
<div className="mb-8">
<h3 className="text-xl mb-2">{t('ListAsset')}</h3>
<h3 className="mb-2 text-xl">{t('ListAsset')}</h3>
<p className="pr-8">{t('ListAssetDescription')}</p>
<EthWalletContainer>
<Button
@@ -4,7 +4,7 @@ import { VoteValue } from '@vegaprotocol/types';
import { useEffect, useState } from 'react';
import { useUserVoteQuery } from './__generated__/Vote';
import { removePaginationWrapper } from '@vegaprotocol/utils';
import type { FinalizedVote } from '@vegaprotocol/proposals';
import { type FinalizedVote } from '@vegaprotocol/proposals';
export enum VoteState {
NotCast = 'NotCast',
@@ -5,15 +5,17 @@ import { useTranslation } from 'react-i18next';
import { SplashLoader } from '../../../components/splash-loader';
import { ProposalsList } from '../components/proposals-list';
import { useProposalsQuery } from './__generated__/Proposals';
import { getNodes, removePaginationWrapper } from '@vegaprotocol/utils';
import {
ProposalState,
ProtocolUpgradeProposalStatus,
} from '@vegaprotocol/types';
import type { NodeConnection, NodeEdge } from '@vegaprotocol/utils';
import type { ProposalFieldsFragment } from './__generated__/Proposals';
import type { ProtocolUpgradeProposalFieldsFragment } from '@vegaprotocol/proposals';
import { type NodeConnection, type NodeEdge } from '@vegaprotocol/utils';
import {
useProposalsQuery,
type ProposalFieldsFragment,
} from './__generated__/Proposals';
import { type ProtocolUpgradeProposalFieldsFragment } from '@vegaprotocol/proposals';
import { useProtocolUpgradeProposalsQuery } from '@vegaprotocol/proposals';
import { FLAGS } from '@vegaprotocol/environment';
@@ -5,9 +5,9 @@ import * as faker from 'faker';
import isArray from 'lodash/isArray';
import mergeWith from 'lodash/mergeWith';
import type { PartialDeep } from 'type-fest';
import type { ProposalQuery } from '../proposal/__generated__/Proposal';
import type { ProtocolUpgradeProposalFieldsFragment } from '@vegaprotocol/proposals';
import { type PartialDeep } from 'type-fest';
import { type ProposalQuery } from '../proposal/__generated__/Proposal';
import { type ProtocolUpgradeProposalFieldsFragment } from '@vegaprotocol/proposals';
export function generateProtocolUpgradeProposal(
override: PartialDeep<ProtocolUpgradeProposalFieldsFragment> = {}
@@ -22,11 +22,13 @@ interface UserBalances {
balance: BigNumber;
}
type Params = { address: string };
export const RedemptionInformation = () => {
const { t } = useTranslation();
const navigate = useNavigate();
const tranches = useTranches((state) => state.tranches);
const { address } = useParams<{ address: string }>();
const { address } = useParams<Params>();
const [userBalances, setUserBalances] = useState<null | UserBalances>();
const getUsersBalances = useGetUserBalances(address);
useEffect(() => {
@@ -84,7 +86,7 @@ export const RedemptionInformation = () => {
i18nKey="noVestingTokens"
components={{
tranchesLink: (
<Link className="underline text-white" to={Routes.SUPPLY} />
<Link className="text-white underline" to={Routes.SUPPLY} />
),
}}
/>
@@ -160,7 +162,7 @@ export const RedemptionInformation = () => {
intent={Intent.Warning}
>
<p>{t('Find out more about Staking.')}</p>
<Link to={Routes.VALIDATORS} className="underline text-white">
<Link to={Routes.VALIDATORS} className="text-white underline">
{t('Stake VEGA tokens')}
</Link>
</Callout>
@@ -21,9 +21,10 @@ import RoutesConfig from '../routes';
interface FormFields {
address: string;
}
type Params = { address: string };
const RedemptionRouter = () => {
const { address } = useParams<{ address: string }>();
const { address } = useParams<Params>();
const navigate = useNavigate();
const { t } = useTranslation();
const validatePubkey = useCallback(
@@ -89,7 +90,7 @@ const RedemptionRouter = () => {
{t('View connected Eth Wallet')}
</Button>
)}
<p className="py-4 flex justify-center">{t('OR')}</p>
<p className="flex justify-center py-4">{t('OR')}</p>
<form
onSubmit={handleSubmit(onSubmit)}
data-testid="view-connector-form"
@@ -21,6 +21,8 @@ import { EthConnectPrompt } from '../../../components/eth-connect-prompt';
import { useUserTrancheBalances } from '../hooks';
import { useAppState } from '../../../contexts/app-state/app-state-context';
type Params = { id: string };
export const RedeemFromTranche = () => {
const { account: address } = useWeb3React();
const { vesting } = useContracts();
@@ -34,7 +36,7 @@ export const RedeemFromTranche = () => {
tranches: state.tranches,
getTranches: state.getTranches,
}));
const { id } = useParams<{ id: string }>();
const { id } = useParams<Params>();
const numberId = Number(id);
const tranche = React.useMemo(
() => tranches?.find(({ tranche_id }) => tranche_id === numberId) || null,
@@ -86,7 +88,7 @@ export const RedeemFromTranche = () => {
i18nKey="noVestingTokens"
components={{
tranchesLink: (
<Link className="underline text-white" to={Routes.SUPPLY} />
<Link className="text-white underline" to={Routes.SUPPLY} />
),
}}
/>
@@ -128,13 +130,13 @@ export const RedeemFromTranche = () => {
components={{
stakingLink: (
<Link
className="underline text-white"
className="text-white underline"
to={Routes.VALIDATORS}
/>
),
governanceLink: (
<Link
className="underline text-white"
className="text-white underline"
to={Routes.PROPOSALS}
/>
),
@@ -11,12 +11,12 @@ import { useTransaction } from '../../../hooks/use-transaction';
import { useAppState } from '../../../contexts/app-state/app-state-context';
import { removeDecimal, removePaginationWrapper } from '@vegaprotocol/utils';
import * as Schema from '@vegaprotocol/types';
import type {
LinkingsFieldsFragment,
PartyStakeLinkingsQuery,
PartyStakeLinkingsQueryVariables,
import {
PartyStakeLinkingsDocument,
type LinkingsFieldsFragment,
type PartyStakeLinkingsQuery,
type PartyStakeLinkingsQueryVariables,
} from './__generated__/PartyStakeLinkings';
import { PartyStakeLinkingsDocument } from './__generated__/PartyStakeLinkings';
export const useAddStake = (
address: string,
@@ -26,9 +26,9 @@ import {
ValidatorRenderer,
VotingPowerRenderer,
} from './shared';
import type { AgGridReact } from 'ag-grid-react';
import type { ColDef, RowHeightParams } from 'ag-grid-community';
import type { ValidatorsTableProps } from './shared';
import { type AgGridReact } from 'ag-grid-react';
import { type ColDef, type RowHeightParams } from 'ag-grid-community';
import { type ValidatorsTableProps } from './shared';
import {
formatNumber,
formatNumberPercentage,
@@ -83,19 +83,19 @@ const TopThirdCellRenderer = (
e.preventDefault();
setHideTopThird(false);
}}
className="grid grid-cols-[60px_1fr] w-full h-full py-4 px-0 text-sm text-white text-center overflow-scroll"
className="grid h-full w-full grid-cols-[60px_1fr] overflow-scroll px-0 py-4 text-center text-sm text-white"
>
<div className="px-3 text-xs text-left">
<div className="px-3 text-left text-xs">
{params?.data?.rankingDisplay}
</div>
<div className="px-3 whitespace-normal">
<div className="whitespace-normal px-3">
<div className="mb-4">
<Button
data-testid="show-all-validators"
rightIcon={
<Icon
name="arrow-right"
className="mr-2 align-text-top fill-current"
className="mr-2 fill-current align-text-top"
/>
}
className="inline-flex items-center"
@@ -22,9 +22,9 @@ import {
PendingStakeRenderer,
VotingPowerRenderer,
} from './shared';
import type { AgGridReact } from 'ag-grid-react';
import type { ColDef } from 'ag-grid-community';
import type { ValidatorsTableProps } from './shared';
import { type AgGridReact } from 'ag-grid-react';
import { type ColDef } from 'ag-grid-community';
import { type ValidatorsTableProps } from './shared';
import {
formatNumber,
formatNumberPercentage,
@@ -28,12 +28,14 @@ interface StakingNodeProps {
previousEpochData?: PreviousEpochQuery;
}
type Params = { node: string };
export const StakingNode = ({ data, previousEpochData }: StakingNodeProps) => {
const { pubKey: vegaKey } = useVegaWallet();
const {
appState: { decimals },
} = useAppState();
const { node } = useParams<{ node: string }>();
const { node } = useParams<Params>();
const { t } = useTranslation();
const { nodeInfo, currentEpoch, delegations } = React.useMemo(
@@ -16,10 +16,12 @@ import Routes from '../routes';
import { TrancheLabel } from './tranche-label';
import { useTranches } from '../../lib/tranches/tranches-store';
type Params = { trancheId: string; address: string };
export const Tranche = () => {
const tranches = useTranches((state) => state.tranches);
const { t } = useTranslation();
const { trancheId } = useParams<{ trancheId: string; address: string }>();
const { trancheId } = useParams<Params>();
const { chainId } = useWeb3React();
const tranche = tranches?.find(
(tranche) => trancheId && parseInt(trancheId) === tranche.tranche_id
@@ -41,7 +43,7 @@ export const Tranche = () => {
}
/>
<div
className="flex justify-between gap-x-4 py-2 px-4"
className="flex justify-between gap-x-4 px-4 py-2"
data-testid="redeemed-tranche-tokens"
>
<span>{t('alreadyRedeemed')}</span>
+4 -10
View File
@@ -3,7 +3,7 @@
// expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom
import '@testing-library/jest-dom';
import dev from './i18n/translations/dev.json';
import { locales } from '@vegaprotocol/i18n';
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import ResizeObserver from 'resize-observer-polyfill';
@@ -12,16 +12,10 @@ import ResizeObserver from 'resize-observer-polyfill';
// en translations
i18n.use(initReactI18next).init({
// we init with resources
resources: {
en: {
translations: {
...dev,
},
},
},
resources: locales,
fallbackLng: 'en',
ns: ['translations'],
defaultNS: 'translations',
ns: ['governance'],
defaultNS: 'governance',
});
global.ResizeObserver = ResizeObserver;
@@ -1,4 +0,0 @@
[[redirects]]
from = "/*"
to = "/index.html"
status = 200
@@ -82,15 +82,6 @@
"jestConfig": "apps/liquidity-provision-dashboard/jest.config.ts"
}
},
"build-netlify": {
"executor": "nx:run-commands",
"options": {
"commands": [
"cp apps/liquidity-provision-dashboard/netlify.toml netlify.toml",
"nx build liquidity-provision-dashboard"
]
}
},
"build-spec": {
"executor": "nx:run-commands",
"outputs": [],
@@ -1,5 +1,5 @@
import { DApp, useLinks } from '@vegaprotocol/environment';
import type { Market } from '@vegaprotocol/liquidity';
import { type Market } from '@vegaprotocol/liquidity';
import {
displayChange,
formatWithAsset,
@@ -12,7 +12,7 @@ import {
toBigNum,
} from '@vegaprotocol/utils';
import { t } from '@vegaprotocol/i18n';
import type { VegaValueFormatterParams } from '@vegaprotocol/datagrid';
import { type VegaValueFormatterParams } from '@vegaprotocol/datagrid';
import { PriceChangeCell } from '@vegaprotocol/datagrid';
import type * as Schema from '@vegaprotocol/types';
import {
@@ -21,10 +21,10 @@ import {
HealthBar,
TooltipCellComponent,
} from '@vegaprotocol/ui-toolkit';
import type {
GetRowIdParams,
RowClickedEvent,
ColDef,
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';
@@ -253,7 +253,7 @@ export const MarketList = () => {
return (
<AsyncRenderer loading={loading} error={error} data={data}>
<div
className="grow w-full"
className="w-full grow"
style={{ minHeight: 500, overflow: 'hidden' }}
>
<Grid
@@ -60,19 +60,21 @@ const useMarketDetails = (marketId: string | undefined) => {
};
};
type Params = { marketId: string };
export const Detail = () => {
const { marketId } = useParams<{ marketId: string }>();
const { marketId } = useParams<Params>();
const { data, loading, error } = useMarketDetails(marketId);
return (
<AsyncRenderer loading={loading} error={error} data={data}>
<div className="px-16 pt-14 pb-12 bg-greys-light-100">
<div className="max-w-screen-xl mx-auto">
<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="max-w-screen-xl mx-auto">
<div className="mx-auto max-w-screen-xl">
<div className="py-12">
{marketId && (
<Market
@@ -86,7 +88,7 @@ export const Detail = () => {
)}
</div>
<div>
<h2 className="font-alpha calt text-2xl mb-4">
<h2 className="font-alpha calt mb-4 text-2xl">
{t('Current Liquidity Provision')}
</h2>
<LPProvidersGrid
@@ -1,11 +1,11 @@
import { useCallback, useMemo } from 'react';
import type { GetRowIdParams, ColDef } from 'ag-grid-community';
import { type GetRowIdParams, type ColDef } from 'ag-grid-community';
import { t } from '@vegaprotocol/i18n';
import type {
LiquidityProviderFeeShareFieldsFragment,
LiquidityProvisionFieldsFragment,
import {
type LiquidityProviderFeeShareFieldsFragment,
type LiquidityProvisionFieldsFragment,
} from '@vegaprotocol/liquidity';
import { formatWithAsset } from '@vegaprotocol/liquidity';
@@ -1,9 +1,9 @@
import { useRef, useCallback, useEffect } from 'react';
import { AgGridReact } from 'ag-grid-react';
import type {
AgGridReactProps,
AgReactUiProps,
AgGridReact as AgGridReactType,
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';
@@ -34,7 +34,7 @@ export const Grid = ({ isRowClickable, ...props }: Props) => {
return (
<AgGridReact
className={classNames('ag-theme-alpine h-full font-alpha calt', {
className={classNames('ag-theme-alpine font-alpha calt h-full', {
'row-hover': isRowClickable,
})}
rowHeight={92}
-4
View File
@@ -1,4 +0,0 @@
[[redirects]]
from = "/*"
to = "/index.html"
status = 200
-9
View File
@@ -66,15 +66,6 @@
"jestConfig": "apps/multisig-signer/jest.config.ts"
}
},
"build-netlify": {
"executor": "nx:run-commands",
"options": {
"commands": [
"cp apps/multisig-signer/netlify.toml netlify.toml",
"nx build multisig-signer"
]
}
},
"build-spec": {
"executor": "nx:run-commands",
"outputs": [],
@@ -11,12 +11,12 @@ import {
Loader,
} from '@vegaprotocol/ui-toolkit';
import { useContracts } from '../../config/contracts/contracts-context';
import type { FormEvent } from 'react';
import type {
AddSignerBundle,
AddSignerBundleVariables,
import { type FormEvent } from 'react';
import {
type AddSignerBundle,
type AddSignerBundleVariables,
} from '../__generated__/AddSignerBundle';
import type { MultisigControl } from '@vegaprotocol/smart-contracts';
import { type MultisigControl } from '@vegaprotocol/smart-contracts';
export const ADD_SIGNER_QUERY = gql`
query AddSignerBundle($nodeId: ID!) {
@@ -11,12 +11,12 @@ import {
Loader,
} from '@vegaprotocol/ui-toolkit';
import { useContracts } from '../../config/contracts/contracts-context';
import type { FormEvent } from 'react';
import type {
RemoveSignerBundle,
RemoveSignerBundleVariables,
import { type FormEvent } from 'react';
import {
type RemoveSignerBundle,
type RemoveSignerBundleVariables,
} from '../__generated__/RemoveSignerBundle';
import type { MultisigControl } from '@vegaprotocol/smart-contracts';
import { type MultisigControl } from '@vegaprotocol/smart-contracts';
const REMOVE_SIGNER_QUERY = gql`
query RemoveSignerBundle($nodeId: ID!) {
@@ -1,7 +1,7 @@
import { useEffect, useState } from 'react';
import { MultisigControl } from '@vegaprotocol/smart-contracts';
import { Splash } from '@vegaprotocol/ui-toolkit';
import type { ContractsContextShape } from './contracts-context';
import { type ContractsContextShape } from './contracts-context';
import { ContractsContext } from './contracts-context';
import { useEthereumConfig } from '@vegaprotocol/web3';
import { useEnvironment } from '@vegaprotocol/environment';
-4
View File
@@ -1,4 +0,0 @@
[[headers]]
for = "/*"
[headers.values]
Access-Control-Allow-Origin = "*"
-9
View File
@@ -46,15 +46,6 @@
"buildTarget": "static:build:production"
}
}
},
"build-netlify": {
"executor": "nx:run-commands",
"options": {
"commands": [
"cp apps/static/netlify.toml netlify.toml",
"nx build static"
]
}
}
}
}
+2 -1
View File
@@ -1,9 +1,10 @@
import { t } from '@vegaprotocol/i18n';
import { useT } from '../../lib/use-t';
import { Links } from '../../lib/links';
import classNames from 'classnames';
import { NavLink, Outlet } from 'react-router-dom';
export const Assets = () => {
const t = useT();
const linkClasses = ({ isActive }: { isActive: boolean }) => {
return classNames('border-b-2 border-transparent', {
'border-vega-yellow': isActive,
@@ -1,4 +1,3 @@
import { t } from '@vegaprotocol/i18n';
import { Intent, TradingAnchorButton } from '@vegaprotocol/ui-toolkit';
import { GetStartedCheckList } from '../../components/welcome-dialog';
import {
@@ -8,8 +7,10 @@ import {
} from '../../components/welcome-dialog/use-get-onboarding-step';
import { Links } from '../../lib/links';
import classNames from 'classnames';
import { useT } from '../../lib/use-t';
export const DepositGetStarted = () => {
const t = useT();
const onboardingDismissed = useOnboardingStore((store) => store.dismissed);
const dismiss = useOnboardingStore((store) => store.dismiss);
const step = useGetOnboardingStep();
@@ -1,6 +1,7 @@
import { t } from '@vegaprotocol/i18n';
import { useT } from '../../lib/use-t';
export const Disclaimer = () => {
const t = useT();
return (
<>
<h1 className="text-4xl uppercase xl:text-5xl font-alpha calt">
@@ -8,37 +9,44 @@ export const Disclaimer = () => {
</h1>
<p className="mt-10 mb-6">
{t(
'Vega is a decentralised peer-to-peer protocol that can be used to trade derivatives with cryptoassets. The Vega Protocol is an implementation layer (layer one) protocol made of free, public, open-source or source-available software. Use of the Vega Protocol involves various risks, including but not limited to, losses while digital assets are supplied to the Vega Protocol and losses due to the fluctuation of prices of assets.'
'DISCLAIMER_P1',
'Vega is a decentralised peer-to-peer protocol that can be used to trade derivatives with cryptoassets. The Vega Protocol is an implementation layer (layer one) protocol made of free, public, open-source or source-available software. Use of the Vega Protocol involves various risks, including but not limited to, losses while digital assets are supplied to the Vega Protocol and losses due to the fluctuation of prices of assets.'
)}
</p>
<p className="mb-6">
{t(
'Before using the Vega Protocol, review the relevant documentation at docs.vega.xyz to make sure that you understand how it works. Conduct your own due diligence and consult your financial advisor before making any investment decisions.'
'DISCLAIMER_P2',
'Before using the Vega Protocol, review the relevant documentation at docs.vega.xyz to make sure that you understand how it works. Conduct your own due diligence and consult your financial advisor before making any investment decisions.'
)}
</p>
<p className="mb-6">
{t(
'As described in the Vega Protocol core license, the Vega Protocol is provided “as is”, at your own risk, and without warranties of any kind. Although Gobalsky Labs Limited developed much of the initial code for the Vega Protocol, it does not provide or control the Vega Protocol, which is run by third parties deploying it on a bespoke blockchain. Upgrades and modifications to the Vega Protocol are managed in a community-driven way by holders of the VEGA governance token.'
'DISCLAIMER_P3',
'As described in the Vega Protocol core license, the Vega Protocol is provided “as is”, at your own risk, and without warranties of any kind. Although Gobalsky Labs Limited developed much of the initial code for the Vega Protocol, it does not provide or control the Vega Protocol, which is run by third parties deploying it on a bespoke blockchain. Upgrades and modifications to the Vega Protocol are managed in a community-driven way by holders of the VEGA governance token.'
)}
</p>
<p className="mb-8">
{t(
'DISCLAIMER_P4',
'No developer or entity involved in creating the Vega Protocol will be liable for any claims or damages whatsoever associated with your use, inability to use, or your interaction with other users of the Vega Protocol, including any direct, indirect, incidental, special, exemplary, punitive or consequential damages, or legal costs, or loss of profits, cryptoassets, tokens or anything else of value.'
)}
</p>
<p className="mb-8">
{t(
'This website is hosted on a decentralised network, the Interplanetary File System (“IPFS”). The IPFS decentralised web is made up of all the computers (nodes) connected to it. Data is therefore stored on many different computers.'
'DISCLAIMER_P5',
'This website is hosted on a decentralised network, the Interplanetary File System (“IPFS”). The IPFS decentralised web is made up of all the computers (nodes) connected to it. Data is therefore stored on many different computers.'
)}
</p>
<p className="mb-8">
{t(
"The information provided on this website does not constitute investment advice, financial advice, trading advice, or any other sort of advice and you should not treat any of the website's content as such. No party recommends that any cryptoasset should be bought, sold, or held by you via this website. No party ensures the accuracy of information listed on this website or holds any responsibility for any missing or wrong information. You understand that you are using any and all information available here at your own risk."
'DISCLAIMER_P6',
"The information provided on this website does not constitute investment advice, financial advice, trading advice, or any other sort of advice and you should not treat any of the website's content as such. No party recommends that any cryptoasset should be bought, sold, or held by you via this website. No party ensures the accuracy of information listed on this website or holds any responsibility for any missing or wrong information. You understand that you are using any and all information available here at your own risk."
)}
</p>
<p className="mb-8">
{t(
'Additionally, just as you can access email protocols such as SMTP through multiple email clients, you can potentially access the Vega Protocol through many web or mobile interfaces. You are responsible for doing your own diligence on those interfaces to understand the associated risks and any fees.'
'DISCLAIMER_P7',
'Additionally, just as you can access email protocols such as SMTP through multiple email clients, you can potentially access the Vega Protocol through many web or mobile interfaces. You are responsible for doing your own diligence on those interfaces to understand the associated risks and any fees.'
)}
</p>
</>
+2 -1
View File
@@ -1,7 +1,8 @@
import { t } from '@vegaprotocol/i18n';
import { FeesContainer } from '../../components/fees-container';
import { useT } from '../../lib/use-t';
export const Fees = () => {
const t = useT();
return (
<div className="container p-4 mx-auto">
<h1 className="px-4 pb-4 text-2xl">{t('Fees')}</h1>
@@ -1,4 +1,3 @@
import { t } from '@vegaprotocol/i18n';
import { VegaIconNames } from '@vegaprotocol/ui-toolkit';
import {
SidebarButton,
@@ -6,8 +5,10 @@ import {
ViewType,
} from '../../components/sidebar';
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
import { useT } from '../../lib/use-t';
export const LiquiditySidebar = () => {
const t = useT();
const currentRouteId = useGetCurrentRouteId();
return (
@@ -1,11 +1,11 @@
import { matchFilter, lpAggregatedDataProvider } from '@vegaprotocol/liquidity';
import { t } from '@vegaprotocol/i18n';
import { useDataProvider } from '@vegaprotocol/data-provider';
import { Tab, Tabs } from '@vegaprotocol/ui-toolkit';
import { useVegaWallet } from '@vegaprotocol/wallet';
import { useEffect, useState } from 'react';
import { useParams } from 'react-router-dom';
import { LiquidityContainer } from '../../components/liquidity-container';
import { useT } from '../../lib/use-t';
const enum LiquidityTabs {
Active = 'active',
@@ -24,6 +24,7 @@ export const LiquidityViewContainer = ({
}: {
marketId: string | undefined;
}) => {
const t = useT();
const [tab, setTab] = useState<string | undefined>(undefined);
const { pubKey } = useVegaWallet();
@@ -1,3 +0,0 @@
import { t } from '@vegaprotocol/i18n';
export const NO_MARKET = t('No market');
@@ -9,7 +9,6 @@ import {
getExpiryDate,
getMarketExpiryDate,
} from '@vegaprotocol/utils';
import { t } from '@vegaprotocol/i18n';
import {
Last24hPriceChange,
Last24hVolume,
@@ -31,12 +30,14 @@ import { MarketLiquiditySupplied } from '../../components/liquidity-supplied';
import { useEffect, useState } from 'react';
import { useDataProvider } from '@vegaprotocol/data-provider';
import { PriceCell } from '@vegaprotocol/datagrid';
import { useT } from '../../lib/use-t';
interface MarketHeaderStatsProps {
market: Market;
}
export const MarketHeaderStats = ({ market }: MarketHeaderStatsProps) => {
const t = useT();
const { VEGA_EXPLORER_URL } = useEnvironment();
const { open: openAssetDetailsDialog } = useAssetDetailsDialogStore();
@@ -234,6 +235,7 @@ const useFormatCountdown = (
startTime?: number,
every?: number
) => {
const t = useT();
if (startTime && every) {
const diff = every - ((now - startTime) % every);
const hours = (diff / 3.6e6) | 0;
@@ -276,6 +278,7 @@ const ExpiryTooltipContent = ({
market,
explorerUrl,
}: ExpiryTooltipContentProps) => {
const t = useT();
if (market.marketTimestamps.close === null) {
const oracleId =
market.tradableInstrument.instrument.product.__typename === 'Future'
+15 -5
View File
@@ -1,6 +1,5 @@
import React, { useEffect, useMemo } from 'react';
import { addDecimalsFormatNumber, titlefy } from '@vegaprotocol/utils';
import { t } from '@vegaprotocol/i18n';
import { useScreenDimensions } from '@vegaprotocol/react-helpers';
import { useThrottledDataProvider } from '@vegaprotocol/data-provider';
import { ExternalLink, Loader, Splash } from '@vegaprotocol/ui-toolkit';
@@ -12,6 +11,8 @@ import { useNavigate, useParams } from 'react-router-dom';
import { Links } from '../../lib/links';
import { ViewType, useSidebar } from '../../components/sidebar';
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
import { useT, ns } from '../../lib/use-t';
import { Trans } from 'react-i18next';
const calculatePrice = (markPrice?: string, decimalPlaces?: number) => {
return markPrice && decimalPlaces
@@ -57,6 +58,7 @@ const TitleUpdater = ({
};
export const MarketPage = () => {
const t = useT();
const { marketId } = useParams();
const navigate = useNavigate();
const currentRouteId = useGetCurrentRouteId();
@@ -111,10 +113,18 @@ export const MarketPage = () => {
{t('This market URL is not available any more.')}
</p>
<p className="justify-center text-sm">
{t(`Please choose another market from the`)}{' '}
<ExternalLink onClick={() => navigate(Links.MARKETS())}>
{t('market list')}
</ExternalLink>
<Trans
defaults="Please choose another market from the <0>market list<0>"
ns={ns}
components={[
<ExternalLink
onClick={() => navigate(Links.MARKETS())}
key="link"
>
market list
</ExternalLink>,
]}
/>
</p>
</span>
</Splash>
@@ -4,10 +4,8 @@ import { LayoutPriority } from 'allotment';
import classNames from 'classnames';
import AutoSizer from 'react-virtualized-auto-sizer';
import type { PinnedAsset } from '@vegaprotocol/accounts';
import { t } from '@vegaprotocol/i18n';
import { OracleBanner, useMarket } from '@vegaprotocol/markets';
import type { Market } from '@vegaprotocol/markets';
import { Filter } from '@vegaprotocol/orders';
import { Tab, LocalStoragePersistTabs as Tabs } from '@vegaprotocol/ui-toolkit';
import {
ResizableGrid,
@@ -21,6 +19,7 @@ import {
MarketTerminationBanner,
} from '../../components/market-banner';
import { FLAGS } from '@vegaprotocol/environment';
import { useT } from '../../lib/use-t';
interface TradeGridProps {
market: Market | null;
@@ -35,6 +34,7 @@ const MainGrid = memo(
marketId: string;
pinnedAsset?: PinnedAsset;
}) => {
const t = useT();
const { data: market } = useMarket(marketId);
const [sizes, handleOnLayoutChange] = usePaneLayout({ id: 'top' });
const [sizesMiddle, handleOnMiddleLayoutChange] = usePaneLayout({
@@ -125,13 +125,13 @@ const MainGrid = memo(
name={t('Open')}
menu={<TradingViews.activeOrders.menu />}
>
<TradingViews.orders.component filter={Filter.Open} />
<TradingViews.activeOrders.component />
</Tab>
<Tab id="closed-orders" name={t('Closed')}>
<TradingViews.orders.component filter={Filter.Closed} />
<TradingViews.closedOrders.component />
</Tab>
<Tab id="rejected-orders" name={t('Rejected')}>
<TradingViews.orders.component filter={Filter.Rejected} />
<TradingViews.rejectedOrders.component />
</Tab>
<Tab
id="orders"
@@ -4,8 +4,6 @@ import { OracleBanner } from '@vegaprotocol/markets';
import type { TradingView } from './trade-views';
import { TradingViews } from './trade-views';
import { useState } from 'react';
import { Splash } from '@vegaprotocol/ui-toolkit';
import { NO_MARKET } from './constants';
import AutoSizer from 'react-virtualized-auto-sizer';
import classNames from 'classnames';
import {
@@ -14,6 +12,8 @@ import {
MarketTerminationBanner,
} from '../../components/market-banner';
import { FLAGS } from '@vegaprotocol/environment';
import { useT } from '../../lib/use-t';
import { Splash } from '@vegaprotocol/ui-toolkit';
interface TradePanelsProps {
market: Market | null;
@@ -30,8 +30,10 @@ export const TradePanels = ({ market, pinnedAsset }: TradePanelsProps) => {
throw new Error(`No component for view: ${view}`);
}
if (!market) return <Splash>{NO_MARKET}</Splash>;
if (!market) return <NoMarketSplash />;
// Watch out here, we don't know what component is being rendered
// so watch out for clashes in props
return <Component marketId={market?.id} pinnedAsset={pinnedAsset} />;
};
@@ -74,33 +76,89 @@ export const TradePanels = ({ market, pinnedAsset }: TradePanelsProps) => {
</AutoSizer>
</div>
<div className="flex flex-nowrap overflow-x-auto max-w-full border-t border-default">
{Object.keys(TradingViews).map((key) => {
const isActive = view === key;
const className = classNames(
'py-2 px-4 min-w-[100px] capitalize text-sm',
{
'bg-vega-clight-500 dark:bg-vega-cdark-500': isActive,
{Object.keys(TradingViews)
// filter to control available views for the current market
// eg only perps should get the funding views
.filter((_key) => {
const key = _key as TradingView;
const perpOnlyViews = ['funding', 'fundingPayments'];
if (
market?.tradableInstrument.instrument.product.__typename ===
'Perpetual'
) {
return true;
}
);
if (
market?.tradableInstrument.instrument.product.__typename !==
'Perpetual' &&
(key === 'funding' || key === 'fundingPayments')
) {
return null;
}
return (
<button
data-testid={key}
onClick={() => setView(key as TradingView)}
className={className}
key={key}
>
{TradingViews[key as keyof typeof TradingViews].label}
</button>
);
})}
if (perpOnlyViews.includes(key)) {
return false;
}
return true;
})
.map((_key) => {
const key = _key as TradingView;
const isActive = view === key;
return (
<ViewButton
key={key}
view={key}
isActive={isActive}
onClick={() => setView(key)}
/>
);
})}
</div>
</div>
);
};
export const NoMarketSplash = () => {
const t = useT();
return <Splash>{t('No market')}</Splash>;
};
const ViewButton = ({
view,
isActive,
onClick,
}: {
view: TradingView;
isActive: boolean;
onClick: () => void;
}) => {
const label = useViewLabel(view);
const className = classNames('py-2 px-4 min-w-[100px] capitalize text-sm', {
'bg-vega-clight-500 dark:bg-vega-cdark-500': isActive,
});
return (
<button data-testid={view} onClick={onClick} className={className}>
{label}
</button>
);
};
const useViewLabel = (view: TradingView) => {
const t = useT();
const labels = {
candles: t('Candles'),
depth: t('Depth'),
liquidity: t('Liquidity'),
funding: t('Funding'),
fundingPayments: t('Funding Payments'),
orderbook: t('Orderbook'),
trades: t('Trades'),
positions: t('Positions'),
activeOrders: t('Active'),
closedOrders: t('Closed'),
rejectedOrders: t('Rejected'),
orders: t('All'),
stopOrders: t('Stop'),
collateral: t('Collateral'),
fills: t('Fills'),
};
return labels[view];
};
@@ -1,12 +1,9 @@
import type { ComponentProps } from 'react';
import { Splash } from '@vegaprotocol/ui-toolkit';
import { DepthChartContainer } from '@vegaprotocol/market-depth';
import {
CandlesChartContainer,
CandlesMenu,
} from '@vegaprotocol/candles-chart';
import { Filter, OpenOrdersMenu } from '@vegaprotocol/orders';
import { NO_MARKET } from './constants';
import { TradesContainer } from '../../components/trades-container';
import { OrderbookContainer } from '../../components/orderbook-container';
import { FillsContainer } from '../../components/fills-container';
@@ -15,96 +12,60 @@ import { AccountsContainer } from '../../components/accounts-container';
import { LiquidityContainer } from '../../components/liquidity-container';
import { FundingContainer } from '../../components/funding-container';
import { FundingPaymentsContainer } from '../../components/funding-payments-container';
import type { OrderContainerProps } from '../../components/orders-container';
import { OrdersContainer } from '../../components/orders-container';
import { StopOrdersContainer } from '../../components/stop-orders-container';
import { AccountsMenu } from '../../components/accounts-menu';
import { PositionsMenu } from '../../components/positions-menu';
type MarketDependantView =
| typeof CandlesChartContainer
| typeof DepthChartContainer
| typeof OrderbookContainer
| typeof TradesContainer;
type MarketDependantViewProps = ComponentProps<MarketDependantView>;
const requiresMarket = (View: MarketDependantView) => {
const WrappedComponent = (props: MarketDependantViewProps) =>
props.marketId ? <View {...props} /> : <Splash>{NO_MARKET}</Splash>;
WrappedComponent.displayName = `RequiresMarket(${View.name})`;
return WrappedComponent;
};
export type TradingView = keyof typeof TradingViews;
export const TradingViews = {
candles: {
label: 'Candles',
component: requiresMarket(CandlesChartContainer),
component: CandlesChartContainer,
menu: CandlesMenu,
},
depth: {
label: 'Depth',
component: requiresMarket(DepthChartContainer),
component: DepthChartContainer,
},
liquidity: {
label: 'Liquidity',
component: requiresMarket(LiquidityContainer),
component: LiquidityContainer,
},
funding: {
label: 'Funding',
component: requiresMarket(FundingContainer),
component: FundingContainer,
},
fundingPayments: {
label: 'Funding Payments',
component: FundingPaymentsContainer,
},
orderbook: {
label: 'Orderbook',
component: requiresMarket(OrderbookContainer),
component: OrderbookContainer,
},
trades: {
label: 'Trades',
component: requiresMarket(TradesContainer),
component: TradesContainer,
},
positions: {
label: 'Positions',
component: PositionsContainer,
menu: PositionsMenu,
},
activeOrders: {
label: 'Active',
component: (props: OrderContainerProps) => (
<OrdersContainer {...props} filter={Filter.Open} />
),
component: () => <OrdersContainer filter={Filter.Open} />,
menu: OpenOrdersMenu,
},
closedOrders: {
label: 'Closed',
component: (props: OrderContainerProps) => (
<OrdersContainer {...props} filter={Filter.Closed} />
),
component: () => <OrdersContainer filter={Filter.Closed} />,
},
rejectedOrders: {
label: 'Rejected',
component: (props: OrderContainerProps) => (
<OrdersContainer {...props} filter={Filter.Rejected} />
),
component: () => <OrdersContainer filter={Filter.Rejected} />,
},
orders: {
label: 'All',
component: OrdersContainer,
menu: OpenOrdersMenu,
},
stopOrders: {
label: 'Stop',
component: StopOrdersContainer,
},
collateral: {
label: 'Collateral',
component: AccountsContainer,
menu: AccountsMenu,
},
fills: { label: 'Fills', component: FillsContainer },
};
fills: { component: FillsContainer },
} as const;
+3 -2
View File
@@ -8,7 +8,6 @@ import type {
import { AgGrid, COL_DEFS } from '@vegaprotocol/datagrid';
import { useDataProvider } from '@vegaprotocol/data-provider';
import { useMemo } from 'react';
import { t } from '@vegaprotocol/i18n';
import type { Asset } from '@vegaprotocol/types';
import type { ProductType } from '@vegaprotocol/types';
import { MarketState, MarketStateMapping } from '@vegaprotocol/types';
@@ -24,6 +23,7 @@ import { SettlementDateCell } from './settlement-date-cell';
import { SettlementPriceCell } from './settlement-price-cell';
import { MarketCodeCell } from './market-code-cell';
import { MarketActionsDropdown } from './market-table-actions';
import { useT } from '../../lib/use-t';
type SettlementAsset = Pick<
Asset,
@@ -127,6 +127,7 @@ const ClosedMarketsDataGrid = ({
rowData: Row[];
error: Error | undefined;
}) => {
const t = useT();
const handleOnSelect = useMarketClickHandler();
const openAssetDialog = useAssetDetailsDialogStore((store) => store.open);
@@ -274,7 +275,7 @@ const ClosedMarketsDataGrid = ({
},
},
];
}, [openAssetDialog]);
}, [openAssetDialog, t]);
return (
<AgGrid
@@ -2,7 +2,7 @@ import compact from 'lodash/compact';
import type { ProductType } from '@vegaprotocol/types';
import { ProductTypeMapping, ProductTypeShortName } from '@vegaprotocol/types';
import { StackedCell } from '@vegaprotocol/datagrid';
import { t } from '@vegaprotocol/i18n';
import { useT } from '../../lib/use-t';
export interface MarketCodeCellProps {
value: string | undefined; // market code
@@ -14,6 +14,7 @@ export interface MarketCodeCellProps {
}
export const MarketCodeCell = ({ value, data }: MarketCodeCellProps) => {
const t = useT();
if (!value || !data || !data.productType) return null;
const infoSpanClasses =
@@ -1,4 +1,3 @@
import { t } from '@vegaprotocol/i18n';
import {
TradingDropdownItem,
TradingDropdownCopyItem,
@@ -11,6 +10,7 @@ import { DApp, EXPLORER_MARKET, useLinks } from '@vegaprotocol/environment';
import { useAssetDetailsDialogStore } from '@vegaprotocol/assets';
import { useNavigate } from 'react-router-dom';
import { Links } from '../../lib/links';
import { useT } from '../../lib/use-t';
export const MarketActionsDropdown = ({
marketId,
@@ -23,6 +23,7 @@ export const MarketActionsDropdown = ({
successorMarketID: string | null | undefined;
parentMarketID: string | null | undefined;
}) => {
const t = useT();
const navigate = useNavigate();
const open = useAssetDetailsDialogStore((store) => store.open);
const linkCreator = useLinks(DApp.Explorer);
@@ -1,6 +1,5 @@
import React, { useEffect } from 'react';
import { titlefy } from '@vegaprotocol/utils';
import { t } from '@vegaprotocol/i18n';
import {
LocalStoragePersistTabs as Tabs,
Tab,
@@ -15,8 +14,10 @@ import {
TOKEN_NEW_MARKET_PROPOSAL,
useLinks,
} from '@vegaprotocol/environment';
import { useT } from '../../lib/use-t';
export const MarketsPage = () => {
const t = useT();
const { updateTitle } = usePageTitleStore((store) => ({
updateTitle: store.updateTitle,
}));
@@ -25,8 +26,8 @@ export const MarketsPage = () => {
const externalLink = governanceLink(TOKEN_NEW_MARKET_PROPOSAL);
useEffect(() => {
updateTitle(titlefy(['Markets']));
}, [updateTitle]);
updateTitle(titlefy([t('Markets')]));
}, [updateTitle, t]);
return (
<div className="h-full pt-0.5 pb-3 px-1.5">
@@ -1,6 +1,5 @@
import { Route, Routes, useParams } from 'react-router-dom';
import { MarketState } from '@vegaprotocol/types';
import { t } from '@vegaprotocol/i18n';
import { useMarket } from '@vegaprotocol/markets';
import { VegaIconNames } from '@vegaprotocol/ui-toolkit';
import {
@@ -9,8 +8,10 @@ import {
ViewType,
} from '../../components/sidebar';
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
import { useT } from '../../lib/use-t';
export const MarketsSidebar = () => {
const t = useT();
const { marketId } = useParams();
const currentRouteId = useGetCurrentRouteId();
const { data } = useMarket(marketId);
@@ -2,16 +2,17 @@ import { useDataProvider } from '@vegaprotocol/data-provider';
import type { MarketMaybeWithData } from '@vegaprotocol/markets';
import { marketListProvider } from '@vegaprotocol/markets';
import { useEffect } from 'react';
import { t } from '@vegaprotocol/i18n';
import type { CellClickedEvent } from 'ag-grid-community';
import MarketListTable from './market-list-table';
import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler';
import { Interval } from '@vegaprotocol/types';
import { useYesterday } from '@vegaprotocol/react-helpers';
import { useT } from '../../lib/use-t';
const POLLING_TIME = 2000;
export const OpenMarkets = () => {
const t = useT();
const handleOnSelect = useMarketClickHandler();
const yesterday = useYesterday();
const { data, error, reload } = useDataProvider({
@@ -1,8 +1,8 @@
import { DApp, EXPLORER_ORACLE, useLinks } from '@vegaprotocol/environment';
import { t } from '@vegaprotocol/i18n';
import { MarketState } from '@vegaprotocol/types';
import { Link } from '@vegaprotocol/ui-toolkit';
import { getDateTimeFormat } from '@vegaprotocol/utils';
import { useT } from '../../lib/use-t';
import { formatDistanceToNowStrict, isAfter } from 'date-fns';
export interface SettlementDataCellProps {
@@ -18,6 +18,7 @@ export const SettlementDateCell = ({
closeTimestamp,
marketState,
}: SettlementDataCellProps) => {
const t = useT();
const linkCreator = useLinks(DApp.Explorer);
const date = closeTimestamp ? new Date(closeTimestamp) : metaDate;
@@ -31,12 +32,12 @@ export const SettlementDateCell = ({
if (expiryHasPassed) {
if (marketState !== MarketState.STATE_SETTLED) {
text = t('Expected %s ago', distance);
text = t('Expected {{distance}} ago', { distance });
} else {
text = t('%s ago', distance);
text = t('{{distance}} ago', { distance });
}
} else {
text = t('Expected in %s', distance);
text = t('Expected in {{distance}}', { distance });
}
}
@@ -1,10 +1,10 @@
import { DApp, EXPLORER_ORACLE, useLinks } from '@vegaprotocol/environment';
import { t } from '@vegaprotocol/i18n';
import type { DataSourceFilterFragment } from '@vegaprotocol/markets';
import { useOracleSpecBindingData } from '@vegaprotocol/markets';
import { PropertyKeyType } from '@vegaprotocol/types';
import { Link } from '@vegaprotocol/ui-toolkit';
import { addDecimalsFormatNumber } from '@vegaprotocol/utils';
import { useT } from '../../lib/use-t';
export interface SettlementPriceCellProps {
oracleSpecId: string | undefined;
@@ -17,6 +17,7 @@ export const SettlementPriceCell = ({
settlementDataSpecBinding,
filter,
}: SettlementPriceCellProps) => {
const t = useT();
const linkCreator = useLinks(DApp.Explorer);
const { property, loading } = useOracleSpecBindingData(
oracleSpecId,
@@ -1,6 +1,5 @@
import { useMemo } from 'react';
import type { ColDef, ValueFormatterParams } from 'ag-grid-community';
import { t } from '@vegaprotocol/i18n';
import type {
VegaICellRendererParams,
VegaValueFormatterParams,
@@ -18,10 +17,12 @@ import type {
import { MarketActionsDropdown } from './market-table-actions';
import { calcCandleVolume, getAsset } from '@vegaprotocol/markets';
import { MarketCodeCell } from './market-code-cell';
import { useT } from '../../lib/use-t';
const { MarketTradingMode, AuctionTrigger } = Schema;
export const useColumnDefs = () => {
const t = useT();
const { open: openAssetDetailsDialog } = useAssetDetailsDialogStore();
return useMemo<ColDef[]>(
() => [
@@ -216,6 +217,6 @@ export const useColumnDefs = () => {
},
},
],
[openAssetDetailsDialog]
[openAssetDetailsDialog, t]
);
};
@@ -1,9 +1,10 @@
import { t } from '@vegaprotocol/i18n';
import { VegaIconNames } from '@vegaprotocol/ui-toolkit';
import { SidebarButton, ViewType } from '../../components/sidebar';
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
import { useT } from '../../lib/use-t';
export const PortfolioSidebar = () => {
const t = useT();
const currentRouteId = useGetCurrentRouteId();
return (
@@ -2,7 +2,6 @@ import { useEffect } from 'react';
import type { ReactNode } from 'react';
import { LayoutPriority } from 'allotment';
import { titlefy } from '@vegaprotocol/utils';
import { t } from '@vegaprotocol/i18n';
import { useIncompleteWithdrawals } from '@vegaprotocol/withdraws';
import { Tab, LocalStoragePersistTabs as Tabs } from '@vegaprotocol/ui-toolkit';
import { usePageTitleStore } from '../../stores';
@@ -25,6 +24,7 @@ import { AccountsMenu } from '../../components/accounts-menu';
import { DepositsMenu } from '../../components/deposits-menu';
import { WithdrawalsMenu } from '../../components/withdrawals-menu';
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
import { useT } from '../../lib/use-t';
const WithdrawalsIndicator = () => {
const { ready } = useIncompleteWithdrawals();
@@ -39,6 +39,7 @@ const WithdrawalsIndicator = () => {
};
export const Portfolio = () => {
const t = useT();
const currentRouteId = useGetCurrentRouteId();
const { getView, setViews } = useSidebar();
const view = getView(currentRouteId);
@@ -49,7 +50,7 @@ export const Portfolio = () => {
useEffect(() => {
updateTitle(titlefy([t('Portfolio')]));
}, [updateTitle]);
}, [updateTitle, t]);
// Make transfer sidebar open by default
useEffect(() => {
@@ -16,13 +16,13 @@ import { useVegaWallet, useVegaWalletDialogStore } from '@vegaprotocol/wallet';
import { useReferral } from './hooks/use-referral';
import { Routes } from '../../lib/links';
import { useTransactionEventSubscription } from '@vegaprotocol/web3';
import { t } from '@vegaprotocol/i18n';
import { Statistics, useStats } from './referral-statistics';
import { useReferralProgram } from './hooks/use-referral-program';
import { useT } from '../../lib/use-t';
const RELOAD_DELAY = 3000;
const validateCode = (value: string) => {
const validateCode = (value: string, t: ReturnType<typeof useT>) => {
const number = +`0x${value}`;
if (!value || value.length !== 64) {
return t('Code must be 64 characters in length');
@@ -33,6 +33,7 @@ const validateCode = (value: string) => {
};
export const ApplyCodeForm = () => {
const t = useT();
const program = useReferralProgram();
const navigate = useNavigate();
const openWalletDialog = useVegaWalletDialogStore(
@@ -59,7 +60,7 @@ export const ApplyCodeForm = () => {
const codeField = watch('code');
const { data: previewData, loading: previewLoading } = useReferral({
code: validateCode(codeField) ? codeField : undefined,
code: validateCode(codeField, t) ? codeField : undefined,
});
useEffect(() => {
@@ -223,7 +224,7 @@ export const ApplyCodeForm = () => {
hasError={Boolean(errors.code)}
{...register('code', {
required: t('You have to provide a code to apply it.'),
validate: validateCode,
validate: (value) => validateCode(value, t),
})}
placeholder="Enter a code"
className="mb-2 bg-vega-clight-900 dark:bg-vega-cdark-700"
@@ -24,13 +24,14 @@ import {
DISCLAIMER_REFERRAL_DOCS_LINK,
} from './constants';
import { useReferral } from './hooks/use-referral';
import { t } from '@vegaprotocol/i18n';
import { useT } from '../../lib/use-t';
export const CreateCodeContainer = () => {
return <CreateCodeForm />;
};
export const CreateCodeForm = () => {
const t = useT();
const [dialogOpen, setDialogOpen] = useState(false);
const openWalletDialog = useVegaWalletDialogStore(
(store) => store.openVegaWalletDialog
@@ -81,6 +82,7 @@ const CreateCodeDialog = ({
}: {
setDialogOpen: (open: boolean) => void;
}) => {
const t = useT();
const createLink = useLinks(DApp.Governance);
const { isReadOnly, pubKey, sendTx } = useVegaWallet();
const { refetch } = useReferral({ pubKey, role: 'referrer' });
@@ -93,6 +95,11 @@ const CreateCodeDialog = ({
const { stakeAvailable: currentStakeAvailable, requiredStake } =
useStakeAvailable();
const { data: referralSets } = useReferral({
pubKey,
role: 'referrer',
});
const onSubmit = () => {
if (isReadOnly || !pubKey) {
setErr('Not connected');
@@ -170,10 +177,14 @@ const CreateCodeDialog = ({
return (
<div className="flex flex-col gap-4">
<p>
{t('You need at least')}{' '}
{addDecimalsFormatNumber(requiredStake.toString(), 18)}{' '}
{t(
'VEGA staked to generate a referral code and participate in the referral program.'
'You need at least {{requiredStake}} VEGA staked to generate a referral code and participate in the referral program.',
{
requiredStake: addDecimalsFormatNumber(
requiredStake.toString(),
18
),
}
)}
</p>
<TradingAnchorButton
@@ -187,6 +198,68 @@ const CreateCodeDialog = ({
);
}
if (!referralSets) {
return (
<div className="flex flex-col gap-4">
{(status === 'idle' || status === 'loading' || status === 'error') && (
<>
{
<p>
{t(
'There is currently no referral program active, are you sure you want to create a code?'
)}
</p>
}
</>
)}
{status === 'success' && code && (
<div className="flex items-center gap-2">
<div className="flex-1 min-w-0 p-2 text-sm rounded bg-vega-clight-700 dark:bg-vega-cdark-700">
<p className="overflow-hidden whitespace-nowrap text-ellipsis">
{code}
</p>
</div>
<CopyWithTooltip text={code}>
<TradingButton
className="text-sm no-underline"
icon={<VegaIcon name={VegaIconNames.COPY} />}
>
<span>{t('Copy')}</span>
</TradingButton>
</CopyWithTooltip>
</div>
)}
<TradingButton
fill={true}
intent={Intent.Primary}
onClick={() => onSubmit()}
{...getButtonProps()}
></TradingButton>
{status === 'idle' && (
<TradingButton
fill={true}
intent={Intent.Primary}
onClick={() => {
refetch();
setDialogOpen(false);
}}
>
{t('No')}
</TradingButton>
)}
{err && <InputError>{err}</InputError>}
<div className="flex justify-center pt-5 mt-2 text-sm border-t gap-4 text-default border-default">
<ExternalLink href={ABOUT_REFERRAL_DOCS_LINK}>
{t('About the referral program')}
</ExternalLink>
<ExternalLink href={DISCLAIMER_REFERRAL_DOCS_LINK}>
{t('Disclaimer')}
</ExternalLink>
</div>
</div>
);
}
return (
<div className="flex flex-col gap-4">
{(status === 'idle' || status === 'loading' || status === 'error') && (
@@ -3,21 +3,22 @@ import { RainbowButton } from './buttons';
import { AnimatedDudeWithWire } from './graphics/dude';
import { LayoutWithSky } from './layout';
import { Routes } from '../../lib/links';
import { t } from '@vegaprotocol/i18n';
import { useT } from '../../lib/use-t';
export const ErrorBoundary = () => {
const t = useT();
const error = useRouteError();
const navigate = useNavigate();
const title = isRouteErrorResponse(error)
? `${error.status} ${error.statusText}`
: 'Something went wrong';
: t('Something went wrong');
const code = isRouteErrorResponse(error) ? error.status : 0;
const messages: Record<number, string> = {
0: 'An unknown error occurred.',
404: "The page you're looking for doesn't exists.",
0: t('An unknown error occurred.'),
404: t("The page you're looking for doesn't exists."),
};
return (
@@ -48,6 +49,7 @@ export const ErrorBoundary = () => {
};
export const NotFound = () => {
const t = useT();
const navigate = useNavigate();
return (
@@ -1,63 +1,66 @@
import { t } from '@vegaprotocol/i18n';
import { useT } from '../../lib/use-t';
import { Table } from './table';
export const HowItWorksTable = () => (
<Table
className="bg-none bg-vega-clight-800 dark:bg-vega-cdark-800"
noHeader
noCollapse
columns={[{ name: 'number', className: 'pr-0' }, { name: 'step' }]}
data={[
{
number: (
<span className="text-2xl calt text-vega-clight-100 dark:text-vega-cdark-100">
1
</span>
),
step: t(
'Referrers generate a code assigned to their key via an on chain transaction'
),
},
{
number: (
<span className="text-2xl calt text-vega-clight-100 dark:text-vega-cdark-100">
2
</span>
),
step: t(
'Anyone with the referral link can apply it to their key(s) of choice via an on chain transaction'
),
},
{
number: (
<span className="text-2xl calt text-vega-clight-100 dark:text-vega-cdark-100">
3
</span>
),
step: t(
'Discounts are applied automatically during trading based on the key(s) used'
),
},
{
number: (
<span className="text-2xl calt text-vega-clight-100 dark:text-vega-cdark-100">
4
</span>
),
step: t(
'Referrers earn commission based on a percentage of the taker fees their referees pay'
),
},
{
number: (
<span className="text-2xl calt text-vega-clight-100 dark:text-vega-cdark-100">
5
</span>
),
step: t(
'The commission is taken from the infrastructure fee, maker fee, and liquidity provider fee, not from the referee'
),
},
]}
></Table>
);
export const HowItWorksTable = () => {
const t = useT();
return (
<Table
className="bg-none bg-vega-clight-800 dark:bg-vega-cdark-800"
noHeader
noCollapse
columns={[{ name: 'number', className: 'pr-0' }, { name: 'step' }]}
data={[
{
number: (
<span className="text-2xl calt text-vega-clight-100 dark:text-vega-cdark-100">
1
</span>
),
step: t(
'Referrers generate a code assigned to their key via an on chain transaction'
),
},
{
number: (
<span className="text-2xl calt text-vega-clight-100 dark:text-vega-cdark-100">
2
</span>
),
step: t(
'Anyone with the referral link can apply it to their key(s) of choice via an on chain transaction'
),
},
{
number: (
<span className="text-2xl calt text-vega-clight-100 dark:text-vega-cdark-100">
3
</span>
),
step: t(
'Discounts are applied automatically during trading based on the key(s) used'
),
},
{
number: (
<span className="text-2xl calt text-vega-clight-100 dark:text-vega-cdark-100">
4
</span>
),
step: t(
'Referrers earn commission based on a percentage of the taker fees their referees pay'
),
},
{
number: (
<span className="text-2xl calt text-vega-clight-100 dark:text-vega-cdark-100">
5
</span>
),
step: t(
'The commission is taken from the infrastructure fee, maker fee, and liquidity provider fee, not from the referee'
),
},
]}
></Table>
);
};

Some files were not shown because too many files have changed in this diff Show More