feat(explorer): delegate/undelegate submission view (#2381)
* fix(explorer): initial tx delegation view * fix(explorer): fix dangling duplicate type * feat(explorer): add governanceasset component to show prices correct for that asset * feat(explorer): add undelegate view * feat(explorer): status code component shows error message if available * fix(explorer): remove unused type
This commit is contained in:
parent
71399105ee
commit
1180a5ff97
@ -0,0 +1,5 @@
|
||||
query ExplorerGovernanceAsset {
|
||||
networkParameter(key: "reward.asset") {
|
||||
value
|
||||
}
|
||||
}
|
45
apps/explorer/src/app/components/asset-balance/__generated__/Governance-asset.ts
generated
Normal file
45
apps/explorer/src/app/components/asset-balance/__generated__/Governance-asset.ts
generated
Normal file
@ -0,0 +1,45 @@
|
||||
import * as Types from '@vegaprotocol/types';
|
||||
|
||||
import { gql } from '@apollo/client';
|
||||
import * as Apollo from '@apollo/client';
|
||||
const defaultOptions = {} as const;
|
||||
export type ExplorerGovernanceAssetQueryVariables = Types.Exact<{ [key: string]: never; }>;
|
||||
|
||||
|
||||
export type ExplorerGovernanceAssetQuery = { __typename?: 'Query', networkParameter?: { __typename?: 'NetworkParameter', value: string } | null };
|
||||
|
||||
|
||||
export const ExplorerGovernanceAssetDocument = gql`
|
||||
query ExplorerGovernanceAsset {
|
||||
networkParameter(key: "reward.asset") {
|
||||
value
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
/**
|
||||
* __useExplorerGovernanceAssetQuery__
|
||||
*
|
||||
* To run a query within a React component, call `useExplorerGovernanceAssetQuery` and pass it any options that fit your needs.
|
||||
* When your component renders, `useExplorerGovernanceAssetQuery` returns an object from Apollo Client that contains loading, error, and data properties
|
||||
* you can use to render your UI.
|
||||
*
|
||||
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
|
||||
*
|
||||
* @example
|
||||
* const { data, loading, error } = useExplorerGovernanceAssetQuery({
|
||||
* variables: {
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useExplorerGovernanceAssetQuery(baseOptions?: Apollo.QueryHookOptions<ExplorerGovernanceAssetQuery, ExplorerGovernanceAssetQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useQuery<ExplorerGovernanceAssetQuery, ExplorerGovernanceAssetQueryVariables>(ExplorerGovernanceAssetDocument, options);
|
||||
}
|
||||
export function useExplorerGovernanceAssetLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<ExplorerGovernanceAssetQuery, ExplorerGovernanceAssetQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useLazyQuery<ExplorerGovernanceAssetQuery, ExplorerGovernanceAssetQueryVariables>(ExplorerGovernanceAssetDocument, options);
|
||||
}
|
||||
export type ExplorerGovernanceAssetQueryHookResult = ReturnType<typeof useExplorerGovernanceAssetQuery>;
|
||||
export type ExplorerGovernanceAssetLazyQueryHookResult = ReturnType<typeof useExplorerGovernanceAssetLazyQuery>;
|
||||
export type ExplorerGovernanceAssetQueryResult = Apollo.QueryResult<ExplorerGovernanceAssetQuery, ExplorerGovernanceAssetQueryVariables>;
|
@ -13,6 +13,7 @@ export type AssetBalanceProps = {
|
||||
*/
|
||||
const AssetBalance = ({ assetId, price }: AssetBalanceProps) => {
|
||||
const { data } = useExplorerAssetQuery({
|
||||
fetchPolicy: 'cache-first',
|
||||
variables: { id: assetId },
|
||||
});
|
||||
|
||||
@ -23,7 +24,8 @@ const AssetBalance = ({ assetId, price }: AssetBalanceProps) => {
|
||||
|
||||
return (
|
||||
<div className="inline-block">
|
||||
<span>{label}</span> <AssetLink id={data?.asset?.id || ''} />
|
||||
<span>{label}</span>{' '}
|
||||
{data?.asset?.id ? <AssetLink id={data.asset.id} /> : null}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -0,0 +1,30 @@
|
||||
import { addDecimalsFormatNumber } from '@vegaprotocol/react-helpers';
|
||||
import { useExplorerGovernanceAssetQuery } from './__generated__/Governance-asset';
|
||||
import AssetBalance from './asset-balance';
|
||||
|
||||
export type GovernanceAssetBalanceProps = {
|
||||
price: string;
|
||||
};
|
||||
|
||||
const DEFAULT_DECIMALS = 18;
|
||||
|
||||
/**
|
||||
* Effectively a wrapper around AssetBalance that does an extra query to fetch
|
||||
* the governance asset first, which is set by a network parameter
|
||||
*/
|
||||
const GovernanceAssetBalance = ({ price }: GovernanceAssetBalanceProps) => {
|
||||
const { data } = useExplorerGovernanceAssetQuery();
|
||||
|
||||
if (data && data.networkParameter?.value) {
|
||||
const governanceAssetId = data.networkParameter.value;
|
||||
return <AssetBalance price={price} assetId={governanceAssetId} />;
|
||||
} else {
|
||||
return (
|
||||
<div className="inline-block">
|
||||
<span>{addDecimalsFormatNumber(price, DEFAULT_DECIMALS)}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default GovernanceAssetBalance;
|
@ -16,6 +16,7 @@ export type AssetLinkProps = Partial<ComponentProps<typeof Link>> & {
|
||||
*/
|
||||
const AssetLink = ({ id, ...props }: AssetLinkProps) => {
|
||||
const { data } = useExplorerAssetQuery({
|
||||
fetchPolicy: 'cache-first',
|
||||
variables: { id },
|
||||
});
|
||||
|
||||
|
@ -1,75 +0,0 @@
|
||||
import * as Types from '@vegaprotocol/types';
|
||||
|
||||
import { gql } from '@apollo/client';
|
||||
import * as Apollo from '@apollo/client';
|
||||
const defaultOptions = {} as const;
|
||||
export type ExplorerNodeVoteQueryVariables = Types.Exact<{
|
||||
id: Types.Scalars['ID'];
|
||||
}>;
|
||||
|
||||
|
||||
export type ExplorerNodeVoteQuery = { __typename?: 'Query', withdrawal?: { __typename?: 'Withdrawal', id: string, status: Types.WithdrawalStatus, createdTimestamp: any, withdrawnTimestamp?: any | null, txHash?: string | null, asset: { __typename?: 'Asset', id: string, name: string, decimals: number }, party: { __typename?: 'Party', id: string } } | null, deposit?: { __typename?: 'Deposit', id: string, status: Types.DepositStatus, createdTimestamp: any, creditedTimestamp?: any | null, txHash?: string | null, asset: { __typename?: 'Asset', id: string, name: string, decimals: number }, party: { __typename?: 'Party', id: string } } | null };
|
||||
|
||||
|
||||
export const ExplorerNodeVoteDocument = gql`
|
||||
query ExplorerNodeVote($id: ID!) {
|
||||
withdrawal(id: $id) {
|
||||
id
|
||||
status
|
||||
createdTimestamp
|
||||
withdrawnTimestamp
|
||||
txHash
|
||||
asset {
|
||||
id
|
||||
name
|
||||
decimals
|
||||
}
|
||||
party {
|
||||
id
|
||||
}
|
||||
}
|
||||
deposit(id: $id) {
|
||||
id
|
||||
status
|
||||
createdTimestamp
|
||||
creditedTimestamp
|
||||
txHash
|
||||
asset {
|
||||
id
|
||||
name
|
||||
decimals
|
||||
}
|
||||
party {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
/**
|
||||
* __useExplorerNodeVoteQuery__
|
||||
*
|
||||
* To run a query within a React component, call `useExplorerNodeVoteQuery` and pass it any options that fit your needs.
|
||||
* When your component renders, `useExplorerNodeVoteQuery` returns an object from Apollo Client that contains loading, error, and data properties
|
||||
* you can use to render your UI.
|
||||
*
|
||||
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
|
||||
*
|
||||
* @example
|
||||
* const { data, loading, error } = useExplorerNodeVoteQuery({
|
||||
* variables: {
|
||||
* id: // value for 'id'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useExplorerNodeVoteQuery(baseOptions: Apollo.QueryHookOptions<ExplorerNodeVoteQuery, ExplorerNodeVoteQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useQuery<ExplorerNodeVoteQuery, ExplorerNodeVoteQueryVariables>(ExplorerNodeVoteDocument, options);
|
||||
}
|
||||
export function useExplorerNodeVoteLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<ExplorerNodeVoteQuery, ExplorerNodeVoteQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useLazyQuery<ExplorerNodeVoteQuery, ExplorerNodeVoteQueryVariables>(ExplorerNodeVoteDocument, options);
|
||||
}
|
||||
export type ExplorerNodeVoteQueryHookResult = ReturnType<typeof useExplorerNodeVoteQuery>;
|
||||
export type ExplorerNodeVoteLazyQueryHookResult = ReturnType<typeof useExplorerNodeVoteLazyQuery>;
|
||||
export type ExplorerNodeVoteQueryResult = Apollo.QueryResult<ExplorerNodeVoteQuery, ExplorerNodeVoteQueryVariables>;
|
@ -13,6 +13,7 @@ export const successCodes = new Set([0]);
|
||||
interface ChainResponseCodeProps {
|
||||
code: number;
|
||||
hideLabel?: boolean;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -23,6 +24,7 @@ interface ChainResponseCodeProps {
|
||||
export const ChainResponseCode = ({
|
||||
code,
|
||||
hideLabel = false,
|
||||
error,
|
||||
}: ChainResponseCodeProps) => {
|
||||
const isSuccess = successCodes.has(code);
|
||||
|
||||
@ -39,6 +41,9 @@ export const ChainResponseCode = ({
|
||||
{icon}
|
||||
</span>
|
||||
{hideLabel ? null : <span>{label}</span>}
|
||||
{!hideLabel && !!error ? (
|
||||
<span className="ml-1">— {error}</span>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -73,7 +73,7 @@ export const TxDetailsShared = ({
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{t('Response code')}</TableCell>
|
||||
<TableCell>
|
||||
<ChainResponseCode code={txData.code} />
|
||||
<ChainResponseCode code={txData.code} error={txData.error} />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</>
|
||||
|
@ -0,0 +1,58 @@
|
||||
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 { TableCell, TableRow, TableWithTbody } from '../../table';
|
||||
import { NodeLink } from '../../links';
|
||||
import GovernanceAssetBalance from '../../asset-balance/governance-asset-balance';
|
||||
import type { components } from '../../../../types/explorer';
|
||||
|
||||
interface TxDetailsDelegateProps {
|
||||
txData: BlockExplorerTransactionResult | undefined;
|
||||
pubKey: string | undefined;
|
||||
blockData: TendermintBlocksResponse | undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* There aren't currently good APIs for exploring delegations or epochs so this
|
||||
* view is currently a little basic - but it gives the key details.
|
||||
*
|
||||
* Future improvements could be:
|
||||
* - Show if the delegation has taken effect or not, based on epoch
|
||||
*
|
||||
* The signature can be turned in to an id with txSignatureToDeterministicId but
|
||||
* for now there are no details to fetch.
|
||||
*/
|
||||
export const TxDetailsDelegate = ({
|
||||
txData,
|
||||
pubKey,
|
||||
blockData,
|
||||
}: TxDetailsDelegateProps) => {
|
||||
if (!txData || !txData.command.delegateSubmission) {
|
||||
return <>{t('Awaiting Block Explorer transaction details')}</>;
|
||||
}
|
||||
const d: components['schemas']['v1DelegateSubmission'] =
|
||||
txData.command.delegateSubmission;
|
||||
|
||||
return (
|
||||
<TableWithTbody className="mb-8">
|
||||
<TxDetailsShared txData={txData} pubKey={pubKey} blockData={blockData} />
|
||||
{d.nodeId ? (
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{t('Node')}</TableCell>
|
||||
<TableCell>
|
||||
<NodeLink id={d.nodeId} />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : null}
|
||||
{d.amount ? (
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{t('Amount')}</TableCell>
|
||||
<TableCell>
|
||||
<GovernanceAssetBalance price={d.amount} />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : null}
|
||||
</TableWithTbody>
|
||||
);
|
||||
};
|
@ -15,6 +15,8 @@ import { TxDetailsOrderCancel } from './tx-order-cancel';
|
||||
import get from 'lodash/get';
|
||||
import { TxDetailsOrderAmend } from './tx-order-amend';
|
||||
import { TxDetailsWithdrawSubmission } from './tx-withdraw-submission';
|
||||
import { TxDetailsDelegate } from './tx-delegation';
|
||||
import { TxDetailsUndelegate } from './tx-undelegation';
|
||||
|
||||
interface TxDetailsWrapperProps {
|
||||
txData: BlockExplorerTransactionResult | undefined;
|
||||
@ -91,6 +93,10 @@ function getTransactionComponent(txData?: BlockExplorerTransactionResult) {
|
||||
return TxDetailsNodeVote;
|
||||
case 'Withdraw':
|
||||
return TxDetailsWithdrawSubmission;
|
||||
case 'Delegate':
|
||||
return TxDetailsDelegate;
|
||||
case 'Undelegate':
|
||||
return TxDetailsUndelegate;
|
||||
default:
|
||||
return TxDetailsGeneric;
|
||||
}
|
||||
|
@ -3,8 +3,8 @@ import type { BlockExplorerTransactionResult } from '../../../routes/types/block
|
||||
import type { TendermintBlocksResponse } from '../../../routes/blocks/tendermint-blocks-response';
|
||||
import { TxDetailsShared } from './shared/tx-details-shared';
|
||||
import { TableCell, TableRow, TableWithTbody } from '../../table';
|
||||
import type { ExplorerNodeVoteQueryResult } from './__generated___/Node-vote';
|
||||
import { useExplorerNodeVoteQuery } from './__generated___/Node-vote';
|
||||
import type { ExplorerNodeVoteQueryResult } from './__generated__/Node-vote';
|
||||
import { useExplorerNodeVoteQuery } from './__generated__/Node-vote';
|
||||
import { PartyLink } from '../../links';
|
||||
import { Time } from '../../time';
|
||||
import {
|
||||
|
@ -0,0 +1,77 @@
|
||||
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 { TableCell, TableRow, TableWithTbody } from '../../table';
|
||||
import { NodeLink } from '../../links';
|
||||
import GovernanceAssetBalance from '../../asset-balance/governance-asset-balance';
|
||||
import type { components } from '../../../../types/explorer';
|
||||
|
||||
export const methodText: Record<
|
||||
components['schemas']['UndelegateSubmissionMethod'],
|
||||
string
|
||||
> = {
|
||||
METHOD_NOW: 'Immediate',
|
||||
METHOD_UNSPECIFIED: 'Unspecified',
|
||||
METHOD_AT_END_OF_EPOCH: 'End of epoch',
|
||||
// This will be removed in a future release
|
||||
METHOD_IN_ANGER: 'Immediate',
|
||||
};
|
||||
|
||||
interface TxDetailsUndelegateProps {
|
||||
txData: BlockExplorerTransactionResult | undefined;
|
||||
pubKey: string | undefined;
|
||||
blockData: TendermintBlocksResponse | undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* There aren't currently good APIs for exploring delegations or epochs so this
|
||||
* view is currently a little basic - but it gives the key details.
|
||||
*
|
||||
* Future improvements could be:
|
||||
* - Show if the undelegation has taken effect or not, based on epoch
|
||||
* - Show the the total stake for the node after undelegation takes effect
|
||||
*
|
||||
* The signature can be turned in to an id with txSignatureToDeterministicId but
|
||||
* for now there are no details to fetch.
|
||||
*/
|
||||
export const TxDetailsUndelegate = ({
|
||||
txData,
|
||||
pubKey,
|
||||
blockData,
|
||||
}: TxDetailsUndelegateProps) => {
|
||||
if (!txData || !txData.command.undelegateSubmission) {
|
||||
return <>{t('Awaiting Block Explorer transaction details')}</>;
|
||||
}
|
||||
|
||||
const u: components['schemas']['v1UndelegateSubmission'] =
|
||||
txData.command.undelegateSubmission;
|
||||
|
||||
return (
|
||||
<TableWithTbody className="mb-8">
|
||||
<TxDetailsShared txData={txData} pubKey={pubKey} blockData={blockData} />
|
||||
{u.nodeId ? (
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{t('Node')}</TableCell>
|
||||
<TableCell>
|
||||
<NodeLink id={u.nodeId} />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : null}
|
||||
{u.amount ? (
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{t('Amount')}</TableCell>
|
||||
<TableCell>
|
||||
<GovernanceAssetBalance price={u.amount} />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : null}
|
||||
{u.method ? (
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{t('When')}</TableCell>
|
||||
<TableCell>{methodText[u.method]}</TableCell>
|
||||
</TableRow>
|
||||
) : null}
|
||||
</TableWithTbody>
|
||||
);
|
||||
};
|
@ -13,6 +13,7 @@ export interface BlockExplorerTransactionResult {
|
||||
signature: {
|
||||
value: string;
|
||||
};
|
||||
error?: string;
|
||||
}
|
||||
|
||||
export interface BlockExplorerTransactions {
|
||||
|
Loading…
Reference in New Issue
Block a user