diff --git a/apps/governance/.env b/apps/governance/.env
index 2c6151228..a616f4faa 100644
--- a/apps/governance/.env
+++ b/apps/governance/.env
@@ -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
diff --git a/apps/governance/.env.stagnet1 b/apps/governance/.env.stagnet1
index 708b234db..589886a1d 100644
--- a/apps/governance/.env.stagnet1
+++ b/apps/governance/.env.stagnet1
@@ -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
diff --git a/apps/governance/src/i18n/translations/dev.json b/apps/governance/src/i18n/translations/dev.json
index 1d573d970..d64845619 100644
--- a/apps/governance/src/i18n/translations/dev.json
+++ b/apps/governance/src/i18n/translations/dev.json
@@ -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"
}
diff --git a/apps/governance/src/routes/proposals/components/proposal-detail-header/proposal-header.tsx b/apps/governance/src/routes/proposals/components/proposal-detail-header/proposal-header.tsx
index 1fb61ccbf..7de9d967f 100644
--- a/apps/governance/src/routes/proposals/components/proposal-detail-header/proposal-header.tsx
+++ b/apps/governance/src/routes/proposals/components/proposal-detail-header/proposal-header.tsx
@@ -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 ? (
+
+ ) : null;
+ break;
+ case 'CancelTransfer':
+ proposalType = 'CancelTransfer';
+ fallbackTitle = t('CancelTransferProposal');
+ details = FLAGS.GOVERNANCE_TRANSFERS ? (
+
+ ) : null;
+ break;
}
return (
@@ -224,3 +243,36 @@ const SuccessorCode = ({ proposalId }: { proposalId?: string | null }) => {
) : null;
};
+
+const NewTransferSummary = ({ proposalId }: { proposalId?: string | null }) => {
+ const { t } = useTranslation();
+ const details = useNewTransferProposalDetails(proposalId);
+
+ if (!details) return null;
+
+ return (
+
+ {GovernanceTransferKindMapping[details.kind.__typename]}{' '}
+ {t('transfer from')} {truncateMiddle(details.source)}{' '}
+ {t('to')} {truncateMiddle(details.destination)}
+
+ );
+};
+
+const CancelTransferSummary = ({
+ proposalId,
+}: {
+ proposalId?: string | null;
+}) => {
+ const { t } = useTranslation();
+ const details = useCancelTransferProposalDetails(proposalId);
+
+ if (!details) return null;
+
+ return (
+
+ {t('Cancel transfer: ')}{' '}
+ {truncateMiddle(details.transferId)}
+
+ );
+};
diff --git a/apps/governance/src/routes/proposals/components/proposal-transfer/index.ts b/apps/governance/src/routes/proposals/components/proposal-transfer/index.ts
new file mode 100644
index 000000000..8dce32b4e
--- /dev/null
+++ b/apps/governance/src/routes/proposals/components/proposal-transfer/index.ts
@@ -0,0 +1,2 @@
+export * from './proposal-transfer-details';
+export * from './proposal-cancel-transfer-details';
diff --git a/apps/governance/src/routes/proposals/components/proposal-transfer/proposal-cancel-transfer-details.tsx b/apps/governance/src/routes/proposals/components/proposal-transfer/proposal-cancel-transfer-details.tsx
new file mode 100644
index 000000000..6f51bd39b
--- /dev/null
+++ b/apps/governance/src/routes/proposals/components/proposal-transfer/proposal-cancel-transfer-details.tsx
@@ -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 (
+ <>
+
+
+
+
+ {t('transferId')}
+ {details.transferId}
+
+
+
+ >
+ );
+};
diff --git a/apps/governance/src/routes/proposals/components/proposal-transfer/proposal-transfer-details.tsx b/apps/governance/src/routes/proposals/components/proposal-transfer/proposal-transfer-details.tsx
new file mode 100644
index 000000000..59d8c5314
--- /dev/null
+++ b/apps/governance/src/routes/proposals/components/proposal-transfer/proposal-transfer-details.tsx
@@ -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 (
+ <>
+
+
+
+ {show && (
+
+
+ {/* The source account */}
+
+ {t('Source')}
+ {details.source}
+
+
+ {/* The type of source account */}
+
+ {t('Source Type')}
+ {AccountTypeMapping[details.sourceType]}
+
+
+ {/* The destination account */}
+
+ {t('Destination')}
+ {details.destination}
+
+
+ {/* The type of destination account */}
+
+ {t('Destination Type')}
+ {AccountTypeMapping[details.destinationType]}
+
+
+ {/* The asset to transfer */}
+
+ {t('Asset')}
+ {details.asset.symbol}
+
+
+ {/*The fraction of the balance to be transfer */}
+
+ {t('Fraction Of Balance')}
+ {`${Number(details.fraction_of_balance) * 100}%`}
+
+
+ {/* The maximum amount to be transferred */}
+
+ {t('Amount')}
+ {addDecimalsFormatNumberQuantum(
+ details.amount,
+ details.asset.decimals,
+ details.asset.quantum
+ )}
+
+
+ {/* The type of the governance transfer */}
+
+ {t('Transfer Type')}
+
+
+ {GovernanceTransferTypeMapping[details.transferType]}
+
+
+
+
+ {/* The type of governance transfer being made, i.e. a one-off or recurring trans */}
+
+ {t('Kind')}
+ {GovernanceTransferKindMapping[details.kind.__typename]}
+
+
+ {details.kind.__typename === 'OneOffGovernanceTransfer' &&
+ details.kind.deliverOn && (
+
+ {t('Deliver On')}
+ {formatDateWithLocalTimezone(
+ new Date(details.kind.deliverOn)
+ )}
+
+ )}
+
+ {details.kind.__typename === 'RecurringGovernanceTransfer' && (
+ <>
+
+ {t('Start On')}
+ {details.kind.startEpoch}
+
+ {details.kind.endEpoch && (
+
+ {t('End on')}
+ {details.kind.endEpoch}
+
+ )}
+ >
+ )}
+
+
+ )}
+ >
+ );
+};
diff --git a/apps/governance/src/routes/proposals/components/proposal/proposal.tsx b/apps/governance/src/routes/proposals/components/proposal/proposal.tsx
index 32df596c5..7b4c42763 100644
--- a/apps/governance/src/routes/proposals/components/proposal/proposal.tsx
+++ b/apps/governance/src/routes/proposals/components/proposal/proposal.tsx
@@ -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 */
+
@@ -187,6 +220,8 @@ export const Proposal = ({
)}
+ {governanceTransferDetails}
+
{
].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
diff --git a/libs/environment/src/hooks/use-environment.ts b/libs/environment/src/hooks/use-environment.ts
index 0c0b3689f..2c1ccd4e4 100644
--- a/libs/environment/src/hooks/use-environment.ts
+++ b/libs/environment/src/hooks/use-environment.ts
@@ -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(
diff --git a/libs/environment/src/types.ts b/libs/environment/src/types.ts
index fa5219c4a..0f8226222 100644
--- a/libs/environment/src/types.ts
+++ b/libs/environment/src/types.ts
@@ -25,6 +25,7 @@ export type CosmicElevatorFlags = Pick<
| 'METAMASK_SNAPS'
| 'REFERRALS'
| 'UPDATE_MARKET_STATE'
+ | 'GOVERNANCE_TRANSFERS'
>;
export type Configuration = z.infer;
export const CUSTOM_NODE_KEY = 'custom' as const;
diff --git a/libs/environment/src/utils/validate-environment.ts b/libs/environment/src/utils/validate-environment.ts
index 059d66156..609faba92 100644
--- a/libs/environment/src/utils/validate-environment.ts
+++ b/libs/environment/src/utils/validate-environment.ts
@@ -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 = {
diff --git a/libs/markets/src/lib/markets.mock.ts b/libs/markets/src/lib/markets.mock.ts
index 6c092b55b..18852d131 100644
--- a/libs/markets/src/lib/markets.mock.ts
+++ b/libs/markets/src/lib/markets.mock.ts
@@ -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',
diff --git a/libs/proposals/src/lib/proposals-data-provider/Proposals.graphql b/libs/proposals/src/lib/proposals-data-provider/Proposals.graphql
index 69a96a8cc..26a68252e 100644
--- a/libs/proposals/src/lib/proposals-data-provider/Proposals.graphql
+++ b/libs/proposals/src/lib/proposals-data-provider/Proposals.graphql
@@ -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 {
diff --git a/libs/proposals/src/lib/proposals-data-provider/__generated__/Proposals.ts b/libs/proposals/src/lib/proposals-data-provider/__generated__/Proposals.ts
index 238799d5d..4650f41ca 100644
--- a/libs/proposals/src/lib/proposals-data-provider/__generated__/Proposals.ts
+++ b/libs/proposals/src/lib/proposals-data-provider/__generated__/Proposals.ts
@@ -13,6 +13,10 @@ export type UpdateAssetFieldsFragment = { __typename?: 'UpdateAsset', assetId: s
export type UpdateNetworkParameterFieldsFragment = { __typename?: 'UpdateNetworkParameter', networkParameter: { __typename?: 'NetworkParameter', key: string, value: string } };
+export type NewTransferFieldsFragment = { __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 } };
+
+export type CancelTransferFieldsFragment = { __typename?: 'CancelTransfer', transferId: string };
+
export type ProposalListFieldsFragment = { __typename?: 'Proposal', id?: string | null, reference: string, state: Types.ProposalState, datetime: any, rejectionReason?: Types.ProposalRejectionReason | null, errorDetails?: string | null, requiredMajority: string, requiredParticipation: string, requiredLpMajority?: string | null, requiredLpParticipation?: string | null, rationale: { __typename?: 'ProposalRationale', title: string, description: string }, party: { __typename?: 'Party', id: string }, votes: { __typename?: 'ProposalVotes', yes: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalWeight: string }, no: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalWeight: string } }, terms: { __typename?: 'ProposalTerms', closingDatetime: any, enactmentDatetime?: any | null, change: { __typename: 'CancelTransfer' } | { __typename: 'NewAsset', name: string, symbol: string, decimals: number, quantum: string, source: { __typename?: 'BuiltinAsset', maxFaucetAmountMint: string } | { __typename?: 'ERC20', contractAddress: string, lifetimeLimit: string, withdrawThreshold: string } } | { __typename: 'NewFreeform' } | { __typename: 'NewMarket', decimalPlaces: number, metadata?: Array | null, instrument: { __typename?: 'InstrumentConfiguration', name: string, code: string, futureProduct?: { __typename?: 'FutureProduct', quoteName: string, settlementAsset: { __typename?: 'Asset', id: string, name: string, symbol: string, decimals: number, quantum: string }, dataSourceSpecForSettlementData: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType }, conditions?: Array<{ __typename?: 'Condition', operator: Types.ConditionOperator, value?: string | null }> | null }> | null } | { __typename?: 'EthCallSpec' } } | { __typename?: 'DataSourceDefinitionInternal' } }, dataSourceSpecForTradingTermination: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType }, conditions?: Array<{ __typename?: 'Condition', operator: Types.ConditionOperator, value?: string | null }> | null }> | null } | { __typename?: 'EthCallSpec' } } | { __typename?: 'DataSourceDefinitionInternal' } }, dataSourceSpecBinding: { __typename?: 'DataSourceSpecToFutureBinding', settlementDataProperty: string, tradingTerminationProperty: string } } | null }, riskParameters: { __typename?: 'LogNormalRiskModel', riskAversionParameter: number, tau: number, params: { __typename?: 'LogNormalModelParams', mu: number, r: number, sigma: number } } | { __typename?: 'SimpleRiskModel', params: { __typename?: 'SimpleRiskModelParams', factorLong: number, factorShort: number } }, successorConfiguration?: { __typename?: 'SuccessorConfiguration', parentMarketId: string } | null } | { __typename: 'NewSpotMarket' } | { __typename: 'NewTransfer' } | { __typename: 'UpdateAsset', assetId: string, quantum: string, source: { __typename?: 'UpdateERC20', lifetimeLimit: string, withdrawThreshold: string } } | { __typename: 'UpdateMarket', marketId: string, updateMarketConfiguration: { __typename?: 'UpdateMarketConfiguration', metadata?: Array | null, instrument: { __typename?: 'UpdateInstrumentConfiguration', code: string, product: { __typename?: 'UpdateFutureProduct', quoteName: string, dataSourceSpecForSettlementData: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType }, conditions?: Array<{ __typename?: 'Condition', operator: Types.ConditionOperator, value?: string | null }> | null }> | null } | { __typename?: 'EthCallSpec' } } | { __typename?: 'DataSourceDefinitionInternal' } }, dataSourceSpecForTradingTermination: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType }, conditions?: Array<{ __typename?: 'Condition', operator: Types.ConditionOperator, value?: string | null }> | null }> | null } | { __typename?: 'EthCallSpec' } } | { __typename?: 'DataSourceDefinitionInternal' } }, dataSourceSpecBinding: { __typename?: 'DataSourceSpecToFutureBinding', settlementDataProperty: string, tradingTerminationProperty: string } } | { __typename?: 'UpdatePerpetualProduct', quoteName: string, dataSourceSpecForSettlementData: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType }, conditions?: Array<{ __typename?: 'Condition', operator: Types.ConditionOperator, value?: string | null }> | null }> | null } | { __typename?: 'EthCallSpec' } } | { __typename?: 'DataSourceDefinitionInternal' } }, dataSourceSpecForSettlementSchedule: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType }, conditions?: Array<{ __typename?: 'Condition', operator: Types.ConditionOperator, value?: string | null }> | null }> | null } | { __typename?: 'EthCallSpec' } } | { __typename?: 'DataSourceDefinitionInternal' } }, dataSourceSpecBinding: { __typename?: 'DataSourceSpecPerpetualBinding', settlementDataProperty: string, settlementScheduleProperty: string } } }, priceMonitoringParameters: { __typename?: 'PriceMonitoringParameters', triggers?: Array<{ __typename?: 'PriceMonitoringTrigger', horizonSecs: number, probability: number, auctionExtensionSecs: number }> | null }, liquidityMonitoringParameters: { __typename?: 'LiquidityMonitoringParameters', triggeringRatio: string, targetStakeParameters: { __typename?: 'TargetStakeParameters', timeWindow: number, scalingFactor: number } }, riskParameters: { __typename: 'UpdateMarketLogNormalRiskModel', logNormal?: { __typename?: 'LogNormalRiskModel', riskAversionParameter: number, tau: number, params: { __typename?: 'LogNormalModelParams', mu: number, r: number, sigma: number } } | null } | { __typename: 'UpdateMarketSimpleRiskModel', simple?: { __typename?: 'SimpleRiskModelParams', factorLong: number, factorShort: number } | null } } } | { __typename: 'UpdateMarketState' } | { __typename: 'UpdateNetworkParameter', networkParameter: { __typename?: 'NetworkParameter', key: string, value: string } } | { __typename: 'UpdateReferralProgram' } | { __typename: 'UpdateSpotMarket' } | { __typename: 'UpdateVolumeDiscountProgram' } } };
export type ProposalsListQueryVariables = Types.Exact<{
@@ -32,6 +36,38 @@ export type SuccessorProposalsListQueryVariables = Types.Exact<{ [key: string]:
export type SuccessorProposalsListQuery = { __typename?: 'Query', proposalsConnection?: { __typename?: 'ProposalsConnection', edges?: Array<{ __typename?: 'ProposalEdge', node: { __typename?: 'Proposal', id?: string | null, terms: { __typename?: 'ProposalTerms', change: { __typename?: 'CancelTransfer' } | { __typename?: 'NewAsset' } | { __typename?: 'NewFreeform' } | { __typename?: 'NewMarket', instrument: { __typename?: 'InstrumentConfiguration', name: string }, successorConfiguration?: { __typename?: 'SuccessorConfiguration', parentMarketId: string } | null } | { __typename?: 'NewSpotMarket' } | { __typename?: 'NewTransfer' } | { __typename?: 'UpdateAsset' } | { __typename?: 'UpdateMarket' } | { __typename?: 'UpdateMarketState' } | { __typename?: 'UpdateNetworkParameter' } | { __typename?: 'UpdateReferralProgram' } | { __typename?: 'UpdateSpotMarket' } | { __typename?: 'UpdateVolumeDiscountProgram' } } } } | null> | null } | null };
+export const NewTransferFieldsFragmentDoc = gql`
+ 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
+ }
+ }
+}
+ `;
+export const CancelTransferFieldsFragmentDoc = gql`
+ fragment CancelTransferFields on CancelTransfer {
+ transferId
+}
+ `;
export const NewMarketFieldsFragmentDoc = gql`
fragment NewMarketFields on NewMarket {
instrument {
diff --git a/libs/proposals/src/lib/proposals-hooks/Proposal.graphql b/libs/proposals/src/lib/proposals-hooks/Proposal.graphql
index ca2158b94..6c93babae 100644
--- a/libs/proposals/src/lib/proposals-hooks/Proposal.graphql
+++ b/libs/proposals/src/lib/proposals-hooks/Proposal.graphql
@@ -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
+ }
+ }
+ }
+ }
+}
diff --git a/libs/proposals/src/lib/proposals-hooks/__generated__/Proposal.ts b/libs/proposals/src/lib/proposals-hooks/__generated__/Proposal.ts
index 1255b0db6..8252d5f5d 100644
--- a/libs/proposals/src/lib/proposals-hooks/__generated__/Proposal.ts
+++ b/libs/proposals/src/lib/proposals-hooks/__generated__/Proposal.ts
@@ -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;
export type InstrumentDetailsLazyQueryHookResult = ReturnType;
-export type InstrumentDetailsQueryResult = Apollo.QueryResult;
\ No newline at end of file
+export type InstrumentDetailsQueryResult = Apollo.QueryResult;
+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) {
+ const options = {...defaultOptions, ...baseOptions}
+ return Apollo.useQuery(NewTransferDetailsDocument, options);
+ }
+export function useNewTransferDetailsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions) {
+ const options = {...defaultOptions, ...baseOptions}
+ return Apollo.useLazyQuery(NewTransferDetailsDocument, options);
+ }
+export type NewTransferDetailsQueryHookResult = ReturnType;
+export type NewTransferDetailsLazyQueryHookResult = ReturnType;
+export type NewTransferDetailsQueryResult = Apollo.QueryResult;
+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) {
+ const options = {...defaultOptions, ...baseOptions}
+ return Apollo.useQuery(CancelTransferDetailsDocument, options);
+ }
+export function useCancelTransferDetailsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions) {
+ const options = {...defaultOptions, ...baseOptions}
+ return Apollo.useLazyQuery(CancelTransferDetailsDocument, options);
+ }
+export type CancelTransferDetailsQueryHookResult = ReturnType;
+export type CancelTransferDetailsLazyQueryHookResult = ReturnType;
+export type CancelTransferDetailsQueryResult = Apollo.QueryResult;
\ No newline at end of file
diff --git a/libs/proposals/src/lib/proposals-hooks/index.ts b/libs/proposals/src/lib/proposals-hooks/index.ts
index 744b0bc5f..45d0ad444 100644
--- a/libs/proposals/src/lib/proposals-hooks/index.ts
+++ b/libs/proposals/src/lib/proposals-hooks/index.ts
@@ -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';
diff --git a/libs/proposals/src/lib/proposals-hooks/use-cancel-transfer-proposal-details.ts b/libs/proposals/src/lib/proposals-hooks/use-cancel-transfer-proposal-details.ts
new file mode 100644
index 000000000..d3ecf5f5f
--- /dev/null
+++ b/libs/proposals/src/lib/proposals-hooks/use-cancel-transfer-proposal-details.ts
@@ -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;
+};
diff --git a/libs/proposals/src/lib/proposals-hooks/use-new-transfer-proposal-details.ts b/libs/proposals/src/lib/proposals-hooks/use-new-transfer-proposal-details.ts
new file mode 100644
index 000000000..70f1204bb
--- /dev/null
+++ b/libs/proposals/src/lib/proposals-hooks/use-new-transfer-proposal-details.ts
@@ -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;
+};
diff --git a/libs/types/src/global-types-mappings.ts b/libs/types/src/global-types-mappings.ts
index 3b4360e51..dd658d5f8 100644
--- a/libs/types/src/global-types-mappings.ts
+++ b/libs/types/src/global-types-mappings.ts
@@ -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]: string;
+};
+export const GovernanceTransferKindMapping: GovernanceTransferKindMap = {
+ OneOffGovernanceTransfer: 'One off',
+ RecurringGovernanceTransfer: 'Recurring',
+};
+
type DispatchMetricLabel = {
[T in DispatchMetric]: string;
};