diff --git a/apps/explorer/src/app/components/txs/details/proposal/summary.tsx b/apps/explorer/src/app/components/txs/details/proposal/summary.tsx
index 0972e3d07..d694e827f 100644
--- a/apps/explorer/src/app/components/txs/details/proposal/summary.tsx
+++ b/apps/explorer/src/app/components/txs/details/proposal/summary.tsx
@@ -64,7 +64,9 @@ export const ProposalSummary = ({
return (
{id &&
}
- {rationale?.title &&
{rationale.title} }
+ {rationale?.title && (
+
{rationale.title}
+ )}
{rationale?.description && (
;
+
+
+export type ExplorerTransferVoteQuery = { __typename?: 'Query', transfer?: { __typename?: 'Transfer', reference?: string | null, timestamp: any, status: Types.TransferStatus, reason?: string | null, fromAccountType: Types.AccountType, from: string, to: string, toAccountType: Types.AccountType, amount: string, asset?: { __typename?: 'Asset', id: string } | null } | null };
+
+
+export const ExplorerTransferVoteDocument = gql`
+ query ExplorerTransferVote($id: ID!) {
+ transfer(id: $id) {
+ reference
+ timestamp
+ status
+ reason
+ fromAccountType
+ from
+ to
+ toAccountType
+ asset {
+ id
+ }
+ amount
+ }
+}
+ `;
+
+/**
+ * __useExplorerTransferVoteQuery__
+ *
+ * To run a query within a React component, call `useExplorerTransferVoteQuery` and pass it any options that fit your needs.
+ * When your component renders, `useExplorerTransferVoteQuery` 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 } = useExplorerTransferVoteQuery({
+ * variables: {
+ * id: // value for 'id'
+ * },
+ * });
+ */
+export function useExplorerTransferVoteQuery(baseOptions: Apollo.QueryHookOptions) {
+ const options = {...defaultOptions, ...baseOptions}
+ return Apollo.useQuery(ExplorerTransferVoteDocument, options);
+ }
+export function useExplorerTransferVoteLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions) {
+ const options = {...defaultOptions, ...baseOptions}
+ return Apollo.useLazyQuery(ExplorerTransferVoteDocument, options);
+ }
+export type ExplorerTransferVoteQueryHookResult = ReturnType;
+export type ExplorerTransferVoteLazyQueryHookResult = ReturnType;
+export type ExplorerTransferVoteQueryResult = Apollo.QueryResult;
\ No newline at end of file
diff --git a/apps/explorer/src/app/components/txs/details/transfer/blocks/transfer-participants.tsx b/apps/explorer/src/app/components/txs/details/transfer/blocks/transfer-participants.tsx
index e84e79f57..843e46a05 100644
--- a/apps/explorer/src/app/components/txs/details/transfer/blocks/transfer-participants.tsx
+++ b/apps/explorer/src/app/components/txs/details/transfer/blocks/transfer-participants.tsx
@@ -111,7 +111,7 @@ export function TransferParticipants({
@@ -120,7 +120,7 @@ export function TransferParticipants({
diff --git a/apps/explorer/src/app/components/txs/details/transfer/blocks/transfer-rewards.tsx b/apps/explorer/src/app/components/txs/details/transfer/blocks/transfer-rewards.tsx
index a9fffc30a..39116a94c 100644
--- a/apps/explorer/src/app/components/txs/details/transfer/blocks/transfer-rewards.tsx
+++ b/apps/explorer/src/app/components/txs/details/transfer/blocks/transfer-rewards.tsx
@@ -4,6 +4,7 @@ import { headerClasses, wrapperClasses } from '../transfer-details';
import type { components } from '../../../../../../types/explorer';
import type { Recurring } from '../transfer-details';
import { DispatchMetricLabels } from '@vegaprotocol/types';
+import { VegaIcon, VegaIconNames } from '@vegaprotocol/ui-toolkit';
export type Metric = components['schemas']['vegaDispatchMetric'];
export type Strategy = components['schemas']['vegaDispatchStrategy'];
@@ -13,6 +14,15 @@ const metricLabels: Record = {
...DispatchMetricLabels,
};
+// Maps the two (non-null) values of entityScope to the icon that represents it
+const entityScopeIcons: Record<
+ string,
+ typeof VegaIconNames[keyof typeof VegaIconNames]
+> = {
+ ENTITY_SCOPE_INDIVIDUALS: VegaIconNames.MAN,
+ ENTITY_SCOPE_TEAMS: VegaIconNames.TEAM,
+};
+
interface TransferRewardsProps {
recurring: Recurring;
}
@@ -34,7 +44,7 @@ export function TransferRewards({ recurring }: TransferRewardsProps) {
return (
{t('Reward metrics')}
-
+
{recurring.dispatchStrategy.assetForMetric ? (
{t('Asset')} :{' '}
@@ -44,6 +54,27 @@ export function TransferRewards({ recurring }: TransferRewardsProps) {
{t('Metric')} : {metricLabels[metric]}
+ {recurring.dispatchStrategy.entityScope &&
+ entityScopeIcons[recurring.dispatchStrategy.entityScope] ? (
+
+ {t('Scope')} :{' '}
+
+
+ ) : null}
+
+ {recurring.dispatchStrategy.individualScope}
+ {recurring.dispatchStrategy.teamScope}
+
+ {recurring.dispatchStrategy.lockPeriod &&
+ recurring.dispatchStrategy.lockPeriod !== '0' ? (
+
+ {t('Lock')} :{' '}
+ {recurring.dispatchStrategy.lockPeriod}
+
+ ) : null}
+
{recurring.dispatchStrategy.markets &&
recurring.dispatchStrategy.markets.length > 0 ? (
@@ -57,9 +88,30 @@ export function TransferRewards({ recurring }: TransferRewardsProps) {
) : null}
-
- {t('Factor')} : {recurring.factor}
-
+
+ {recurring.dispatchStrategy.stakingRequirement &&
+ recurring.dispatchStrategy.stakingRequirement !== '0' ? (
+
+ {t('Staking requirement')} :{' '}
+ {recurring.dispatchStrategy.stakingRequirement}
+
+ ) : null}
+
+ {recurring.dispatchStrategy.windowLength &&
+ recurring.dispatchStrategy.windowLength !== '0' ? (
+
+ {t('Window length')} :{' '}
+ {recurring.dispatchStrategy.windowLength}
+
+ ) : null}
+
+ {recurring.dispatchStrategy.rankTable &&
+ recurring.dispatchStrategy.rankTable.length > 0 ? (
+
+ {t('Ranks')} :{' '}
+ {recurring.dispatchStrategy.rankTable.toString()}
+
+ ) : null}
);
diff --git a/apps/explorer/src/app/components/txs/details/transfer/blocks/transfer-status.tsx b/apps/explorer/src/app/components/txs/details/transfer/blocks/transfer-status.tsx
new file mode 100644
index 000000000..b0052aa95
--- /dev/null
+++ b/apps/explorer/src/app/components/txs/details/transfer/blocks/transfer-status.tsx
@@ -0,0 +1,40 @@
+import { t } from '@vegaprotocol/i18n';
+import { headerClasses, wrapperClasses } from '../transfer-details';
+import { Icon, Loader } from '@vegaprotocol/ui-toolkit';
+import type { ApolloError } from '@apollo/client';
+
+interface TransferStatusProps {
+ status: string;
+ error: ApolloError | undefined;
+ loading: boolean;
+}
+
+/**
+ * Renderer for a transfer. These can vary quite
+ * widely, essentially every field can be null.
+ *
+ * @param transfer A recurring transfer object
+ */
+export function TransferStatusView({ status, loading }: TransferStatusProps) {
+ if (!status) {
+ return null;
+ }
+
+ return (
+
+
{t('Status')}
+
+ {loading ? (
+
+ ) : (
+ <>
+
+
+
+
{status}
+ >
+ )}
+
+
+ );
+}
diff --git a/apps/explorer/src/app/components/txs/details/transfer/transfer-details.tsx b/apps/explorer/src/app/components/txs/details/transfer/transfer-details.tsx
index de807be5b..ff303e335 100644
--- a/apps/explorer/src/app/components/txs/details/transfer/transfer-details.tsx
+++ b/apps/explorer/src/app/components/txs/details/transfer/transfer-details.tsx
@@ -2,6 +2,9 @@ import type { components } from '../../../../../types/explorer';
import { TransferRepeat } from './blocks/transfer-repeat';
import { TransferRewards } from './blocks/transfer-rewards';
import { TransferParticipants } from './blocks/transfer-participants';
+import { useExplorerTransferVoteQuery } from './__generated__/Transfer';
+import { TransferStatusView } from './blocks/transfer-status';
+import { TransferStatus } from '@vegaprotocol/types';
export type Recurring = components['schemas']['commandsv1RecurringTransfer'];
export type Metric = components['schemas']['vegaDispatchMetric'];
@@ -16,6 +19,9 @@ export type Transfer = components['schemas']['commandsv1Transfer'];
interface TransferDetailsProps {
transfer: Transfer;
from: string;
+ id: string;
+ // If set, all blocks except the status one are hidden
+ statusOnly?: boolean;
}
/**
@@ -24,15 +30,37 @@ interface TransferDetailsProps {
*
* @param transfer A recurring transfer object
*/
-export function TransferDetails({ transfer, from }: TransferDetailsProps) {
+export function TransferDetails({
+ transfer,
+ from,
+ id,
+ statusOnly = false,
+}: TransferDetailsProps) {
const recurring = transfer.recurring;
+ // Currently all this is passed in to TransferStatus, but the extra details
+ // may be useful in the future.
+ const { data, error, loading } = useExplorerTransferVoteQuery({
+ variables: { id },
+ });
+
+ const status = error
+ ? TransferStatus.STATUS_REJECTED
+ : data?.transfer?.status;
+
return (
-
- {recurring ? : null}
- {recurring && recurring.dispatchStrategy ? (
-
+ {statusOnly ? null : (
+ <>
+
+ {recurring ? : null}
+ {recurring && recurring.dispatchStrategy ? (
+
+ ) : null}
+ >
+ )}
+ {status ? (
+
) : null}
);
diff --git a/apps/explorer/src/app/components/txs/details/tx-proposal.tsx b/apps/explorer/src/app/components/txs/details/tx-proposal.tsx
index 12a89ab24..cfeb2c665 100644
--- a/apps/explorer/src/app/components/txs/details/tx-proposal.tsx
+++ b/apps/explorer/src/app/components/txs/details/tx-proposal.tsx
@@ -12,6 +12,8 @@ import { ProposalSignatureBundleNewAsset } from './proposal/signature-bundle-new
import { ProposalSignatureBundleUpdateAsset } from './proposal/signature-bundle-update';
import { MarketLink } from '../../links';
import { formatNumber } from '@vegaprotocol/utils';
+import { TransferDetails } from './transfer/transfer-details';
+import { proposalToTransfer } from '../lib/proposal-to-transfer';
export type Proposal = components['schemas']['v1ProposalSubmission'];
export type ProposalTerms = components['schemas']['vegaProposalTerms'];
@@ -104,6 +106,12 @@ export const TxProposal = ({ txData, pubKey, blockData }: TxProposalProps) => {
? ProposalSignatureBundleNewAsset
: ProposalSignatureBundleUpdateAsset;
+ let transfer, from;
+ if (proposal.terms?.newTransfer?.changes) {
+ transfer = proposalToTransfer(proposal.terms?.newTransfer.changes);
+ from = proposal.terms.newTransfer.changes.source;
+ }
+
return (
<>
@@ -149,14 +157,27 @@ export const TxProposal = ({ txData, pubKey, blockData }: TxProposalProps) => {
>
) : null}
+
+
{proposalRequiresSignatureBundle(proposal) && (
)}
+
+ {transfer && (
+
+
+
+ )}
>
);
};
diff --git a/apps/explorer/src/app/components/txs/details/tx-transfer.tsx b/apps/explorer/src/app/components/txs/details/tx-transfer.tsx
index feb3ecf0a..3b9fed1e1 100644
--- a/apps/explorer/src/app/components/txs/details/tx-transfer.tsx
+++ b/apps/explorer/src/app/components/txs/details/tx-transfer.tsx
@@ -60,7 +60,7 @@ export const TxDetailsTransfer = ({
}
const from = txData.submitter;
-
+ const id = txSignatureToDeterministicId(txData.signature.value);
return (
<>
@@ -70,9 +70,7 @@ export const TxDetailsTransfer = ({
{t('Transfer ID')}
-
- {txSignatureToDeterministicId(txData.signature.value)}
-
+ {id}
) : null}
-
+
>
);
};
diff --git a/apps/explorer/src/app/components/txs/lib/proposal-to-transfer.ts b/apps/explorer/src/app/components/txs/lib/proposal-to-transfer.ts
new file mode 100644
index 000000000..d24f4bb08
--- /dev/null
+++ b/apps/explorer/src/app/components/txs/lib/proposal-to-transfer.ts
@@ -0,0 +1,29 @@
+import type { components } from '../../../../types/explorer';
+
+type TransferProposal = components['schemas']['vegaNewTransferConfiguration'];
+type ActualTransfer = components['schemas']['commandsv1Transfer'];
+
+/**
+ * Converts a governance proposal for a transfer in to a transfer command that the
+ * TransferDetails component can then render. The types are very similar, but do not
+ * map precisely to each other due to some missing fields and some different field
+ * names.
+ *
+ * @param proposal Governance proposal for a transfer
+ * @returns transfer a Transfer object as if it had been submitted
+ */
+export function proposalToTransfer(proposal: TransferProposal): ActualTransfer {
+ return {
+ amount: proposal.amount,
+ asset: proposal.asset,
+ // On a transfer, 'from' is determined by the submitter, so there is no 'from' field
+ // fromAccountType does exist and is just named differently on the proposal
+ fromAccountType: proposal.sourceType,
+ oneOff: proposal.oneOff,
+ recurring: proposal.recurring,
+ // There is no reference applied on governance initiated transfers
+ reference: '',
+ to: proposal.destination,
+ toAccountType: proposal.destinationType,
+ };
+}