feat(governance): governance transfers (#4411)

This commit is contained in:
Art 2023-10-03 17:35:17 +02:00 committed by GitHub
parent 5b5765ef63
commit d510330c6b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 557 additions and 9 deletions

View File

@ -34,3 +34,4 @@ NX_SUCCESSOR_MARKETS=true
NX_METAMASK_SNAPS=true
NX_PRODUCT_PERPETUALS=false
NX_UPDATE_MARKET_STATE=false
NX_GOVERNANCE_TRANSFERS=false

View File

@ -22,3 +22,4 @@ NX_SUCCESSOR_MARKETS=true
NX_METAMASK_SNAPS=true
NX_PRODUCT_PERPETUALS=true
NX_UPDATE_MARKET_STATE=true
NX_GOVERNANCE_TRANSFERS=true

View File

@ -888,5 +888,7 @@
"HowToPropose": "How to make a proposal",
"HowToProposeRawStep1": "1. Sense check your proposal with the community on the forum:",
"HowToProposeRawStep2": "2. Use the appropriate proposal template in the docs:",
"HowToProposeRawStep3": "3. Submit on-chain below"
"HowToProposeRawStep3": "3. Submit on-chain below",
"proposalTransferDetails": "New governance transfer details",
"proposalCancelTransferDetails": "Cancel governance transfer details"
}

View File

@ -7,12 +7,17 @@ import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
import { truncateMiddle } from '../../../../lib/truncate-middle';
import { CurrentProposalState } from '../current-proposal-state';
import { ProposalInfoLabel } from '../proposal-info-label';
import { useSuccessorMarketProposalDetails } from '@vegaprotocol/proposals';
import {
useCancelTransferProposalDetails,
useNewTransferProposalDetails,
useSuccessorMarketProposalDetails,
} from '@vegaprotocol/proposals';
import { FLAGS } from '@vegaprotocol/environment';
import Routes from '../../../routes';
import { Link } from 'react-router-dom';
import type { VoteState } from '../vote-details/use-user-vote';
import { VoteBreakdown } from '../vote-breakdown';
import { GovernanceTransferKindMapping } from '@vegaprotocol/types';
export const ProposalHeader = ({
proposal,
@ -147,6 +152,20 @@ export const ProposalHeader = ({
);
break;
}
case 'NewTransfer':
proposalType = 'NewTransfer';
fallbackTitle = t('NewTransferProposal');
details = FLAGS.GOVERNANCE_TRANSFERS ? (
<NewTransferSummary proposalId={proposal?.id} />
) : null;
break;
case 'CancelTransfer':
proposalType = 'CancelTransfer';
fallbackTitle = t('CancelTransferProposal');
details = FLAGS.GOVERNANCE_TRANSFERS ? (
<CancelTransferSummary proposalId={proposal?.id} />
) : null;
break;
}
return (
@ -224,3 +243,36 @@ const SuccessorCode = ({ proposalId }: { proposalId?: string | null }) => {
</span>
) : null;
};
const NewTransferSummary = ({ proposalId }: { proposalId?: string | null }) => {
const { t } = useTranslation();
const details = useNewTransferProposalDetails(proposalId);
if (!details) return null;
return (
<span>
{GovernanceTransferKindMapping[details.kind.__typename]}{' '}
{t('transfer from')} <Lozenge>{truncateMiddle(details.source)}</Lozenge>{' '}
{t('to')} <Lozenge>{truncateMiddle(details.destination)}</Lozenge>
</span>
);
};
const CancelTransferSummary = ({
proposalId,
}: {
proposalId?: string | null;
}) => {
const { t } = useTranslation();
const details = useCancelTransferProposalDetails(proposalId);
if (!details) return null;
return (
<span>
{t('Cancel transfer: ')}{' '}
<Lozenge>{truncateMiddle(details.transferId)}</Lozenge>
</span>
);
};

View File

@ -0,0 +1,2 @@
export * from './proposal-transfer-details';
export * from './proposal-cancel-transfer-details';

View File

@ -0,0 +1,37 @@
import { useTranslation } from 'react-i18next';
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
import { useCancelTransferProposalDetails } from '@vegaprotocol/proposals';
import {
KeyValueTable,
KeyValueTableRow,
RoundedWrapper,
} from '@vegaprotocol/ui-toolkit';
import { SubHeading } from '../../../../components/heading';
export const ProposalCancelTransferDetails = ({
proposal,
}: {
proposal: ProposalFieldsFragment | ProposalQuery['proposal'];
}) => {
const { t } = useTranslation();
const details = useCancelTransferProposalDetails(proposal?.id);
if (!details) {
return null;
}
return (
<>
<SubHeading title={t('proposalCancelTransferDetails')} />
<RoundedWrapper paddingBottom={true}>
<KeyValueTable data-testid="proposal-cancel-transfer-details-table">
<KeyValueTableRow noBorder={true}>
{t('transferId')}
{details.transferId}
</KeyValueTableRow>
</KeyValueTable>
</RoundedWrapper>
</>
);
};

View File

@ -0,0 +1,145 @@
import { useState } from 'react';
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
import { CollapsibleToggle } from '../../../../components/collapsible-toggle';
import { SubHeading } from '../../../../components/heading';
import { useTranslation } from 'react-i18next';
import {
KeyValueTable,
KeyValueTableRow,
RoundedWrapper,
Tooltip,
} from '@vegaprotocol/ui-toolkit';
import { useNewTransferProposalDetails } from '@vegaprotocol/proposals';
import {
AccountTypeMapping,
DescriptionGovernanceTransferTypeMapping,
GovernanceTransferKindMapping,
GovernanceTransferTypeMapping,
} from '@vegaprotocol/types';
import {
addDecimalsFormatNumberQuantum,
formatDateWithLocalTimezone,
} from '@vegaprotocol/utils';
export const ProposalTransferDetails = ({
proposal,
}: {
proposal: ProposalFieldsFragment | ProposalQuery['proposal'];
}) => {
const { t } = useTranslation();
const [show, setShow] = useState(false);
const details = useNewTransferProposalDetails(proposal?.id);
if (!details) {
return null;
}
return (
<>
<CollapsibleToggle
toggleState={show}
setToggleState={setShow}
dataTestId="proposal-transfer-details"
>
<SubHeading title={t('proposalTransferDetails')} />
</CollapsibleToggle>
{show && (
<RoundedWrapper paddingBottom={true}>
<KeyValueTable data-testid="proposal-transfer-details-table">
{/* The source account */}
<KeyValueTableRow>
{t('Source')}
{details.source}
</KeyValueTableRow>
{/* The type of source account */}
<KeyValueTableRow>
{t('Source Type')}
{AccountTypeMapping[details.sourceType]}
</KeyValueTableRow>
{/* The destination account */}
<KeyValueTableRow>
{t('Destination')}
{details.destination}
</KeyValueTableRow>
{/* The type of destination account */}
<KeyValueTableRow>
{t('Destination Type')}
{AccountTypeMapping[details.destinationType]}
</KeyValueTableRow>
{/* The asset to transfer */}
<KeyValueTableRow>
{t('Asset')}
{details.asset.symbol}
</KeyValueTableRow>
{/*The fraction of the balance to be transfer */}
<KeyValueTableRow>
{t('Fraction Of Balance')}
{`${Number(details.fraction_of_balance) * 100}%`}
</KeyValueTableRow>
{/* The maximum amount to be transferred */}
<KeyValueTableRow>
{t('Amount')}
{addDecimalsFormatNumberQuantum(
details.amount,
details.asset.decimals,
details.asset.quantum
)}
</KeyValueTableRow>
{/* The type of the governance transfer */}
<KeyValueTableRow>
{t('Transfer Type')}
<Tooltip
description={
DescriptionGovernanceTransferTypeMapping[details.transferType]
}
>
<span>
{GovernanceTransferTypeMapping[details.transferType]}
</span>
</Tooltip>
</KeyValueTableRow>
{/* The type of governance transfer being made, i.e. a one-off or recurring trans */}
<KeyValueTableRow>
{t('Kind')}
{GovernanceTransferKindMapping[details.kind.__typename]}
</KeyValueTableRow>
{details.kind.__typename === 'OneOffGovernanceTransfer' &&
details.kind.deliverOn && (
<KeyValueTableRow noBorder={true}>
{t('Deliver On')}
{formatDateWithLocalTimezone(
new Date(details.kind.deliverOn)
)}
</KeyValueTableRow>
)}
{details.kind.__typename === 'RecurringGovernanceTransfer' && (
<>
<KeyValueTableRow noBorder={!details.kind.endEpoch}>
{t('Start On')}
<span>{details.kind.startEpoch}</span>
</KeyValueTableRow>
{details.kind.endEpoch && (
<KeyValueTableRow noBorder={true}>
{t('End on')}
{details.kind.endEpoch}
</KeyValueTableRow>
)}
</>
)}
</KeyValueTable>
</RoundedWrapper>
)}
</>
);
};

View File

@ -20,6 +20,11 @@ import { ProposalUpdateMarketState } from '../proposal-update-market-state';
import type { NetworkParamsResult } from '@vegaprotocol/network-parameters';
import { useVoteSubmit } from '@vegaprotocol/proposals';
import { useUserVote } from '../vote-details/use-user-vote';
import {
ProposalCancelTransferDetails,
ProposalTransferDetails,
} from '../proposal-transfer';
import { FLAGS } from '@vegaprotocol/environment';
export interface ProposalProps {
proposal: ProposalQuery['proposal'];
@ -99,9 +104,37 @@ export const Proposal = ({
minVoterBalance =
networkParams.governance_proposal_freeform_minVoterBalance;
break;
case 'NewTransfer':
// TODO: check minVoterBalance for 'NewTransfer'
minVoterBalance =
networkParams.governance_proposal_freeform_minVoterBalance;
break;
case 'CancelTransfer':
// TODO: check minVoterBalance for 'CancelTransfer'
minVoterBalance =
networkParams.governance_proposal_freeform_minVoterBalance;
}
}
// Show governance transfer details only if the GOVERNANCE_TRANSFERS flag is on.
const governanceTransferDetails = FLAGS.GOVERNANCE_TRANSFERS && (
<>
{proposal.terms.change.__typename === 'NewTransfer' && (
/** Governance New Transfer Details */
<div className="mb-4">
<ProposalTransferDetails proposal={proposal} />
</div>
)}
{proposal.terms.change.__typename === 'CancelTransfer' && (
/** Governance Cancel Transfer Details */
<div className="mb-4">
<ProposalCancelTransferDetails proposal={proposal} />
</div>
)}
</>
);
return (
<section data-testid="proposal">
<div className="flex items-center gap-1 mb-6">
@ -187,6 +220,8 @@ export const Proposal = ({
</div>
)}
{governanceTransferDetails}
<div className="mb-10">
<RoundedWrapper paddingBottom={true}>
<UserVote

View File

@ -1,4 +1,4 @@
import { act, render, screen, within } from '@testing-library/react';
import { act, render, screen, waitFor, within } from '@testing-library/react';
import { MemoryRouter } from 'react-router-dom';
import { Closed } from './closed';
import { MarketStateMapping, PropertyKeyType } from '@vegaprotocol/types';
@ -300,9 +300,11 @@ describe('Closed', () => {
].includes(m.node.state);
});
// check rows length is correct
const rows = container.getAllByRole('row');
expect(rows).toHaveLength(expectedRows.length);
await waitFor(() => {
// check rows length is correct
const rows = container.getAllByRole('row');
expect(rows).toHaveLength(expectedRows.length);
});
// check that only included ids are shown
const cells = screen

View File

@ -423,6 +423,12 @@ function compileFeatureFlags(): FeatureFlags {
process.env['NX_UPDATE_MARKET_STATE']
) as string
),
GOVERNANCE_TRANSFERS: TRUTHY.includes(
windowOrDefault(
'NX_GOVERNANCE_TRANSFERS',
process.env['NX_GOVERNANCE_TRANSFERS']
) as string
),
};
const EXPLORER_FLAGS = {
EXPLORER_ASSETS: TRUTHY.includes(

View File

@ -25,6 +25,7 @@ export type CosmicElevatorFlags = Pick<
| 'METAMASK_SNAPS'
| 'REFERRALS'
| 'UPDATE_MARKET_STATE'
| 'GOVERNANCE_TRANSFERS'
>;
export type Configuration = z.infer<typeof tomlConfigSchema>;
export const CUSTOM_NODE_KEY = 'custom' as const;

View File

@ -80,6 +80,7 @@ const COSMIC_ELEVATOR_FLAGS = {
METAMASK_SNAPS: z.optional(z.boolean()),
REFERRALS: z.optional(z.boolean()),
UPDATE_MARKET_STATE: z.optional(z.boolean()),
GOVERNANCE_TRANSFERS: z.optional(z.boolean()),
};
const EXPLORER_FLAGS = {

View File

@ -96,6 +96,7 @@ export const createMarketFragment = (
filters: [
{
__typename: 'Filter',
conditions: [],
key: {
__typename: 'PropertyKey',
name: 'settlement-data-property',
@ -129,6 +130,7 @@ export const createMarketFragment = (
filters: [
{
__typename: 'Filter',
conditions: [],
key: {
__typename: 'PropertyKey',
name: 'settlement-data-property',

View File

@ -334,6 +334,36 @@ fragment UpdateNetworkParameterFields on UpdateNetworkParameter {
}
}
fragment NewTransferFields on NewTransfer {
source
sourceType
destination
destinationType
asset {
id
symbol
decimals
quantum
}
fraction_of_balance
amount
transferType
kind {
__typename
... on OneOffGovernanceTransfer {
deliverOn
}
... on RecurringGovernanceTransfer {
startEpoch
endEpoch
}
}
}
fragment CancelTransferFields on CancelTransfer {
transferId
}
fragment ProposalListFields on Proposal {
id
rationale {

File diff suppressed because one or more lines are too long

View File

@ -67,3 +67,29 @@ query InstrumentDetails($marketId: ID!) {
}
}
}
query NewTransferDetails($proposalId: ID!) {
proposal(id: $proposalId) {
id
terms {
change {
... on NewTransfer {
...NewTransferFields
}
}
}
}
}
query CancelTransferDetails($proposalId: ID!) {
proposal(id: $proposalId) {
id
terms {
change {
... on CancelTransfer {
...CancelTransferFields
}
}
}
}
}

View File

@ -1,7 +1,7 @@
import * as Types from '@vegaprotocol/types';
import { gql } from '@apollo/client';
import { UpdateNetworkParameterFieldsFragmentDoc } from '../../proposals-data-provider/__generated__/Proposals';
import { UpdateNetworkParameterFieldsFragmentDoc, NewTransferFieldsFragmentDoc, CancelTransferFieldsFragmentDoc } from '../../proposals-data-provider/__generated__/Proposals';
import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
export type ProposalEventFieldsFragment = { __typename?: 'Proposal', id?: string | null, reference: string, state: Types.ProposalState, rejectionReason?: Types.ProposalRejectionReason | null, errorDetails?: string | null };
@ -41,6 +41,20 @@ export type InstrumentDetailsQueryVariables = Types.Exact<{
export type InstrumentDetailsQuery = { __typename?: 'Query', market?: { __typename?: 'Market', tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', code: string, name: string } } } | null };
export type NewTransferDetailsQueryVariables = Types.Exact<{
proposalId: Types.Scalars['ID'];
}>;
export type NewTransferDetailsQuery = { __typename?: 'Query', proposal?: { __typename?: 'Proposal', id?: string | null, terms: { __typename?: 'ProposalTerms', change: { __typename?: 'CancelTransfer' } | { __typename?: 'NewAsset' } | { __typename?: 'NewFreeform' } | { __typename?: 'NewMarket' } | { __typename?: 'NewSpotMarket' } | { __typename?: 'NewTransfer', source: string, sourceType: Types.AccountType, destination: string, destinationType: Types.AccountType, fraction_of_balance: string, amount: string, transferType: Types.GovernanceTransferType, asset: { __typename?: 'Asset', id: string, symbol: string, decimals: number, quantum: string }, kind: { __typename: 'OneOffGovernanceTransfer', deliverOn?: any | null } | { __typename: 'RecurringGovernanceTransfer', startEpoch: number, endEpoch?: number | null } } | { __typename?: 'UpdateAsset' } | { __typename?: 'UpdateMarket' } | { __typename?: 'UpdateMarketState' } | { __typename?: 'UpdateNetworkParameter' } | { __typename?: 'UpdateReferralProgram' } | { __typename?: 'UpdateSpotMarket' } | { __typename?: 'UpdateVolumeDiscountProgram' } } } | null };
export type CancelTransferDetailsQueryVariables = Types.Exact<{
proposalId: Types.Scalars['ID'];
}>;
export type CancelTransferDetailsQuery = { __typename?: 'Query', proposal?: { __typename?: 'Proposal', id?: string | null, terms: { __typename?: 'ProposalTerms', change: { __typename?: 'CancelTransfer', transferId: string } | { __typename?: 'NewAsset' } | { __typename?: 'NewFreeform' } | { __typename?: 'NewMarket' } | { __typename?: 'NewSpotMarket' } | { __typename?: 'NewTransfer' } | { __typename?: 'UpdateAsset' } | { __typename?: 'UpdateMarket' } | { __typename?: 'UpdateMarketState' } | { __typename?: 'UpdateNetworkParameter' } | { __typename?: 'UpdateReferralProgram' } | { __typename?: 'UpdateSpotMarket' } | { __typename?: 'UpdateVolumeDiscountProgram' } } } | null };
export const ProposalEventFieldsFragmentDoc = gql`
fragment ProposalEventFields on Proposal {
id
@ -246,4 +260,88 @@ export function useInstrumentDetailsLazyQuery(baseOptions?: Apollo.LazyQueryHook
}
export type InstrumentDetailsQueryHookResult = ReturnType<typeof useInstrumentDetailsQuery>;
export type InstrumentDetailsLazyQueryHookResult = ReturnType<typeof useInstrumentDetailsLazyQuery>;
export type InstrumentDetailsQueryResult = Apollo.QueryResult<InstrumentDetailsQuery, InstrumentDetailsQueryVariables>;
export type InstrumentDetailsQueryResult = Apollo.QueryResult<InstrumentDetailsQuery, InstrumentDetailsQueryVariables>;
export const NewTransferDetailsDocument = gql`
query NewTransferDetails($proposalId: ID!) {
proposal(id: $proposalId) {
id
terms {
change {
... on NewTransfer {
...NewTransferFields
}
}
}
}
}
${NewTransferFieldsFragmentDoc}`;
/**
* __useNewTransferDetailsQuery__
*
* To run a query within a React component, call `useNewTransferDetailsQuery` and pass it any options that fit your needs.
* When your component renders, `useNewTransferDetailsQuery` 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 } = useNewTransferDetailsQuery({
* variables: {
* proposalId: // value for 'proposalId'
* },
* });
*/
export function useNewTransferDetailsQuery(baseOptions: Apollo.QueryHookOptions<NewTransferDetailsQuery, NewTransferDetailsQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<NewTransferDetailsQuery, NewTransferDetailsQueryVariables>(NewTransferDetailsDocument, options);
}
export function useNewTransferDetailsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<NewTransferDetailsQuery, NewTransferDetailsQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<NewTransferDetailsQuery, NewTransferDetailsQueryVariables>(NewTransferDetailsDocument, options);
}
export type NewTransferDetailsQueryHookResult = ReturnType<typeof useNewTransferDetailsQuery>;
export type NewTransferDetailsLazyQueryHookResult = ReturnType<typeof useNewTransferDetailsLazyQuery>;
export type NewTransferDetailsQueryResult = Apollo.QueryResult<NewTransferDetailsQuery, NewTransferDetailsQueryVariables>;
export const CancelTransferDetailsDocument = gql`
query CancelTransferDetails($proposalId: ID!) {
proposal(id: $proposalId) {
id
terms {
change {
... on CancelTransfer {
...CancelTransferFields
}
}
}
}
}
${CancelTransferFieldsFragmentDoc}`;
/**
* __useCancelTransferDetailsQuery__
*
* To run a query within a React component, call `useCancelTransferDetailsQuery` and pass it any options that fit your needs.
* When your component renders, `useCancelTransferDetailsQuery` 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 } = useCancelTransferDetailsQuery({
* variables: {
* proposalId: // value for 'proposalId'
* },
* });
*/
export function useCancelTransferDetailsQuery(baseOptions: Apollo.QueryHookOptions<CancelTransferDetailsQuery, CancelTransferDetailsQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<CancelTransferDetailsQuery, CancelTransferDetailsQueryVariables>(CancelTransferDetailsDocument, options);
}
export function useCancelTransferDetailsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<CancelTransferDetailsQuery, CancelTransferDetailsQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<CancelTransferDetailsQuery, CancelTransferDetailsQueryVariables>(CancelTransferDetailsDocument, options);
}
export type CancelTransferDetailsQueryHookResult = ReturnType<typeof useCancelTransferDetailsQuery>;
export type CancelTransferDetailsLazyQueryHookResult = ReturnType<typeof useCancelTransferDetailsLazyQuery>;
export type CancelTransferDetailsQueryResult = Apollo.QueryResult<CancelTransferDetailsQuery, CancelTransferDetailsQueryVariables>;

View File

@ -4,3 +4,5 @@ export * from './use-proposal-submit';
export * from './use-update-proposal';
export * from './use-update-network-paramaters-toasts';
export * from './use-successor-market-proposal-details';
export * from './use-new-transfer-proposal-details';
export * from './use-cancel-transfer-proposal-details';

View File

@ -0,0 +1,19 @@
import type { CancelTransferFieldsFragment } from '../proposals-data-provider';
import { useCancelTransferDetailsQuery } from './__generated__/Proposal';
export const useCancelTransferProposalDetails = (
proposalId?: string | null
) => {
const { data } = useCancelTransferDetailsQuery({
variables: {
proposalId: proposalId || '',
},
skip: !proposalId || proposalId.length === 0,
});
if (data?.proposal?.terms.change.__typename === 'CancelTransfer') {
return data?.proposal?.terms.change as CancelTransferFieldsFragment;
}
return undefined;
};

View File

@ -0,0 +1,17 @@
import type { NewTransferFieldsFragment } from '../proposals-data-provider';
import { useNewTransferDetailsQuery } from './__generated__/Proposal';
export const useNewTransferProposalDetails = (proposalId?: string | null) => {
const { data } = useNewTransferDetailsQuery({
variables: {
proposalId: proposalId || '',
},
skip: !proposalId || proposalId.length === 0,
});
if (data?.proposal?.terms.change.__typename === 'NewTransfer') {
return data?.proposal?.terms.change as NewTransferFieldsFragment;
}
return undefined;
};

View File

@ -1,4 +1,9 @@
import type { ConditionOperator, PeggedReference } from './__generated__/types';
import type {
ConditionOperator,
GovernanceTransferKind,
GovernanceTransferType,
PeggedReference,
} from './__generated__/types';
import type { AccountType } from './__generated__/types';
import type {
AuctionTrigger,
@ -519,6 +524,34 @@ export const DescriptionTransferTypeMapping: TransferTypeMap = {
TRANSFER_TYPE_SUCCESSOR_INSURANCE_FRACTION: 'Successor insurance fraction',
};
/**
* Governance transfers
*/
type GovernanceTransferTypeMap = {
[T in GovernanceTransferType]: string;
};
export const GovernanceTransferTypeMapping: GovernanceTransferTypeMap = {
GOVERNANCE_TRANSFER_TYPE_ALL_OR_NOTHING: 'All or nothing',
GOVERNANCE_TRANSFER_TYPE_BEST_EFFORT: 'Best effort',
GOVERNANCE_TRANSFER_TYPE_UNSPECIFIED: 'Unspecified',
};
export const DescriptionGovernanceTransferTypeMapping: GovernanceTransferTypeMap =
{
GOVERNANCE_TRANSFER_TYPE_ALL_OR_NOTHING:
'Transfers the specified amount or does not transfer anything',
GOVERNANCE_TRANSFER_TYPE_BEST_EFFORT:
'Transfers the specified amount or the max allowable amount if this is less than the specified amount',
GOVERNANCE_TRANSFER_TYPE_UNSPECIFIED: 'Default value, always invalid',
};
type GovernanceTransferKindMap = {
[T in NonNullable<GovernanceTransferKind['__typename']>]: string;
};
export const GovernanceTransferKindMapping: GovernanceTransferKindMap = {
OneOffGovernanceTransfer: 'One off',
RecurringGovernanceTransfer: 'Recurring',
};
type DispatchMetricLabel = {
[T in DispatchMetric]: string;
};