Compare commits

..
Author SHA1 Message Date
asiaznik 88992fa6e6 chore: rebased 2023-02-06 20:51:10 +01:00
asiaznik abe650cf95 chore: order rejection case after rebase 2023-02-06 20:48:05 +01:00
asiaznik 2298c434c8 chore: addressed PR comments 2023-02-06 20:48:05 +01:00
asiaznik 41126111a5 fix: dismiss all btn 2023-02-06 20:48:05 +01:00
asiaznik f4b11e83df fix: lint 2023-02-06 20:48:05 +01:00
asiaznik 0e77062cc6 fix: auto fix 2023-02-06 20:48:05 +01:00
asiaznik 1391db5eed chore: new designs 2023-02-06 20:48:05 +01:00
asiaznik 717b470a9e chore: auto close after 5s for final stages of tx 2023-02-06 20:48:05 +01:00
asiaznik 4df59b9c64 fix: lint housekeeping 2023-02-06 20:48:05 +01:00
asiaznik 23bc8335cc chore: refactored toasts 2023-02-06 20:47:01 +01:00
35 changed files with 293 additions and 510 deletions
+2 -2
View File
@@ -6,7 +6,7 @@ NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_VEGA_NETWORKS={\"MAINNET"\:\"https://explorer.vega.xyz"\,\"TESTNET\":\"https://explorer.fairground.wtf\"}
NX_TENDERMINT_URL=https://tm.sandbox.vega.xyz
NX_TENDERMINT_WEBSOCKET_URL=wss://tm.sandbox.vega.xyz/websocket
NX_TENDERMINT_URL=https://tm.n01.sandbox.vega.xyz
NX_TENDERMINT_WEBSOCKET_URL=wss://tm.n01.sandbox.vega.xyz/websocket
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_VEGA_GOVERNANCE_URL=https://sandbox.token.vega.xyz
@@ -66,9 +66,7 @@ export const Search = () => {
className="text-white"
hasError={Boolean(error?.message)}
type="text"
placeholder={t(
'Enter block number, public key or transaction hash'
)}
placeholder={t('Enter block number, party id or transaction hash')}
/>
{error?.message && (
<div className="bg-white border border-t-0 border-accent absolute top-[100%] flex-1 w-full pb-2 px-2 rounded-b text-black">
@@ -20,7 +20,6 @@ import { TxDetailsLiquidityCancellation } from './tx-liquidity-cancel';
import { TxDetailsDataSubmission } from './tx-data-submission';
import { TxProposalVote } from './tx-proposal-vote';
import { TxDetailsProtocolUpgrade } from './tx-details-protocol-upgrade';
import { TxDetailsIssueSignatures } from './tx-issue-signatures';
interface TxDetailsWrapperProps {
txData: BlockExplorerTransactionResult | undefined;
@@ -68,8 +67,6 @@ function getTransactionComponent(txData?: BlockExplorerTransactionResult) {
// These come from https://github.com/vegaprotocol/vega/blob/develop/core/txn/command.go#L72-L98
switch (txData.type) {
case 'Issue Signatures':
return TxDetailsIssueSignatures;
case 'Submit Order':
return TxDetailsOrder;
case 'Submit Oracle Data':
@@ -11,7 +11,8 @@ interface TxDetailsGenericProps {
}
/**
* A node is
* If there is not yet a custom component for a transaction, just display
* the basic details. This allows someone to view the decoded transaction.
*/
export const TxDetailsGeneric = ({
txData,
@@ -1,77 +0,0 @@
import { t } from '@vegaprotocol/react-helpers';
import type { BlockExplorerTransactionResult } from '../../../routes/types/block-explorer-response';
import type { TendermintBlocksResponse } from '../../../routes/blocks/tendermint-blocks-response';
import { TxDetailsShared } from './shared/tx-details-shared';
import { TableRow, TableCell, TableWithTbody } from '../../table';
import type { components } from '../../../../types/explorer';
import {
EthExplorerLink,
EthExplorerLinkTypes,
} from '../../links/eth-explorer-link/eth-explorer-link';
import { NodeLink } from '../../links';
type Command = components['schemas']['v1IssueSignatures'];
const kind: Record<components['schemas']['v1NodeSignatureKind'], string> = {
NODE_SIGNATURE_KIND_UNSPECIFIED: 'Unspecified',
NODE_SIGNATURE_KIND_ASSET_NEW: 'New asset',
NODE_SIGNATURE_KIND_ASSET_WITHDRAWAL: 'Asset withdrawal',
NODE_SIGNATURE_KIND_ASSET_UPDATE: ' Asset update',
NODE_SIGNATURE_KIND_ERC20_MULTISIG_SIGNER_ADDED: 'Multisig signer added',
NODE_SIGNATURE_KIND_ERC20_MULTISIG_SIGNER_REMOVED: 'Multisig signer removed',
};
interface TxDetailsGenericProps {
txData: BlockExplorerTransactionResult | undefined;
pubKey: string | undefined;
blockData: TendermintBlocksResponse | undefined;
}
/**
* If there is not yet a custom component for a transaction, just display
* the basic details. This allows someone to view the decoded transaction.
*/
export const TxDetailsIssueSignatures = ({
txData,
pubKey,
blockData,
}: TxDetailsGenericProps) => {
if (!txData) {
return <>{t('Awaiting Block Explorer transaction details')}</>;
}
const cmd: Command = txData.command;
const k = cmd.kind ? kind[cmd.kind] : null;
return (
<TableWithTbody className="mb-8">
<TxDetailsShared txData={txData} pubKey={pubKey} blockData={blockData} />
{k ? (
<TableRow modifier="bordered">
<TableCell>{t('Kind')}</TableCell>
<TableCell>{k}</TableCell>
</TableRow>
) : null}
{cmd.submitter ? (
<TableRow modifier="bordered">
<TableCell>{t('ETH key')}</TableCell>
<TableCell>
<EthExplorerLink
id={cmd.submitter}
type={EthExplorerLinkTypes.address}
/>
</TableCell>
</TableRow>
) : null}
{cmd.validatorNodeId ? (
<TableRow modifier="bordered">
<TableCell>{t('Validator')}</TableCell>
<TableCell>
<NodeLink id={cmd.validatorNodeId} />
</TableCell>
</TableRow>
) : null}
</TableWithTbody>
);
};
@@ -10,7 +10,7 @@ import { useDocumentTitle } from '../../../hooks/use-document-title';
export const JumpToParty = () => {
const navigate = useNavigate();
useDocumentTitle(['Public keys']);
useDocumentTitle(['Parties']);
const handleSubmit = (e: React.SyntheticEvent) => {
e.preventDefault();
@@ -27,8 +27,8 @@ export const JumpToParty = () => {
};
return (
<JumpTo
label={t('Go to public key')}
placeholder={t('Public key')}
label={t('Go to party')}
placeholder={t('Party id')}
inputId="party-input"
inputType="text"
inputName="partyId"
@@ -40,7 +40,7 @@ export const JumpToParty = () => {
const Parties = () => {
return (
<section>
<RouteTitle data-testid="parties-header">{t('Public keys')}</RouteTitle>
<RouteTitle data-testid="parties-header">{t('Parties')}</RouteTitle>
<JumpToParty />
</section>
);
@@ -14,7 +14,7 @@ type PartyIdErrorProps = {
const PartyIdError = ({ id, error }: PartyIdErrorProps) => {
const end = isValidPartyId(id)
? t('No accounts or transactions found for: ')
: 'Invalid public key: ';
: 'Invalid party id: ';
return (
<section>
<p>
@@ -16,7 +16,7 @@ import { PartyAccounts } from './components/party-accounts';
const Party = () => {
const { party } = useParams<{ party: string }>();
useDocumentTitle(['Public keys', party || '-']);
useDocumentTitle(['Parties', party || '-']);
const partyId = toNonHex(party ? party : '');
const { isMobile } = useScreenDimensions();
const visibleChars = useMemo(() => (isMobile ? 10 : 14), [isMobile]);
@@ -44,7 +44,7 @@ const Party = () => {
/>
) : (
<Panel>
<p>No data found for public key {party}</p>
<p>No party found for key {party}</p>
</Panel>
);
@@ -71,7 +71,7 @@ const Party = () => {
className="font-alpha uppercase font-xl mb-4 text-zinc-800 dark:text-zinc-200"
data-testid="parties-header"
>
{t('Public key')}
{t('Party')}
</h1>
{partyRes.data ? (
<>
+36 -113
View File
@@ -104,7 +104,7 @@
"tranche_end": "2023-12-05T00:00:00.000Z",
"total_added": "86666.297",
"total_removed": "0",
"locked_amount": "71290.812100647638944706",
"locked_amount": "71527.9874377224479678513",
"deposits": [
{
"amount": "86666.297",
@@ -170,7 +170,7 @@
"tranche_end": "2023-06-01T00:00:00.000Z",
"total_added": "2500",
"total_removed": "0",
"locked_amount": "1555.5670024420025",
"locked_amount": "1569.28784467846975",
"deposits": [
{
"amount": "2500",
@@ -558,7 +558,7 @@
"tranche_end": "2023-08-01T00:00:00.000Z",
"total_added": "37500",
"total_removed": "183.137181525",
"locked_amount": "36100.54097605893375",
"locked_amount": "36307.49069597912625",
"deposits": [
{
"amount": "7500",
@@ -624,7 +624,7 @@
"tranche_end": "2023-12-05T00:00:00.000Z",
"total_added": "129999.45",
"total_removed": "0",
"locked_amount": "71225.768204759715901935",
"locked_amount": "71462.72714918200044174",
"deposits": [
{
"amount": "129999.45",
@@ -690,7 +690,7 @@
"tranche_end": "2023-09-03T00:00:00.000Z",
"total_added": "62600",
"total_removed": "0",
"locked_amount": "35543.98462709284528",
"locked_amount": "35715.29892820902776",
"deposits": [
{
"amount": "10000",
@@ -883,7 +883,7 @@
"tranche_end": "2023-09-17T00:00:00.000Z",
"total_added": "5000",
"total_removed": "0",
"locked_amount": "3030.7572298325725",
"locked_amount": "3044.440480720446",
"deposits": [
{
"amount": "5000",
@@ -1094,7 +1094,7 @@
"tranche_end": "2023-04-05T00:00:00.000Z",
"total_added": "97499.58",
"total_removed": "0",
"locked_amount": "13088.045251352428418448",
"locked_amount": "13320.4799021065353983616",
"deposits": [
{
"amount": "97499.58",
@@ -1127,7 +1127,7 @@
"tranche_end": "2023-04-05T00:00:00.000Z",
"total_added": "135173.4239508",
"total_removed": "98230.390980249184455396",
"locked_amount": "17889.0983018495693667123126",
"locked_amount": "18206.796341262034809441102696",
"deposits": [
{
"amount": "135173.4239508",
@@ -1173,7 +1173,7 @@
"tranche_end": "2023-04-05T00:00:00.000Z",
"total_added": "32499.86",
"total_removed": "0",
"locked_amount": "5505.914618791834455152",
"locked_amount": "5603.6958624323669808404",
"deposits": [
{
"amount": "32499.86",
@@ -1206,7 +1206,7 @@
"tranche_end": "2023-04-05T00:00:00.000Z",
"total_added": "10833.29",
"total_removed": "0",
"locked_amount": "1792.1217802859475346957",
"locked_amount": "1823.94862624421256173",
"deposits": [
{
"amount": "10833.29",
@@ -1239,7 +1239,7 @@
"tranche_end": "2023-04-05T00:00:00.000Z",
"total_added": "22749.93",
"total_removed": "0",
"locked_amount": "6699.35147787958153563",
"locked_amount": "6818.3273364692396522472",
"deposits": [
{
"amount": "6500",
@@ -1378,7 +1378,7 @@
"tranche_end": "2023-05-01T00:00:00.000Z",
"total_added": "22500",
"total_removed": "3995.28612255",
"locked_amount": "10223.86049723756925",
"locked_amount": "10348.030329189685875",
"deposits": [
{
"amount": "7500",
@@ -1576,7 +1576,7 @@
"tranche_end": "2023-06-02T00:00:00.000Z",
"total_added": "1939928.38",
"total_removed": "928642.9598472029154",
"locked_amount": "607199.059293409416755688",
"locked_amount": "612507.96463901900484135",
"deposits": [
{
"amount": "1852091.69",
@@ -2876,7 +2876,7 @@
"tranche_start": "2021-09-05T00:00:00.000Z",
"tranche_end": "2022-09-30T00:00:00.000Z",
"total_added": "60916.66666633337",
"total_removed": "39088.634764730198755268",
"total_removed": "34173.43587066019652061",
"locked_amount": "0",
"deposits": [
{
@@ -2976,16 +2976,6 @@
}
],
"withdrawals": [
{
"amount": "3395.631843574",
"user": "0xa0fF757077B5D796259582b2b9Db99c906277007",
"tx": "0x335be76fc5ff37d4272a624f47822e6163d395e0d8a3a9cfcbcc5f7207239361"
},
{
"amount": "1519.567050496002234658",
"user": "0x6E14D4e15E245d6ECfe3181B08c2FF30d2c1Da19",
"tx": "0x3d0b5ec97be95410571cbc03dd4edd1406243cafbafc40d7c4740f88287efcd3"
},
{
"amount": "1366.363766241335342692",
"user": "0xef633C319801eB899354030DFF2BBD0024C0b39c",
@@ -3616,12 +3606,6 @@
}
],
"withdrawals": [
{
"amount": "1519.567050496002234658",
"user": "0x6E14D4e15E245d6ECfe3181B08c2FF30d2c1Da19",
"tranche_id": 13,
"tx": "0x3d0b5ec97be95410571cbc03dd4edd1406243cafbafc40d7c4740f88287efcd3"
},
{
"amount": "747.099616170667765342",
"user": "0x6E14D4e15E245d6ECfe3181B08c2FF30d2c1Da19",
@@ -3630,8 +3614,8 @@
}
],
"total_tokens": "2266.66666666667",
"withdrawn_tokens": "2266.66666666667",
"remaining_tokens": "0"
"withdrawn_tokens": "747.099616170667765342",
"remaining_tokens": "1519.567050496002234658"
},
{
"address": "0xb14025f7eB7717cFF43e7a33b66d86eaBd6bC7d7",
@@ -3674,12 +3658,6 @@
}
],
"withdrawals": [
{
"amount": "3395.631843574",
"user": "0xa0fF757077B5D796259582b2b9Db99c906277007",
"tranche_id": 13,
"tx": "0x335be76fc5ff37d4272a624f47822e6163d395e0d8a3a9cfcbcc5f7207239361"
},
{
"amount": "4.368156426",
"user": "0xa0fF757077B5D796259582b2b9Db99c906277007",
@@ -3688,8 +3666,8 @@
}
],
"total_tokens": "3400",
"withdrawn_tokens": "3400",
"remaining_tokens": "0"
"withdrawn_tokens": "4.368156426",
"remaining_tokens": "3395.631843574"
},
{
"address": "0x2D69BAB9738b05048be16DE3E5E0A945b8EeEf3a",
@@ -33559,7 +33537,7 @@
"tranche_end": "2023-06-05T00:00:00.000Z",
"total_added": "3732368.4671",
"total_removed": "589730.8667090699299",
"locked_amount": "957554.874604284746674723657",
"locked_amount": "965712.81375596155222172624",
"deposits": [
{
"amount": "1998.95815",
@@ -34873,8 +34851,8 @@
"tranche_start": "2022-06-05T00:00:00.000Z",
"tranche_end": "2023-12-05T00:00:00.000Z",
"total_added": "15870102.715470999700000001",
"total_removed": "560317.67585805941693952",
"locked_amount": "8695115.6900884208257391762291393526601783",
"total_removed": "557133.30560592463281452",
"locked_amount": "8724043.2185305122313058695420656514828332",
"deposits": [
{
"amount": "16249.93",
@@ -35438,16 +35416,6 @@
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tx": "0xa344428c2b1bf9b4685959441983da46bcd434ac0ee5ada325876c8733eba603"
},
{
"amount": "2667.021494",
"user": "0xF4c75FdbAe821C6B5DBB9f001bB23cBA58D1bA37",
"tx": "0x94fe358ee7c973dd1d28ca4c56250dfaccbcb718673872c8e9cf8651a22a1108"
},
{
"amount": "517.348758134784125",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tx": "0xb6a33b8b8855789fbc2d0f9545ba5c2258e52f143d90bc75bbe2411bd2f9b4a5"
},
{
"amount": "858.360074993579125",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
@@ -37067,12 +37035,6 @@
"tranche_id": 2,
"tx": "0xa344428c2b1bf9b4685959441983da46bcd434ac0ee5ada325876c8733eba603"
},
{
"amount": "517.348758134784125",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tranche_id": 2,
"tx": "0xb6a33b8b8855789fbc2d0f9545ba5c2258e52f143d90bc75bbe2411bd2f9b4a5"
},
{
"amount": "858.360074993579125",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
@@ -38161,8 +38123,8 @@
}
],
"total_tokens": "259998.8875",
"withdrawn_tokens": "117484.092376092228875",
"remaining_tokens": "142514.795123907771125"
"withdrawn_tokens": "116966.74361795744475",
"remaining_tokens": "143032.14388204255525"
},
{
"address": "0x89051CAb67Bc7F8CC44F7e270c6EDaf1EC57676c",
@@ -38816,12 +38778,6 @@
"tranche_id": 2,
"tx": "0x606ddaa3882cccb0062bc2827cfcfceb64cef8a6c4df41d06747ae651e5dd52e"
},
{
"amount": "2667.021494",
"user": "0xF4c75FdbAe821C6B5DBB9f001bB23cBA58D1bA37",
"tranche_id": 2,
"tx": "0x94fe358ee7c973dd1d28ca4c56250dfaccbcb718673872c8e9cf8651a22a1108"
},
{
"amount": "1099.300488",
"user": "0xF4c75FdbAe821C6B5DBB9f001bB23cBA58D1bA37",
@@ -39004,8 +38960,8 @@
}
],
"total_tokens": "200000",
"withdrawn_tokens": "90095.748006",
"remaining_tokens": "109904.251994"
"withdrawn_tokens": "87428.726512",
"remaining_tokens": "112571.273488"
},
{
"address": "0x1b956E6c00E238194B331eddEFF72Cb5f28A8d01",
@@ -40351,8 +40307,8 @@
"tranche_start": "2021-11-05T00:00:00.000Z",
"tranche_end": "2023-05-05T00:00:00.000Z",
"total_added": "14597706.0446472999",
"total_removed": "3921707.576409189114087282",
"locked_amount": "2305830.059961451513978716150659574",
"total_removed": "3920990.096830703963164282",
"locked_amount": "2332535.769515174096512550505057687",
"deposits": [
{
"amount": "129284.449",
@@ -40611,11 +40567,6 @@
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
"tx": "0xc73c47dcfac4093bb6325fe92986dd010d5a773af4bb97e188ad57359a309a52"
},
{
"amount": "717.479578485150923",
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
"tx": "0x0c292f2355b5cdc3b389ed35e6e138e593b1db9d91fcd58f132835343724bf90"
},
{
"amount": "8950.14985089483210984",
"user": "0x66827bCD635f2bB1779d68c46aEB16541bCA6ba8",
@@ -43453,12 +43404,6 @@
"tranche_id": 3,
"tx": "0xc73c47dcfac4093bb6325fe92986dd010d5a773af4bb97e188ad57359a309a52"
},
{
"amount": "717.479578485150923",
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
"tranche_id": 3,
"tx": "0x0c292f2355b5cdc3b389ed35e6e138e593b1db9d91fcd58f132835343724bf90"
},
{
"amount": "1192.05386354121365675",
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
@@ -45849,8 +45794,8 @@
}
],
"total_tokens": "359123.469575",
"withdrawn_tokens": "302309.38363864041348675",
"remaining_tokens": "56814.08593635958651325"
"withdrawn_tokens": "301591.90406015526256375",
"remaining_tokens": "57531.56551484473743625"
},
{
"address": "0xBdd412797c1B78535Afc5F71503b91fAbD0160fB",
@@ -47188,7 +47133,7 @@
"tranche_end": "2023-04-05T00:00:00.000Z",
"total_added": "5778205.3912159303",
"total_removed": "2730068.739915456784546642",
"locked_amount": "594143.998694685843745742950916108",
"locked_amount": "604695.586054148570133080743399363",
"deposits": [
{
"amount": "552496.6455",
@@ -49170,8 +49115,8 @@
"tranche_start": "2022-06-05T00:00:00.000Z",
"tranche_end": "2023-06-05T00:00:00.000Z",
"total_added": "472355.6199999996",
"total_removed": "32476.7515062512685",
"locked_amount": "151730.043498066832591983753120252",
"total_removed": "32361.4666889012685",
"locked_amount": "153022.71558941830041836828209032",
"deposits": [
{
"amount": "3000",
@@ -55830,16 +55775,6 @@
"user": "0x0e199b123f71f964d6567869B6B849C1f255A855",
"tx": "0x6df8666e49f7d491c740d6a36df031aac232a6cf37fa197aa1f6b2bb481be99c"
},
{
"amount": "99.47735921",
"user": "0x2E32F49389CF3039ab6365ca59329002922Cc01D",
"tx": "0x7de3c1703d23970f04ce1bebe2cece8a2fe3fbe6b876502da1812c312151b954"
},
{
"amount": "15.80745814",
"user": "0x4eD2b3c68BB4fda084ce1591a210F4aC8b71234A",
"tx": "0xc0f953d7ecc45bfc1d7b69ecd491a63e8b00b0d732ff006bee008e917a04179f"
},
{
"amount": "78.261187214",
"user": "0x479F059c46c6873C6B970A92911084b3eA036d56",
@@ -68095,12 +68030,6 @@
}
],
"withdrawals": [
{
"amount": "99.47735921",
"user": "0x2E32F49389CF3039ab6365ca59329002922Cc01D",
"tranche_id": 5,
"tx": "0x7de3c1703d23970f04ce1bebe2cece8a2fe3fbe6b876502da1812c312151b954"
},
{
"amount": "35.842421358",
"user": "0x2E32F49389CF3039ab6365ca59329002922Cc01D",
@@ -68109,8 +68038,8 @@
}
],
"total_tokens": "200",
"withdrawn_tokens": "135.319780568",
"remaining_tokens": "64.680219432"
"withdrawn_tokens": "35.842421358",
"remaining_tokens": "164.157578642"
},
{
"address": "0x2F2588aCd44253312b4A94bF6753bE67514A5Cc6",
@@ -69185,12 +69114,6 @@
}
],
"withdrawals": [
{
"amount": "15.80745814",
"user": "0x4eD2b3c68BB4fda084ce1591a210F4aC8b71234A",
"tranche_id": 5,
"tx": "0xc0f953d7ecc45bfc1d7b69ecd491a63e8b00b0d732ff006bee008e917a04179f"
},
{
"amount": "32.761796044",
"user": "0x4eD2b3c68BB4fda084ce1591a210F4aC8b71234A",
@@ -69265,8 +69188,8 @@
}
],
"total_tokens": "400",
"withdrawn_tokens": "270.64275748",
"remaining_tokens": "129.35724252"
"withdrawn_tokens": "254.83529934",
"remaining_tokens": "145.16470066"
},
{
"address": "0x5c90765F50629570738fEe7b7FA82ae118f81Ed1",
@@ -1,6 +1,6 @@
import {
addDecimal,
fromISONano,
fromNanoSeconds,
t,
useThemeSwitcher,
} from '@vegaprotocol/react-helpers';
@@ -228,7 +228,7 @@ export const AccountHistoryChart = ({
.reduce((acc, edge) => {
if (edge.node.accountType === accountType) {
acc?.push({
datetime: fromISONano(edge.node.timestamp),
datetime: fromNanoSeconds(edge.node.timestamp),
balance: Number(addDecimal(edge.node.balance, asset.decimals)),
});
}
+4 -13
View File
@@ -1,6 +1,6 @@
import { fireEvent, render, screen } from '@testing-library/react';
import { Footer, NodeHealth } from './footer';
import { useEnvironment, useNodeHealth } from '@vegaprotocol/environment';
import { useEnvironment } from '@vegaprotocol/environment';
jest.mock('@vegaprotocol/environment');
@@ -12,14 +12,10 @@ describe('Footer', () => {
// @ts-ignore mock env hook
useEnvironment.mockImplementation(() => ({
VEGA_URL: `https://api.${node}/graphql`,
blockDifference: 0,
setNodeSwitcherOpen: mockOpenNodeSwitcher,
}));
// @ts-ignore mock env hook
useNodeHealth.mockImplementation(() => ({
blockDiff: 0,
}));
render(<Footer />);
fireEvent.click(screen.getByText(node));
@@ -33,14 +29,10 @@ describe('Footer', () => {
// @ts-ignore mock env hook
useEnvironment.mockImplementation(() => ({
VEGA_URL: `https://api.${node}/graphql`,
blockDifference: 0,
setNodeSwitcherOpen: mockOpenNodeSwitcher,
}));
// @ts-ignore mock env hook
useNodeHealth.mockImplementation(() => ({
blockDiff: 0,
}));
render(<Footer />);
fireEvent.click(screen.getByText('Operational'));
@@ -51,9 +43,8 @@ describe('Footer', () => {
describe('NodeHealth', () => {
const cases = [
{ diff: 0, classname: 'bg-success', text: 'Operational' },
{ diff: -1, classname: 'bg-success', text: 'Operational' },
{ diff: 5, classname: 'bg-warning', text: '5 Blocks behind' },
{ diff: null, classname: 'bg-danger', text: 'Non operational' },
{ diff: -1, classname: 'bg-danger', text: 'Non operational' },
];
it.each(cases)(
'renders correct text and indicator color for $diff block difference',
+8 -8
View File
@@ -1,10 +1,9 @@
import { useEnvironment, useNodeHealth } from '@vegaprotocol/environment';
import { useEnvironment } from '@vegaprotocol/environment';
import { t, useNavigatorOnline } from '@vegaprotocol/react-helpers';
import { ButtonLink, Indicator, Intent } from '@vegaprotocol/ui-toolkit';
export const Footer = () => {
const { VEGA_URL, setNodeSwitcherOpen } = useEnvironment();
const { blockDiff } = useNodeHealth();
const { VEGA_URL, blockDifference, setNodeSwitcherOpen } = useEnvironment();
return (
<footer className="px-4 py-1 text-xs border-t border-default">
<div className="flex justify-between">
@@ -12,9 +11,10 @@ export const Footer = () => {
{VEGA_URL && (
<>
<NodeHealth
blockDiff={blockDiff}
blockDiff={blockDifference}
openNodeSwitcher={setNodeSwitcherOpen}
/>
{' | '}
<NodeUrl url={VEGA_URL} openNodeSwitcher={setNodeSwitcherOpen} />
</>
)}
@@ -37,8 +37,8 @@ const NodeUrl = ({ url, openNodeSwitcher }: NodeUrlProps) => {
};
interface NodeHealthProps {
blockDiff: number | null;
openNodeSwitcher: () => void;
blockDiff: number;
}
// How many blocks behind the most advanced block that is
@@ -57,7 +57,7 @@ export const NodeHealth = ({
if (!online) {
text = t('Offline');
intent = Intent.Danger;
} else if (blockDiff === null) {
} else if (blockDiff < 0) {
// Block height query failed and null was returned
text = t('Non operational');
intent = Intent.Danger;
@@ -67,9 +67,9 @@ export const NodeHealth = ({
}
return (
<>
<span>
<Indicator variant={intent} />
<ButtonLink onClick={openNodeSwitcher}>{text}</ButtonLink>
</>
</span>
);
};
-1
View File
@@ -1,2 +1 @@
export * from './lib/apollo-client';
export * from './lib/header-store';
+2 -36
View File
@@ -13,11 +13,7 @@ import { createClient as createWSClient } from 'graphql-ws';
import { onError } from '@apollo/client/link/error';
import { RetryLink } from '@apollo/client/link/retry';
import ApolloLinkTimeout from 'apollo-link-timeout';
import {
fromNanoSeconds,
localLoggerFactory,
} from '@vegaprotocol/react-helpers';
import { useHeaderStore } from './header-store';
import { localLoggerFactory } from '@vegaprotocol/react-helpers';
const isBrowser = typeof window !== 'undefined';
@@ -28,7 +24,6 @@ export type ClientOptions = {
cacheConfig?: InMemoryCacheConfig;
retry?: boolean;
connectToDevTools?: boolean;
connectToHeaderStore?: boolean;
};
export function createClient({
@@ -36,7 +31,6 @@ export function createClient({
cacheConfig,
retry = true,
connectToDevTools = true,
connectToHeaderStore = true,
}: ClientOptions) {
if (!url) {
throw new Error('url must be passed into createClient!');
@@ -50,28 +44,6 @@ export function createClient({
return forward(operation);
});
const headerLink = connectToHeaderStore
? new ApolloLink((operation, forward) => {
return forward(operation).map((response) => {
const context = operation.getContext();
const headers = context['response'].headers;
const blockHeight = headers.get('x-block-height');
const timestamp = headers.get('x-block-timestamp');
if (blockHeight && timestamp) {
const state = useHeaderStore.getState();
useHeaderStore.setState({
...state,
[context['response'].url]: {
blockHeight: Number(blockHeight),
timestamp: fromNanoSeconds(timestamp),
},
});
}
return response;
});
})
: noOpLink;
const timeoutLink = new ApolloLinkTimeout(10000);
const enlargedTimeoutLink = new ApolloLinkTimeout(100000);
@@ -132,13 +104,7 @@ export function createClient({
);
return new ApolloClient({
link: from([
errorLink,
composedTimeoutLink,
headerLink,
retryLink,
splitLink,
]),
link: from([errorLink, composedTimeoutLink, retryLink, splitLink]),
cache: new InMemoryCache(cacheConfig),
connectToDevTools,
});
@@ -1,10 +0,0 @@
import { create } from 'zustand';
type HeaderStore = {
[url: string]: {
blockHeight: number;
timestamp: Date;
};
};
export const useHeaderStore = create<HeaderStore>(() => ({}));
@@ -10,9 +10,9 @@ interface Props {
export const DealTicketButton = ({ disabled, variant }: Props) => {
const { pubKey } = useVegaWallet();
const openVegaWalletDialog = useVegaWalletDialogStore(
(store) => store.openVegaWalletDialog
);
const { openVegaWalletDialog } = useVegaWalletDialogStore((store) => ({
openVegaWalletDialog: store.openVegaWalletDialog,
}));
return pubKey ? (
<div className="mb-4">
<Button
@@ -31,6 +31,8 @@ import {
usePersistedOrderStoreSubscription,
} from '@vegaprotocol/orders';
export type TransactionStatus = 'default' | 'pending';
export interface DealTicketProps {
market: MarketDealTicket;
submit: (order: OrderSubmissionBody['orderSubmission']) => void;
@@ -13,10 +13,6 @@ type NodeStatsContentProps = {
setBlock: (value: number) => void;
children?: ReactNode;
dataTestId?: string;
headers?: {
blockHeight: number;
timestamp: Date;
};
};
const getResponseTimeDisplayValue = (
@@ -32,13 +28,13 @@ const getResponseTimeDisplayValue = (
};
const getBlockDisplayValue = (
block: number | undefined,
block: NodeData['block'] | undefined,
setBlock: (block: number) => void
) => {
if (block) {
return <NodeBlockHeight value={block} setValue={setBlock} />;
if (block?.value) {
return <NodeBlockHeight value={block?.value} setValue={setBlock} />;
}
if (!block) {
if (block?.hasError) {
return t('n/a');
}
return '-';
@@ -63,7 +59,6 @@ const NodeStatsContent = ({
setBlock,
children,
dataTestId,
headers,
}: NodeStatsContentProps) => {
return (
<LayoutRow dataTestId={dataTestId}>
@@ -77,12 +72,15 @@ const NodeStatsContent = ({
{getResponseTimeDisplayValue(data.responseTime)}
</LayoutCell>
<LayoutCell
label={t('Header block')}
isLoading={false}
hasError={headers ? highestBlock - 3 > headers.blockHeight : false}
dataTestId="header-block-cell"
label={t('Block')}
isLoading={data.block?.isLoading}
hasError={
data.block?.hasError ||
(!!data.block?.value && highestBlock > data.block.value)
}
dataTestId="block-cell"
>
{getBlockDisplayValue(headers?.blockHeight, setBlock)}
{getBlockDisplayValue(data.block, setBlock)}
</LayoutCell>
<LayoutCell
label={t('Subscription')}
@@ -115,10 +113,6 @@ export type NodeStatsProps = {
highestBlock: number;
setBlock: (value: number) => void;
children?: ReactNode;
headers: {
blockHeight: number;
timestamp: Date;
};
};
export const NodeStats = ({
@@ -127,7 +121,6 @@ export const NodeStats = ({
highestBlock,
children,
setBlock,
headers,
}: NodeStatsProps) => {
return (
<Wrapper client={client}>
@@ -136,7 +129,6 @@ export const NodeStats = ({
highestBlock={highestBlock}
setBlock={setBlock}
dataTestId="node-row"
headers={headers}
>
{children}
</NodeStatsContent>
@@ -21,7 +21,6 @@ import type { Configuration, NodeData, ErrorType } from '../../types';
import { LayoutRow } from './layout-row';
import { NodeError } from './node-error';
import { NodeStats } from './node-stats';
import { useHeaderStore } from '@vegaprotocol/apollo-client';
type NodeSwitcherProps = {
error?: string;
@@ -47,7 +46,6 @@ export const NodeSwitcher = ({
onConnect,
}: NodeSwitcherProps) => {
const { VEGA_ENV, VEGA_URL } = useEnvironment();
const headerStore = useHeaderStore();
const [networkError, setNetworkError] = useState(
getErrorByType(initialErrorType, VEGA_ENV, VEGA_URL)
);
@@ -98,7 +96,7 @@ export const NodeSwitcher = ({
<LayoutRow>
<div />
<span className="text-right">{t('Response time')}</span>
<span className="text-right">{t('Block height')}</span>
<span className="text-right">{t('Block')}</span>
<span className="text-right">{t('Subscription')}</span>
</LayoutRow>
</div>
@@ -117,7 +115,6 @@ export const NodeSwitcher = ({
client={clients[node]}
highestBlock={highestBlock}
setBlock={(block) => updateNodeBlock(node, block)}
headers={headerStore[node]}
>
<div className="break-all" data-testid="node">
<Radio
@@ -134,7 +131,6 @@ export const NodeSwitcher = ({
client={customUrl ? clients[customUrl] : undefined}
highestBlock={highestBlock}
setBlock={(block) => updateNodeBlock(CUSTOM_NODE_KEY, block)}
headers={headerStore[CUSTOM_NODE_KEY]}
>
<div className="flex w-full mb-2">
<Radio
@@ -37,7 +37,6 @@ export const getMockStatisticsResult = (
__typename: 'Statistics',
chainId: `${env.toLowerCase()}-0123`,
blockHeight: '11',
vegaTime: new Date().toISOString(),
},
});
@@ -46,7 +45,6 @@ export const getMockQueryResult = (env: Networks): StatisticsQuery => ({
__typename: 'Statistics',
chainId: `${env.toLowerCase()}-0123`,
blockHeight: '11',
vegaTime: new Date().toISOString(),
},
});
@@ -17,10 +17,6 @@ jest.mock('react-dom', () => ({
createPortal: (node: ReactNode) => node,
}));
jest.mock('../components/node-switcher', () => ({
NodeSwitcher: () => <div />,
}));
global.fetch = jest.fn();
const MockWrapper = (props: ComponentProps<typeof EnvironmentProvider>) => {
@@ -50,6 +46,7 @@ const mockEnvironmentState = {
GITHUB_FEEDBACK_URL: 'https://github.com/test/feedback',
MAINTENANCE_PAGE: false,
configLoading: false,
blockDifference: 0,
nodeSwitcherOpen: false,
setNodeSwitcherOpen: noop,
networkError: undefined,
@@ -25,6 +25,7 @@ import type {
NodeData,
Configuration,
} from '../types';
import { useNodeHealth } from './use-node-health';
type EnvironmentProviderProps = {
config?: Configuration;
@@ -35,6 +36,7 @@ type EnvironmentProviderProps = {
export type EnvironmentState = Environment & {
configLoading: boolean;
networkError?: ErrorType;
blockDifference: number;
nodeSwitcherOpen: boolean;
setNodeSwitcherOpen: () => void;
};
@@ -84,6 +86,8 @@ export const EnvironmentProvider = ({
environment.MAINTENANCE_PAGE
);
const blockDifference = useNodeHealth(clients, environment.VEGA_URL);
const nodeKeys = Object.keys(nodes);
useEffect(() => {
@@ -146,6 +150,7 @@ export const EnvironmentProvider = ({
...environment,
configLoading: loading,
networkError,
blockDifference,
nodeSwitcherOpen: isNodeSwitcherOpen,
setNodeSwitcherOpen: () => setNodeSwitcherOpen(true),
}}
@@ -1,118 +1,117 @@
import { renderHook, waitFor } from '@testing-library/react';
import { useNodeHealth } from './use-node-health';
import type { MockedResponse } from '@apollo/react-testing';
import { MockedProvider } from '@apollo/react-testing';
import type { StatisticsQuery } from '../utils/__generated__/Node';
import { StatisticsDocument } from '../utils/__generated__/Node';
import { useEnvironment } from './use-environment';
import { useHeaderStore } from '@vegaprotocol/apollo-client';
import { act, renderHook } from '@testing-library/react';
import {
useNodeHealth,
NODE_SUBSET_COUNT,
INTERVAL_TIME,
} from './use-node-health';
import type { createClient } from '@vegaprotocol/apollo-client';
import type { ClientCollection } from './use-nodes';
const vegaUrl = 'https://foo.bar.com';
function setup(...args: Parameters<typeof useNodeHealth>) {
return renderHook(() => useNodeHealth(...args));
}
jest.mock('./use-environment');
jest.mock('@vegaprotocol/apollo-client');
// @ts-ignore ignore mock implementation
useEnvironment.mockImplementation(() => ({
VEGA_URL: vegaUrl,
}));
const createStatsMock = (
blockHeight: number
): MockedResponse<StatisticsQuery> => {
function createMockClient(blockHeight: number) {
return {
request: {
query: StatisticsDocument,
},
result: {
query: jest.fn().mockResolvedValue({
data: {
statistics: {
chainId: 'chain-id',
blockHeight: blockHeight.toString(),
vegaTime: '12345',
},
},
},
};
};
function setup(
mock: MockedResponse<StatisticsQuery>,
headers:
| {
blockHeight: number;
timestamp: Date;
}
| undefined
) {
// @ts-ignore ignore mock implementation
useHeaderStore.mockImplementation(() => ({
[vegaUrl]: headers,
}));
return renderHook(() => useNodeHealth(), {
wrapper: ({ children }) => (
<MockedProvider mocks={[mock]}>{children}</MockedProvider>
),
});
}),
} as unknown as ReturnType<typeof createClient>;
}
describe('useNodeHealth', () => {
it.each([
{ core: 1, node: 1, expected: 0 },
{ core: 1, node: 5, expected: -4 },
{ core: 10, node: 5, expected: 5 },
])(
'provides difference core block $core and node block $node',
async (cases) => {
const { result } = setup(createStatsMock(cases.core), {
blockHeight: cases.node,
timestamp: new Date(),
});
expect(result.current.blockDiff).toEqual(null);
expect(result.current.coreBlockHeight).toEqual(undefined);
expect(result.current.datanodeBlockHeight).toEqual(cases.node);
await waitFor(() => {
expect(result.current.blockDiff).toEqual(cases.expected);
expect(result.current.coreBlockHeight).toEqual(cases.core);
expect(result.current.datanodeBlockHeight).toEqual(cases.node);
});
}
);
function createRejectingClient() {
return {
query: () => Promise.reject(new Error('request failed')),
} as unknown as ReturnType<typeof createClient>;
}
it('block diff is null if query fails indicating non operational', async () => {
const failedQuery: MockedResponse<StatisticsQuery> = {
request: {
query: StatisticsDocument,
},
result: {
// @ts-ignore failed query with no result
data: {},
},
};
const { result } = setup(failedQuery, {
blockHeight: 1,
timestamp: new Date(),
});
expect(result.current.blockDiff).toEqual(null);
expect(result.current.coreBlockHeight).toEqual(undefined);
expect(result.current.datanodeBlockHeight).toEqual(1);
await waitFor(() => {
expect(result.current.blockDiff).toEqual(null);
expect(result.current.coreBlockHeight).toEqual(undefined);
expect(result.current.datanodeBlockHeight).toEqual(1);
});
function createErroringClient() {
return {
query: () =>
Promise.resolve({
error: new Error('failed'),
}),
} as unknown as ReturnType<typeof createClient>;
}
const CURRENT_URL = 'https://current.test.com';
describe('useNodeHealth', () => {
beforeAll(() => {
jest.useFakeTimers();
});
it('returns 0 if no headers are found (wait until stats query resolves)', async () => {
const { result } = setup(createStatsMock(1), undefined);
expect(result.current.blockDiff).toEqual(null);
expect(result.current.coreBlockHeight).toEqual(undefined);
expect(result.current.datanodeBlockHeight).toEqual(undefined);
await waitFor(() => {
expect(result.current.blockDiff).toEqual(0);
expect(result.current.coreBlockHeight).toEqual(1);
expect(result.current.datanodeBlockHeight).toEqual(undefined);
it('provides difference between the highest block and the current block', async () => {
const highest = 100;
const curr = 97;
const clientCollection: ClientCollection = {
[CURRENT_URL]: createMockClient(curr),
'https://n02.test.com': createMockClient(98),
'https://n03.test.com': createMockClient(highest),
};
const { result } = setup(clientCollection, CURRENT_URL);
await act(async () => {
jest.advanceTimersByTime(INTERVAL_TIME);
});
expect(result.current).toBe(highest - curr);
});
it('returns -1 if the current node query fails', async () => {
const clientCollection: ClientCollection = {
[CURRENT_URL]: createRejectingClient(),
'https://n02.test.com': createMockClient(200),
'https://n03.test.com': createMockClient(102),
};
const { result } = setup(clientCollection, CURRENT_URL);
await act(async () => {
jest.advanceTimersByTime(INTERVAL_TIME);
});
expect(result.current).toBe(-1);
});
it('returns -1 if the current node query returns an error', async () => {
const clientCollection: ClientCollection = {
[CURRENT_URL]: createErroringClient(),
'https://n02.test.com': createMockClient(200),
'https://n03.test.com': createMockClient(102),
};
const { result } = setup(clientCollection, CURRENT_URL);
await act(async () => {
jest.advanceTimersByTime(INTERVAL_TIME);
});
expect(result.current).toBe(-1);
});
it('queries against 5 random nodes along with the current url', async () => {
const clientCollection: ClientCollection = new Array(20)
.fill(null)
.reduce((obj, x, i) => {
obj[`https://n${i}.test.com`] = createMockClient(100);
return obj;
}, {} as ClientCollection);
clientCollection[CURRENT_URL] = createMockClient(100);
const spyOnCurrent = jest.spyOn(clientCollection[CURRENT_URL], 'query');
const { result } = setup(clientCollection, CURRENT_URL);
await act(async () => {
jest.advanceTimersByTime(INTERVAL_TIME);
});
let count = 0;
Object.values(clientCollection).forEach((client) => {
// @ts-ignore jest.fn() in client setup means mock will be present
if (client?.query.mock.calls.length) {
count++;
}
});
expect(count).toBe(NODE_SUBSET_COUNT + 1);
expect(spyOnCurrent).toHaveBeenCalledTimes(1);
expect(result.current).toBe(0);
});
});
+82 -31
View File
@@ -1,37 +1,88 @@
import { useMemo } from 'react';
import { useStatisticsQuery } from '../utils/__generated__/Node';
import { useHeaderStore } from '@vegaprotocol/apollo-client';
import { fromISONano } from '@vegaprotocol/react-helpers';
import { useEnvironment } from './use-environment';
import compact from 'lodash/compact';
import shuffle from 'lodash/shuffle';
import type { createClient } from '@vegaprotocol/apollo-client';
import { useEffect, useState } from 'react';
import type { StatisticsQuery } from '../utils/__generated__/Node';
import { StatisticsDocument } from '../utils/__generated__/Node';
import type { ClientCollection } from './use-nodes';
export const useNodeHealth = () => {
const { VEGA_URL } = useEnvironment();
const headerStore = useHeaderStore();
const headers = VEGA_URL ? headerStore[VEGA_URL] : undefined;
const { data } = useStatisticsQuery({
pollInterval: 1000,
fetchPolicy: 'no-cache',
});
// How often to query other nodes
export const INTERVAL_TIME = 30 * 1000;
// Number of nodes to query against
export const NODE_SUBSET_COUNT = 5;
const blockDiff = useMemo(() => {
if (!data?.statistics.blockHeight) {
return null;
}
// Queries all nodes from the environment provider via an interval
// to calculate and return the difference between the most advanced block
// and the block height of the current node
export const useNodeHealth = (clients: ClientCollection, vegaUrl?: string) => {
const [blockDiff, setBlockDiff] = useState(0);
if (!headers) {
return 0;
}
useEffect(() => {
if (!clients || !vegaUrl) return;
return Number(data.statistics.blockHeight) - headers.blockHeight;
}, [data, headers]);
const fetchBlockHeight = async (
client?: ReturnType<typeof createClient>
) => {
try {
const result = await client?.query<StatisticsQuery>({
query: StatisticsDocument,
fetchPolicy: 'no-cache', // always fetch and never cache
});
return {
coreBlockHeight: data?.statistics.blockHeight
? Number(data?.statistics.blockHeight)
: undefined,
coreVegaTime: fromISONano(data?.statistics.vegaTime),
datanodeBlockHeight: headers?.blockHeight,
datanodeVegaTime: headers?.timestamp,
blockDiff,
};
if (!result) return null;
if (result.error) return null;
return result;
} catch {
return null;
}
};
const getBlockHeights = async () => {
const nodes = Object.keys(clients).filter((key) => key !== vegaUrl);
// make sure that your current vega url is always included
// so we can compare later
const testNodes = [vegaUrl, ...randomSubset(nodes, NODE_SUBSET_COUNT)];
const result = await Promise.all(
testNodes.map((node) => fetchBlockHeight(clients[node]))
);
const blockHeights: { [node: string]: number | null } = {};
testNodes.forEach((node, i) => {
const data = result[i];
const blockHeight = data
? Number(data?.data.statistics.blockHeight)
: null;
blockHeights[node] = blockHeight;
});
return blockHeights;
};
// Every INTERVAL_TIME get block heights of a random subset
// of nodes and determine if your current node is falling behind
const interval = setInterval(async () => {
const blockHeights = await getBlockHeights();
const highestBlock = Math.max.apply(
null,
compact(Object.values(blockHeights))
);
const currNodeBlock = blockHeights[vegaUrl];
if (!currNodeBlock) {
// Block height query failed and null was returned
setBlockDiff(-1);
} else {
setBlockDiff(highestBlock - currNodeBlock);
}
}, INTERVAL_TIME);
return () => {
clearInterval(interval);
};
}, [clients, vegaUrl]);
return blockDiff;
};
const randomSubset = (arr: string[], size: number) => {
const shuffled = shuffle(arr);
return shuffled.slice(0, size);
};
-1
View File
@@ -2,7 +2,6 @@ query Statistics {
statistics {
chainId
blockHeight
vegaTime
}
}
+1 -2
View File
@@ -6,7 +6,7 @@ const defaultOptions = {} as const;
export type StatisticsQueryVariables = Types.Exact<{ [key: string]: never; }>;
export type StatisticsQuery = { __typename?: 'Query', statistics: { __typename?: 'Statistics', chainId: string, blockHeight: string, vegaTime: any } };
export type StatisticsQuery = { __typename?: 'Query', statistics: { __typename?: 'Statistics', chainId: string, blockHeight: string } };
export type BlockTimeSubscriptionVariables = Types.Exact<{ [key: string]: never; }>;
@@ -19,7 +19,6 @@ export const StatisticsDocument = gql`
statistics {
chainId
blockHeight
vegaTime
}
}
`;
-1
View File
@@ -10,7 +10,6 @@ export const statisticsQuery = (
__typename: 'Statistics',
chainId: 'chain-id',
blockHeight: '11',
vegaTime: new Date().toISOString(),
},
};
+2 -2
View File
@@ -1,7 +1,7 @@
import {
addDecimalsFormatNumber,
DateRangeFilter,
fromISONano,
fromNanoSeconds,
getDateTimeFormat,
SetFilter,
t,
@@ -199,7 +199,7 @@ export const LedgerTable = forwardRef<AgGridReact, LedgerEntryProps>(
valueFormatter={({
value,
}: VegaValueFormatterParams<LedgerEntry, 'vegaTime'>) =>
value ? getDateTimeFormat().format(fromISONano(value)) : '-'
value ? getDateTimeFormat().format(fromNanoSeconds(value)) : '-'
}
filter={DateRangeFilter}
/>
+1 -6
View File
@@ -4,12 +4,7 @@ export const toNanoSeconds = (date: Date | string) => {
return new Date(date).getTime().toString() + '000000';
};
export const fromISONano = (ts: string) => {
export const fromNanoSeconds = (ts: string) => {
const val = parseISO(ts);
return new Date(isValid(val) ? val : 0);
};
export const fromNanoSeconds = (ts: string | number) => {
const ms = Number(String(ts).slice(0, -6));
return new Date(ms);
};
@@ -1,6 +1,7 @@
import { Splash } from '../splash';
import type { ReactNode } from 'react';
import { t } from '@vegaprotocol/react-helpers';
import * as Sentry from '@sentry/react';
interface AsyncRendererProps<T> {
loading: boolean;
@@ -26,6 +27,7 @@ export function AsyncRenderer<T = object>({
render,
}: AsyncRendererProps<T>) {
if (error) {
Sentry.captureException(`Error rendering data: ${error.message}`);
if (!data) {
return (
<Splash>
@@ -13,12 +13,10 @@ const mockSendTx = jest.fn<Promise<Partial<TransactionResponse> | null>, []>();
const pubKey = 'pubKey';
const mockDisconnect = jest.fn();
jest.mock('./use-vega-wallet', () => ({
useVegaWallet: () => ({
sendTx: mockSendTx,
pubKey,
disconnect: mockDisconnect,
}),
}));
@@ -106,17 +104,6 @@ describe('useVegaTransactionManager', () => {
mockSendTx.mockRejectedValue(null);
renderHook(useVegaTransactionManager);
await waitForNextTick();
expect(mockDisconnect).not.toHaveBeenCalledWith();
expect(update).toBeCalled();
expect(update.mock.calls[0][1]?.status).toEqual(VegaTxStatus.Error);
});
it('call disconnect if detect no service error', async () => {
mockTransactionStoreState.mockReturnValue(defaultState);
mockSendTx.mockRejectedValue(new TypeError('Failed to fetch'));
renderHook(useVegaTransactionManager);
await waitForNextTick();
expect(mockDisconnect).toHaveBeenCalledWith();
expect(update).toBeCalled();
expect(update.mock.calls[0][1]?.status).toEqual(VegaTxStatus.Error);
});
@@ -1,12 +1,12 @@
import { useVegaWallet } from './use-vega-wallet';
import { useEffect, useRef } from 'react';
import type { WalletError } from './connectors';
import { ClientErrors } from './connectors';
import { VegaTxStatus, orderErrorResolve } from './use-vega-transaction';
import { VegaTxStatus } from './use-vega-transaction';
import { useVegaTransactionStore } from './use-vega-transaction-store';
import { WalletClientError } from '@vegaprotocol/wallet-client';
export const useVegaTransactionManager = () => {
const { sendTx, pubKey, disconnect } = useVegaWallet();
const { sendTx, pubKey } = useVegaWallet();
const processed = useRef<Set<number>>(new Set());
const transaction = useVegaTransactionStore((state) =>
state.transactions.find(
@@ -38,14 +38,10 @@ export const useVegaTransactionManager = () => {
}
})
.catch((err) => {
const error = orderErrorResolve(err);
if ((error as WalletError).code === ClientErrors.NO_SERVICE.code) {
disconnect();
}
update(transaction.id, {
error,
error: err instanceof WalletClientError ? err : ClientErrors.UNKNOWN,
status: VegaTxStatus.Error,
});
});
}, [transaction, pubKey, del, sendTx, update, disconnect]);
}, [transaction, pubKey, del, sendTx, update]);
};
@@ -6,18 +6,17 @@ import {
useTransactionEventSubscription,
} from './__generated__/TransactionResult';
import { useVegaTransactionStore } from './use-vega-transaction-store';
import { waitForWithdrawalApproval } from './wait-for-withdrawal-approval';
export const useVegaTransactionUpdater = () => {
const client = useApolloClient();
const updateWithdrawal = useVegaTransactionStore(
(state) => state.updateWithdrawal
);
const updateOrder = useVegaTransactionStore((state) => state.updateOrder);
const updateTransaction = useVegaTransactionStore(
(state) => state.updateTransactionResult
);
const { updateWithdrawal, updateOrder, updateTransaction } =
useVegaTransactionStore((state) => ({
updateWithdrawal: state.updateWithdrawal,
updateOrder: state.updateOrder,
updateTransaction: state.updateTransactionResult,
}));
const { pubKey } = useVegaWallet();
const variables = { partyId: pubKey || '' };
const skip = !pubKey;
@@ -87,7 +87,7 @@ describe('useVegaTransaction', () => {
);
});
it('handles an unknown error', () => {
it('handles an unkwown error', () => {
const unknownThrow = { foo: 'bar' };
const mockSendTx = jest.fn(() => {
throw unknownThrow;
+3 -24
View File
@@ -1,15 +1,10 @@
import type { ReactNode } from 'react';
import { useCallback, useMemo, useState } from 'react';
import {
WalletClientError,
WalletHttpError,
} from '@vegaprotocol/wallet-client';
import { useVegaWallet } from './use-vega-wallet';
import type { VegaTransactionContentMap } from './vega-transaction-dialog';
import { VegaTransactionDialog } from './vega-transaction-dialog';
import type { Intent } from '@vegaprotocol/ui-toolkit';
import type { Transaction } from './connectors';
import type { WalletError } from './connectors';
import { ClientErrors } from './connectors';
export interface DialogProps {
@@ -43,21 +38,8 @@ export const initialState = {
dialogOpen: false,
};
export const orderErrorResolve = (err: Error | unknown): Error => {
if (err instanceof WalletClientError) {
return err;
} else if (err instanceof WalletHttpError) {
return ClientErrors.UNKNOWN;
} else if (err instanceof TypeError) {
return ClientErrors.NO_SERVICE;
} else if (err instanceof Error) {
return err;
}
return ClientErrors.UNKNOWN;
};
export const useVegaTransaction = () => {
const { sendTx, disconnect } = useVegaWallet();
const { sendTx } = useVegaWallet();
const [transaction, _setTransaction] = useState<VegaTxState>(initialState);
const setTransaction = useCallback((update: Partial<VegaTxState>) => {
@@ -106,10 +88,7 @@ export const useVegaTransaction = () => {
return null;
} catch (err) {
const error = orderErrorResolve(err);
if ((error as WalletError).code === ClientErrors.NO_SERVICE.code) {
disconnect();
}
const error = err instanceof Error ? err : ClientErrors.UNKNOWN;
setTransaction({
error,
status: VegaTxStatus.Error,
@@ -117,7 +96,7 @@ export const useVegaTransaction = () => {
return null;
}
},
[sendTx, setTransaction, reset, disconnect]
[sendTx, setTransaction, reset]
);
const Dialog = useMemo(() => {