diff --git a/apps/governance-e2e/.env b/apps/governance-e2e/.env index b61fc86228..bad2c3f30c 100644 --- a/apps/governance-e2e/.env +++ b/apps/governance-e2e/.env @@ -24,6 +24,7 @@ NX_SUCCESSOR_MARKETS=true NX_PRODUCT_PERPETUALS=true NX_REFERRALS=true NX_UPDATE_MARKET_STATE=true +NX_VOLUME_DISCOUNTS=true NX_VEGA_EXPLORER_URL=https://explorer.fairground.wtf NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn diff --git a/apps/governance-e2e/.env.devnet b/apps/governance-e2e/.env.devnet index b104124da3..9f282982b9 100644 --- a/apps/governance-e2e/.env.devnet +++ b/apps/governance-e2e/.env.devnet @@ -7,3 +7,4 @@ NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/m NX_PRODUCT_PERPETUALS=true NX_UPDATE_MARKET_STATE=true NX_REFERRALS=true +NX_VOLUME_DISCOUNTS=true diff --git a/apps/governance-e2e/.env.mainnet b/apps/governance-e2e/.env.mainnet index de9fecb201..23d3520517 100644 --- a/apps/governance-e2e/.env.mainnet +++ b/apps/governance-e2e/.env.mainnet @@ -7,3 +7,4 @@ NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/m NX_PRODUCT_PERPETUALS=false NX_UPDATE_MARKET_STATE=false NX_REFERRALS=false +NX_VOLUME_DISCOUNTS=false diff --git a/apps/governance-e2e/.env.testnet b/apps/governance-e2e/.env.testnet index ec606e1014..6c0a4a11ca 100644 --- a/apps/governance-e2e/.env.testnet +++ b/apps/governance-e2e/.env.testnet @@ -7,3 +7,4 @@ NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/m NX_PRODUCT_PERPETUALS=true NX_UPDATE_MARKET_STATE=true NX_REFERRALS=true +NX_VOLUME_DISCOUNTS=true diff --git a/apps/governance/.env b/apps/governance/.env index 4016db77b6..97fcb79ea4 100644 --- a/apps/governance/.env +++ b/apps/governance/.env @@ -37,3 +37,4 @@ NX_PRODUCT_PERPETUALS=false NX_UPDATE_MARKET_STATE=false NX_REFERRALS=false NX_GOVERNANCE_TRANSFERS=false +NX_VOLUME_DISCOUNTS=false diff --git a/apps/governance/.env.capsule b/apps/governance/.env.capsule index 5dd459a064..e70efca5ec 100644 --- a/apps/governance/.env.capsule +++ b/apps/governance/.env.capsule @@ -36,3 +36,4 @@ NX_METAMASK_SNAPS=false NX_PRODUCT_PERPETUALS=true NX_UPDATE_MARKET_STATE=true NX_REFERRALS=true +NX_VOLUME_DISCOUNTS=true diff --git a/apps/governance/.env.devnet b/apps/governance/.env.devnet index 5616a4821c..019dbda7d3 100644 --- a/apps/governance/.env.devnet +++ b/apps/governance/.env.devnet @@ -28,3 +28,4 @@ NX_METAMASK_SNAPS=true NX_PRODUCT_PERPETUALS=true NX_UPDATE_MARKET_STATE=true NX_REFERRALS=true +NX_VOLUME_DISCOUNTS=true diff --git a/apps/governance/.env.mainnet b/apps/governance/.env.mainnet index d06633a3c9..4afaabacd4 100644 --- a/apps/governance/.env.mainnet +++ b/apps/governance/.env.mainnet @@ -27,3 +27,4 @@ NX_METAMASK_SNAPS=false NX_PRODUCT_PERPETUALS=false NX_UPDATE_MARKET_STATE=false NX_REFERRALS=false +NX_VOLUME_DISCOUNTS=false diff --git a/apps/governance/.env.mainnet-mirror b/apps/governance/.env.mainnet-mirror index f09518a6d4..24af456f41 100644 --- a/apps/governance/.env.mainnet-mirror +++ b/apps/governance/.env.mainnet-mirror @@ -26,3 +26,4 @@ NX_METAMASK_SNAPS=false NX_PRODUCT_PERPETUALS=false NX_UPDATE_MARKET_STATE=false NX_REFERRALS=false +NX_VOLUME_DISCOUNTS=false diff --git a/apps/governance/.env.stagnet1 b/apps/governance/.env.stagnet1 index b919954478..a67956b478 100644 --- a/apps/governance/.env.stagnet1 +++ b/apps/governance/.env.stagnet1 @@ -25,3 +25,4 @@ NX_PRODUCT_PERPETUALS=true NX_UPDATE_MARKET_STATE=true NX_REFERRALS=true NX_GOVERNANCE_TRANSFERS=true +NX_VOLUME_DISCOUNTS=true diff --git a/apps/governance/.env.testnet b/apps/governance/.env.testnet index 305473b452..ac0d64db94 100644 --- a/apps/governance/.env.testnet +++ b/apps/governance/.env.testnet @@ -29,3 +29,4 @@ NX_METAMASK_SNAPS=true NX_PRODUCT_PERPETUALS=true NX_UPDATE_MARKET_STATE=true NX_REFERRALS=true +NX_VOLUME_DISCOUNTS=true diff --git a/apps/governance/.env.validators-testnet b/apps/governance/.env.validators-testnet index 693723078a..f8f57ed8ce 100644 --- a/apps/governance/.env.validators-testnet +++ b/apps/governance/.env.validators-testnet @@ -25,3 +25,4 @@ NX_METAMASK_SNAPS=false NX_PRODUCT_PERPETUALS=false NX_UPDATE_MARKET_STATE=false NX_REFERRALS=false +NX_VOLUME_DISCOUNTS=false diff --git a/apps/governance/src/i18n/translations/dev.json b/apps/governance/src/i18n/translations/dev.json index 2ce6aa073b..e2dfc5a796 100644 --- a/apps/governance/src/i18n/translations/dev.json +++ b/apps/governance/src/i18n/translations/dev.json @@ -710,6 +710,7 @@ "UpdateMarketProposal": "Update market proposal", "UpdateMarketStateProposal": "Update market state proposal", "UpdateReferralProgramProposal": "Update referral program proposal", + "UpdateVolumeDiscountProgramProposal": "Update volume discount program proposal", "MarketChange": "Market change", "MarketStateChange": "Market state change", "MarketDetails": "Market details", @@ -734,6 +735,7 @@ "UpdateMarket": "Update market", "UpdateMarketState": "Update market state", "UpdateReferralProgram": "Update referral program", + "UpdateVolumeDiscountProgram": "Update volume discount program", "NewAsset": "New asset", "UpdateAsset": "Update asset", "AssetID": "Asset ID", @@ -910,5 +912,7 @@ "WindowLength": "Window length", "WindowLengthDescription": "Number of epochs over which to evaluate a referral set's running volume", "EndOfProgramTimestamp": "End of program", - "EndOfProgramTimestampDescription": "Time after which when the current epoch ends, the programs will end and benefits will be disabled." + "EndOfProgramTimestampDescription": "Time after which when the current epoch ends, the programs will end and benefits will be disabled.", + "BenefitTierVolumeDiscountFactor": "Volume discount factor", + "BenefitTierVolumeDiscountFactorDescription": "Discount given to those in this benefit tier" } diff --git a/apps/governance/src/routes/home/index.tsx b/apps/governance/src/routes/home/index.tsx index 6ae4df1855..3977a57432 100644 --- a/apps/governance/src/routes/home/index.tsx +++ b/apps/governance/src/routes/home/index.tsx @@ -189,6 +189,7 @@ const GovernanceHome = ({ name }: RouteChildProps) => { includeNewMarketProductFields: !!FLAGS.PRODUCT_PERPETUALS, includeUpdateMarketStates: !!FLAGS.UPDATE_MARKET_STATE, includeUpdateReferralPrograms: !!FLAGS.REFERRALS, + includeUpdateVolumeDiscountPrograms: !!FLAGS.VOLUME_DISCOUNTS, }, }); 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 d8595b5f82..6ed92e8ce0 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 @@ -103,6 +103,11 @@ export const ProposalHeader = ({ fallbackTitle = t('UpdateReferralProgramProposal'); break; } + case 'UpdateVolumeDiscountProgram': { + proposalType = 'UpdateVolumeDiscountProgram'; + fallbackTitle = t('UpdateVolumeDiscountProgramProposal'); + break; + } case 'NewAsset': { proposalType = 'NewAsset'; fallbackTitle = t('NewAssetProposal'); diff --git a/apps/governance/src/routes/proposals/components/proposal-referral-program-details/proposal-referral-program-details.spec.tsx b/apps/governance/src/routes/proposals/components/proposal-referral-program-details/proposal-referral-program-details.spec.tsx index 838a8fd8c0..b9382a9c9c 100644 --- a/apps/governance/src/routes/proposals/components/proposal-referral-program-details/proposal-referral-program-details.spec.tsx +++ b/apps/governance/src/routes/proposals/components/proposal-referral-program-details/proposal-referral-program-details.spec.tsx @@ -1,6 +1,5 @@ import { render, screen } from '@testing-library/react'; import { - formatEndOfProgramTimestamp, formatMinimumRunningNotionalTakerVolume, formatReferralDiscountFactor, formatReferralRewardFactor, @@ -18,13 +17,16 @@ jest.mock('../../../../contexts/app-state/app-state-context', () => ({ }), })); -describe('ProposalReferralProgramDetails helper functions', () => { - it('should format end of program timestamp correctly', () => { - const input = '2023-01-01T12:00:00Z'; - const formatted = formatEndOfProgramTimestamp(input); - expect(formatted).toBe('01 January 2023 12:00 (GMT)'); - }); +beforeEach(() => { + jest.useFakeTimers(); + jest.setSystemTime(0); +}); +afterEach(() => { + jest.useRealTimers(); +}); + +describe('ProposalReferralProgramDetails helper functions', () => { it('should format minimum running notional taker volume correctly', () => { const input = '1000'; const formatted = formatMinimumRunningNotionalTakerVolume(input); diff --git a/apps/governance/src/routes/proposals/components/proposal-volume-discount-program-details/index.tsx b/apps/governance/src/routes/proposals/components/proposal-volume-discount-program-details/index.tsx new file mode 100644 index 0000000000..bb76a884b7 --- /dev/null +++ b/apps/governance/src/routes/proposals/components/proposal-volume-discount-program-details/index.tsx @@ -0,0 +1 @@ +export * from './proposal-volume-discount-program-details'; diff --git a/apps/governance/src/routes/proposals/components/proposal-volume-discount-program-details/proposal-volume-discount-program-details.spec.tsx b/apps/governance/src/routes/proposals/components/proposal-volume-discount-program-details/proposal-volume-discount-program-details.spec.tsx new file mode 100644 index 0000000000..57a8f0791d --- /dev/null +++ b/apps/governance/src/routes/proposals/components/proposal-volume-discount-program-details/proposal-volume-discount-program-details.spec.tsx @@ -0,0 +1,114 @@ +import { render, screen } from '@testing-library/react'; +import { ProposalVolumeDiscountProgramDetails } from './proposal-volume-discount-program-details'; +import { generateProposal } from '../../test-helpers/generate-proposals'; + +jest.mock('../../../../contexts/app-state/app-state-context', () => ({ + useAppState: () => ({ + appState: { + decimals: 2, + }, + }), +})); + +const mockReferralProposal = generateProposal({ + terms: { + change: { + __typename: 'UpdateVolumeDiscountProgram', + benefitTiers: [ + { + minimumRunningNotionalTakerVolume: '10000', + volumeDiscountFactor: '0.05', + }, + { + minimumRunningNotionalTakerVolume: '50000', + volumeDiscountFactor: '0.1', + }, + { + minimumRunningNotionalTakerVolume: '100000', + volumeDiscountFactor: '0.15', + }, + { + minimumRunningNotionalTakerVolume: '250000', + volumeDiscountFactor: '0.2', + }, + { + minimumRunningNotionalTakerVolume: '500000', + volumeDiscountFactor: '0.25', + }, + { + minimumRunningNotionalTakerVolume: '1000000', + volumeDiscountFactor: '0.3', + }, + { + minimumRunningNotionalTakerVolume: '1500000', + volumeDiscountFactor: '0.35', + }, + { + minimumRunningNotionalTakerVolume: '2000000', + volumeDiscountFactor: '0.4', + }, + ], + endOfProgramTimestamp: '1970-01-01T00:00:01.791568493Z', + windowLength: 7, + }, + }, +}); + +describe('ProposalVolumeDiscountProgramDetails', () => { + it('should not render if proposal is null', () => { + render(); + expect( + screen.queryByTestId('proposal-volume-discount-program-details') + ).toBeNull(); + }); + + it('should not render if __typename is not UpdateVolumeDiscountProgram', () => { + const updateMarketProposal = generateProposal({ + terms: { + change: { + __typename: 'UpdateMarket', + }, + }, + }); + render( + + ); + expect( + screen.queryByTestId('proposal-volume-discount-program-details') + ).toBeNull(); + }); + + it('should not render if there are no relevant fields', () => { + const incompleteProposal = generateProposal({ + terms: { + change: { + __typename: 'UpdateVolumeDiscountProgram', + }, + }, + }); + + render( + + ); + expect( + screen.queryByTestId('proposal-volume-discount-program-details') + ).toBeNull(); + }); + + it('should render relevant fields if present', () => { + render( + + ); + expect( + screen.getByTestId('proposal-volume-discount-program-window-length') + ).toBeInTheDocument(); + expect( + screen.getByTestId( + 'proposal-volume-discount-program-end-of-program-timestamp' + ) + ).toBeInTheDocument(); + expect( + screen.getByTestId('proposal-volume-discount-program-benefit-tiers') + ).toBeInTheDocument(); + }); +}); diff --git a/apps/governance/src/routes/proposals/components/proposal-volume-discount-program-details/proposal-volume-discount-program-details.tsx b/apps/governance/src/routes/proposals/components/proposal-volume-discount-program-details/proposal-volume-discount-program-details.tsx new file mode 100644 index 0000000000..48a72b1301 --- /dev/null +++ b/apps/governance/src/routes/proposals/components/proposal-volume-discount-program-details/proposal-volume-discount-program-details.tsx @@ -0,0 +1,130 @@ +import { useTranslation } from 'react-i18next'; +import type { ProposalQuery } from '../../proposal/__generated__/Proposal'; +import { + KeyValueTable, + KeyValueTableRow, + RoundedWrapper, + Tooltip, +} from '@vegaprotocol/ui-toolkit'; +import { + formatEndOfProgramTimestamp, + formatMinimumRunningNotionalTakerVolume, +} from '../proposal-referral-program-details'; +import { formatNumberPercentage } from '@vegaprotocol/utils'; +import BigNumber from 'bignumber.js'; + +interface ProposalReferralProgramDetailsProps { + proposal: ProposalQuery['proposal']; +} + +export const formatVolumeDiscountFactor = (value: string) => { + return formatNumberPercentage(new BigNumber(value).times(100)); +}; + +export const ProposalVolumeDiscountProgramDetails = ({ + proposal, +}: ProposalReferralProgramDetailsProps) => { + const { t } = useTranslation(); + if (proposal?.terms?.change?.__typename !== 'UpdateVolumeDiscountProgram') { + return null; + } + + const benefitTiers = proposal?.terms?.change?.benefitTiers; + const windowLength = proposal?.terms?.change?.windowLength; + const endOfProgramTimestamp = proposal?.terms?.change?.endOfProgramTimestamp; + + if (!benefitTiers && !windowLength && !endOfProgramTimestamp) { + return null; + } + + return ( +
+ + {windowLength && ( +
+ + + + {t('WindowLength')} + + {windowLength} + + +
+ )} + + {endOfProgramTimestamp && ( +
+ + + + {t('EndOfProgramTimestamp')} + + {formatEndOfProgramTimestamp(endOfProgramTimestamp)} + + +
+ )} + + {benefitTiers && ( +
+

+ {t('BenefitTiers')} +

+ + {benefitTiers + .sort( + (a, b) => + Number(a.minimumRunningNotionalTakerVolume) - + Number(b.minimumRunningNotionalTakerVolume) + ) + .map((benefitTier, index) => ( +
+

+ Tier {index + 1} +

+ {benefitTier.minimumRunningNotionalTakerVolume && ( + + + + {t('BenefitTierMinimumRunningNotionalTakerVolume')} + + + {formatMinimumRunningNotionalTakerVolume( + benefitTier.minimumRunningNotionalTakerVolume + )} + + )} + {benefitTier.volumeDiscountFactor && ( + + + {t('BenefitTierVolumeDiscountFactor')} + + {formatVolumeDiscountFactor( + benefitTier.volumeDiscountFactor + )} + + )} +
+ ))} +
+
+ )} +
+
+ ); +}; diff --git a/apps/governance/src/routes/proposals/components/proposal/proposal.tsx b/apps/governance/src/routes/proposals/components/proposal/proposal.tsx index 4a18ca2207..e4347fcfaa 100644 --- a/apps/governance/src/routes/proposals/components/proposal/proposal.tsx +++ b/apps/governance/src/routes/proposals/components/proposal/proposal.tsx @@ -7,6 +7,7 @@ import { ProposalChangeTable } from '../proposal-change-table'; import { ProposalJson } from '../proposal-json'; import { ProposalAssetDetails } from '../proposal-asset-details'; import { ProposalReferralProgramDetails } from '../proposal-referral-program-details'; +import { ProposalVolumeDiscountProgramDetails } from '../proposal-volume-discount-program-details'; import { UserVote } from '../vote-details'; import { ListAsset } from '../list-asset'; import Routes from '../../../routes'; @@ -119,6 +120,10 @@ export const Proposal = ({ minVoterBalance = networkParams.governance_proposal_referralProgram_minVoterBalance; break; + case 'UpdateVolumeDiscountProgram': + minVoterBalance = + networkParams.governance_proposal_VolumeDiscountProgram_minVoterBalance; + break; } } @@ -232,6 +237,12 @@ export const Proposal = ({ )} + {proposal.terms.change.__typename === 'UpdateVolumeDiscountProgram' && ( +
+ +
+ )} + {governanceTransferDetails}
diff --git a/apps/governance/src/routes/proposals/hooks/use-proposal-network-params.ts b/apps/governance/src/routes/proposals/hooks/use-proposal-network-params.ts index 832a5ef013..ba559cdb51 100644 --- a/apps/governance/src/routes/proposals/hooks/use-proposal-network-params.ts +++ b/apps/governance/src/routes/proposals/hooks/use-proposal-network-params.ts @@ -28,6 +28,8 @@ export const useProposalNetworkParams = ({ NetworkParams.governance_proposal_updateNetParam_requiredParticipation, NetworkParams.governance_proposal_freeform_requiredMajority, NetworkParams.governance_proposal_freeform_requiredParticipation, + NetworkParams.governance_proposal_VolumeDiscountProgram_requiredMajority, + NetworkParams.governance_proposal_VolumeDiscountProgram_requiredParticipation, ]); const fallback = { @@ -101,6 +103,14 @@ export const useProposalNetworkParams = ({ params.governance_proposal_referralProgram_requiredParticipation ), }; + case 'UpdateVolumeDiscountProgram': + return { + requiredMajority: + params.governance_proposal_VolumeDiscountProgram_requiredMajority, + requiredParticipation: new BigNumber( + params.governance_proposal_VolumeDiscountProgram_requiredParticipation + ), + }; default: return fallback; } diff --git a/apps/governance/src/routes/proposals/proposal/Proposal.graphql b/apps/governance/src/routes/proposals/proposal/Proposal.graphql index 50739ecd86..3ff49833c0 100644 --- a/apps/governance/src/routes/proposals/proposal/Proposal.graphql +++ b/apps/governance/src/routes/proposals/proposal/Proposal.graphql @@ -66,11 +66,27 @@ fragment UpdateReferralProgram on Proposal { } } +fragment UpdateVolumeDiscountProgram on Proposal { + terms { + change { + ... on UpdateVolumeDiscountProgram { + benefitTiers { + minimumRunningNotionalTakerVolume + volumeDiscountFactor + } + endOfProgramTimestamp + windowLength + } + } + } +} + query Proposal( $proposalId: ID! $includeNewMarketProductField: Boolean! $includeUpdateMarketState: Boolean! $includeUpdateReferralProgram: Boolean! + $includeUpdateVolumeDiscountProgram: Boolean! ) { proposal(id: $proposalId) { id @@ -89,6 +105,8 @@ query Proposal( ...NewMarketProductField @include(if: $includeNewMarketProductField) ...UpdateMarketState @include(if: $includeUpdateMarketState) ...UpdateReferralProgram @include(if: $includeUpdateReferralProgram) + ...UpdateVolumeDiscountProgram + @include(if: $includeUpdateVolumeDiscountProgram) terms { closingDatetime enactmentDatetime diff --git a/apps/governance/src/routes/proposals/proposal/__generated__/Proposal.ts b/apps/governance/src/routes/proposals/proposal/__generated__/Proposal.ts index 516d879723..ed05e20558 100644 --- a/apps/governance/src/routes/proposals/proposal/__generated__/Proposal.ts +++ b/apps/governance/src/routes/proposals/proposal/__generated__/Proposal.ts @@ -9,15 +9,18 @@ export type UpdateMarketStateFragment = { __typename?: 'Proposal', terms: { __ty export type UpdateReferralProgramFragment = { __typename?: 'Proposal', terms: { __typename?: 'ProposalTerms', change: { __typename?: 'CancelTransfer' } | { __typename?: 'NewAsset' } | { __typename?: 'NewFreeform' } | { __typename?: 'NewMarket' } | { __typename?: 'NewSpotMarket' } | { __typename?: 'NewTransfer' } | { __typename?: 'UpdateAsset' } | { __typename?: 'UpdateMarket' } | { __typename?: 'UpdateMarketState' } | { __typename?: 'UpdateNetworkParameter' } | { __typename?: 'UpdateReferralProgram', changes: { __typename?: 'ReferralProgram', endOfProgramTimestamp: string, windowLength: number, benefitTiers: Array<{ __typename?: 'BenefitTier', minimumEpochs: number, minimumRunningNotionalTakerVolume: string, referralDiscountFactor: string, referralRewardFactor: string }>, stakingTiers: Array<{ __typename?: 'StakingTier', minimumStakedTokens: string, referralRewardMultiplier: string }> } } | { __typename?: 'UpdateSpotMarket' } | { __typename?: 'UpdateVolumeDiscountProgram' } } }; +export type UpdateVolumeDiscountProgramFragment = { __typename?: 'Proposal', terms: { __typename?: 'ProposalTerms', change: { __typename?: 'CancelTransfer' } | { __typename?: 'NewAsset' } | { __typename?: 'NewFreeform' } | { __typename?: 'NewMarket' } | { __typename?: 'NewSpotMarket' } | { __typename?: 'NewTransfer' } | { __typename?: 'UpdateAsset' } | { __typename?: 'UpdateMarket' } | { __typename?: 'UpdateMarketState' } | { __typename?: 'UpdateNetworkParameter' } | { __typename?: 'UpdateReferralProgram' } | { __typename?: 'UpdateSpotMarket' } | { __typename?: 'UpdateVolumeDiscountProgram', endOfProgramTimestamp: any, windowLength: number, benefitTiers: Array<{ __typename?: 'VolumeBenefitTier', minimumRunningNotionalTakerVolume: string, volumeDiscountFactor: string }> } } }; + export type ProposalQueryVariables = Types.Exact<{ proposalId: Types.Scalars['ID']; includeNewMarketProductField: Types.Scalars['Boolean']; includeUpdateMarketState: Types.Scalars['Boolean']; includeUpdateReferralProgram: Types.Scalars['Boolean']; + includeUpdateVolumeDiscountProgram: Types.Scalars['Boolean']; }>; -export type ProposalQuery = { __typename?: 'Query', proposal?: { __typename?: 'Proposal', id?: string | null, reference: string, state: Types.ProposalState, datetime: any, rejectionReason?: Types.ProposalRejectionReason | null, errorDetails?: string | null, rationale: { __typename?: 'ProposalRationale', title: string, description: string }, party: { __typename?: 'Party', id: 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, positionDecimalPlaces: number, linearSlippageFactor: string, quadraticSlippageFactor: string, 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 } }, 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', sourceType: { __typename?: 'DataSourceSpecConfigurationTime', conditions: Array<{ __typename?: 'Condition', operator: Types.ConditionOperator, value?: string | null } | null> } | { __typename?: 'DataSourceSpecConfigurationTimeTrigger' } } }, dataSourceSpecBinding: { __typename?: 'DataSourceSpecToFutureBinding', settlementDataProperty: string, tradingTerminationProperty: string } } | null, product?: { __typename: 'FutureProduct' } | { __typename: 'PerpetualProduct' } | { __typename: 'SpotProduct' } | null }, 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 } } } | { __typename?: 'NewSpotMarket' } | { __typename?: 'NewTransfer' } | { __typename?: 'UpdateAsset', quantum: string, assetId: 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', sourceType: { __typename?: 'DataSourceSpecConfigurationTime', conditions: Array<{ __typename?: 'Condition', operator: Types.ConditionOperator, value?: string | null } | null> } | { __typename?: 'DataSourceSpecConfigurationTimeTrigger' } } }, 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', sourceType: { __typename?: 'DataSourceSpecConfigurationTime', conditions: Array<{ __typename?: 'Condition', operator: Types.ConditionOperator, value?: string | null } | null> } | { __typename?: 'DataSourceSpecConfigurationTimeTrigger' } } }, 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', r: number, sigma: number, mu: number } } | null } | { __typename?: 'UpdateMarketSimpleRiskModel', simple?: { __typename?: 'SimpleRiskModelParams', factorLong: number, factorShort: number } | null } } } | { __typename?: 'UpdateMarketState', updateType: Types.MarketUpdateType, price?: string | null, market: { __typename?: 'Market', decimalPlaces: number, id: string, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', name: string, code: string, product: { __typename: 'Future', quoteName: string } | { __typename: 'Perpetual', quoteName: string } | { __typename: 'Spot' } } } } } | { __typename?: 'UpdateNetworkParameter', networkParameter: { __typename?: 'NetworkParameter', key: string, value: string } } | { __typename?: 'UpdateReferralProgram', changes: { __typename?: 'ReferralProgram', endOfProgramTimestamp: string, windowLength: number, benefitTiers: Array<{ __typename?: 'BenefitTier', minimumEpochs: number, minimumRunningNotionalTakerVolume: string, referralDiscountFactor: string, referralRewardFactor: string }>, stakingTiers: Array<{ __typename?: 'StakingTier', minimumStakedTokens: string, referralRewardMultiplier: string }> } } | { __typename?: 'UpdateSpotMarket' } | { __typename?: 'UpdateVolumeDiscountProgram' } }, votes: { __typename?: 'ProposalVotes', yes: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalEquityLikeShareWeight: string }, no: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalEquityLikeShareWeight: string } } } | null }; +export type ProposalQuery = { __typename?: 'Query', proposal?: { __typename?: 'Proposal', id?: string | null, reference: string, state: Types.ProposalState, datetime: any, rejectionReason?: Types.ProposalRejectionReason | null, errorDetails?: string | null, rationale: { __typename?: 'ProposalRationale', title: string, description: string }, party: { __typename?: 'Party', id: 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, positionDecimalPlaces: number, linearSlippageFactor: string, quadraticSlippageFactor: string, 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 } }, 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', sourceType: { __typename?: 'DataSourceSpecConfigurationTime', conditions: Array<{ __typename?: 'Condition', operator: Types.ConditionOperator, value?: string | null } | null> } | { __typename?: 'DataSourceSpecConfigurationTimeTrigger' } } }, dataSourceSpecBinding: { __typename?: 'DataSourceSpecToFutureBinding', settlementDataProperty: string, tradingTerminationProperty: string } } | null, product?: { __typename: 'FutureProduct' } | { __typename: 'PerpetualProduct' } | { __typename: 'SpotProduct' } | null }, 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 } } } | { __typename?: 'NewSpotMarket' } | { __typename?: 'NewTransfer' } | { __typename?: 'UpdateAsset', quantum: string, assetId: 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', sourceType: { __typename?: 'DataSourceSpecConfigurationTime', conditions: Array<{ __typename?: 'Condition', operator: Types.ConditionOperator, value?: string | null } | null> } | { __typename?: 'DataSourceSpecConfigurationTimeTrigger' } } }, 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', sourceType: { __typename?: 'DataSourceSpecConfigurationTime', conditions: Array<{ __typename?: 'Condition', operator: Types.ConditionOperator, value?: string | null } | null> } | { __typename?: 'DataSourceSpecConfigurationTimeTrigger' } } }, 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', r: number, sigma: number, mu: number } } | null } | { __typename?: 'UpdateMarketSimpleRiskModel', simple?: { __typename?: 'SimpleRiskModelParams', factorLong: number, factorShort: number } | null } } } | { __typename?: 'UpdateMarketState', updateType: Types.MarketUpdateType, price?: string | null, market: { __typename?: 'Market', decimalPlaces: number, id: string, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', name: string, code: string, product: { __typename: 'Future', quoteName: string } | { __typename: 'Perpetual', quoteName: string } | { __typename: 'Spot' } } } } } | { __typename?: 'UpdateNetworkParameter', networkParameter: { __typename?: 'NetworkParameter', key: string, value: string } } | { __typename?: 'UpdateReferralProgram', changes: { __typename?: 'ReferralProgram', endOfProgramTimestamp: string, windowLength: number, benefitTiers: Array<{ __typename?: 'BenefitTier', minimumEpochs: number, minimumRunningNotionalTakerVolume: string, referralDiscountFactor: string, referralRewardFactor: string }>, stakingTiers: Array<{ __typename?: 'StakingTier', minimumStakedTokens: string, referralRewardMultiplier: string }> } } | { __typename?: 'UpdateSpotMarket' } | { __typename?: 'UpdateVolumeDiscountProgram', endOfProgramTimestamp: any, windowLength: number, benefitTiers: Array<{ __typename?: 'VolumeBenefitTier', minimumRunningNotionalTakerVolume: string, volumeDiscountFactor: string }> } }, votes: { __typename?: 'ProposalVotes', yes: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalEquityLikeShareWeight: string }, no: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalEquityLikeShareWeight: string } } } | null }; export const NewMarketProductFieldFragmentDoc = gql` fragment NewMarketProductField on Proposal { @@ -90,8 +93,24 @@ export const UpdateReferralProgramFragmentDoc = gql` } } `; +export const UpdateVolumeDiscountProgramFragmentDoc = gql` + fragment UpdateVolumeDiscountProgram on Proposal { + terms { + change { + ... on UpdateVolumeDiscountProgram { + benefitTiers { + minimumRunningNotionalTakerVolume + volumeDiscountFactor + } + endOfProgramTimestamp + windowLength + } + } + } +} + `; export const ProposalDocument = gql` - query Proposal($proposalId: ID!, $includeNewMarketProductField: Boolean!, $includeUpdateMarketState: Boolean!, $includeUpdateReferralProgram: Boolean!) { + query Proposal($proposalId: ID!, $includeNewMarketProductField: Boolean!, $includeUpdateMarketState: Boolean!, $includeUpdateReferralProgram: Boolean!, $includeUpdateVolumeDiscountProgram: Boolean!) { proposal(id: $proposalId) { id rationale { @@ -109,6 +128,7 @@ export const ProposalDocument = gql` ...NewMarketProductField @include(if: $includeNewMarketProductField) ...UpdateMarketState @include(if: $includeUpdateMarketState) ...UpdateReferralProgram @include(if: $includeUpdateReferralProgram) + ...UpdateVolumeDiscountProgram @include(if: $includeUpdateVolumeDiscountProgram) terms { closingDatetime enactmentDatetime @@ -397,7 +417,8 @@ export const ProposalDocument = gql` } ${NewMarketProductFieldFragmentDoc} ${UpdateMarketStateFragmentDoc} -${UpdateReferralProgramFragmentDoc}`; +${UpdateReferralProgramFragmentDoc} +${UpdateVolumeDiscountProgramFragmentDoc}`; /** * __useProposalQuery__ @@ -415,6 +436,7 @@ ${UpdateReferralProgramFragmentDoc}`; * includeNewMarketProductField: // value for 'includeNewMarketProductField' * includeUpdateMarketState: // value for 'includeUpdateMarketState' * includeUpdateReferralProgram: // value for 'includeUpdateReferralProgram' + * includeUpdateVolumeDiscountProgram: // value for 'includeUpdateVolumeDiscountProgram' * }, * }); */ diff --git a/apps/governance/src/routes/proposals/proposal/proposal-container.tsx b/apps/governance/src/routes/proposals/proposal/proposal-container.tsx index 01f4c350b8..5f3ff62d04 100644 --- a/apps/governance/src/routes/proposals/proposal/proposal-container.tsx +++ b/apps/governance/src/routes/proposals/proposal/proposal-container.tsx @@ -37,6 +37,7 @@ export const ProposalContainer = () => { NetworkParams.governance_proposal_updateNetParam_minVoterBalance, NetworkParams.governance_proposal_freeform_minVoterBalance, NetworkParams.governance_proposal_referralProgram_minVoterBalance, + NetworkParams.governance_proposal_VolumeDiscountProgram_minVoterBalance, NetworkParams.spam_protection_voting_min_tokens, NetworkParams.governance_proposal_market_requiredMajority, NetworkParams.governance_proposal_updateMarket_requiredMajority, @@ -46,6 +47,7 @@ export const ProposalContainer = () => { NetworkParams.governance_proposal_updateNetParam_requiredMajority, NetworkParams.governance_proposal_freeform_requiredMajority, NetworkParams.governance_proposal_referralProgram_requiredMajority, + NetworkParams.governance_proposal_VolumeDiscountProgram_requiredMajority, ]); const { @@ -60,6 +62,7 @@ export const ProposalContainer = () => { includeNewMarketProductField: !!FLAGS.PRODUCT_PERPETUALS, includeUpdateMarketState: !!FLAGS.UPDATE_MARKET_STATE, includeUpdateReferralProgram: !!FLAGS.REFERRALS, + includeUpdateVolumeDiscountProgram: !!FLAGS.VOLUME_DISCOUNTS, }, skip: !params.proposalId, }); diff --git a/apps/governance/src/routes/proposals/proposals/Proposals.graphql b/apps/governance/src/routes/proposals/proposals/Proposals.graphql index 7029735ce5..24cb96a85a 100644 --- a/apps/governance/src/routes/proposals/proposals/Proposals.graphql +++ b/apps/governance/src/routes/proposals/proposals/Proposals.graphql @@ -66,6 +66,21 @@ fragment UpdateReferralPrograms on Proposal { } } +fragment UpdateVolumeDiscountPrograms on Proposal { + terms { + change { + ... on UpdateVolumeDiscountProgram { + benefitTiers { + minimumRunningNotionalTakerVolume + volumeDiscountFactor + } + endOfProgramTimestamp + windowLength + } + } + } +} + fragment ProposalFields on Proposal { id rationale { @@ -151,6 +166,7 @@ query Proposals( $includeNewMarketProductFields: Boolean! $includeUpdateMarketStates: Boolean! $includeUpdateReferralPrograms: Boolean! + $includeUpdateVolumeDiscountPrograms: Boolean! ) { proposalsConnection { edges { @@ -159,6 +175,8 @@ query Proposals( ...NewMarketProductFields @include(if: $includeNewMarketProductFields) ...UpdateMarketStates @include(if: $includeUpdateMarketStates) ...UpdateReferralPrograms @include(if: $includeUpdateReferralPrograms) + ...UpdateVolumeDiscountPrograms + @include(if: $includeUpdateVolumeDiscountPrograms) } } } diff --git a/apps/governance/src/routes/proposals/proposals/__generated__/Proposals.ts b/apps/governance/src/routes/proposals/proposals/__generated__/Proposals.ts index 858cd49848..803bc24deb 100644 --- a/apps/governance/src/routes/proposals/proposals/__generated__/Proposals.ts +++ b/apps/governance/src/routes/proposals/proposals/__generated__/Proposals.ts @@ -9,16 +9,19 @@ export type UpdateMarketStatesFragment = { __typename?: 'Proposal', terms: { __t export type UpdateReferralProgramsFragment = { __typename?: 'Proposal', terms: { __typename?: 'ProposalTerms', change: { __typename?: 'CancelTransfer' } | { __typename?: 'NewAsset' } | { __typename?: 'NewFreeform' } | { __typename?: 'NewMarket' } | { __typename?: 'NewSpotMarket' } | { __typename?: 'NewTransfer' } | { __typename?: 'UpdateAsset' } | { __typename?: 'UpdateMarket' } | { __typename?: 'UpdateMarketState' } | { __typename?: 'UpdateNetworkParameter' } | { __typename?: 'UpdateReferralProgram', changes: { __typename?: 'ReferralProgram', endOfProgramTimestamp: string, windowLength: number, benefitTiers: Array<{ __typename?: 'BenefitTier', minimumEpochs: number, minimumRunningNotionalTakerVolume: string, referralDiscountFactor: string, referralRewardFactor: string }>, stakingTiers: Array<{ __typename?: 'StakingTier', minimumStakedTokens: string, referralRewardMultiplier: string }> } } | { __typename?: 'UpdateSpotMarket' } | { __typename?: 'UpdateVolumeDiscountProgram' } } }; +export type UpdateVolumeDiscountProgramsFragment = { __typename?: 'Proposal', terms: { __typename?: 'ProposalTerms', change: { __typename?: 'CancelTransfer' } | { __typename?: 'NewAsset' } | { __typename?: 'NewFreeform' } | { __typename?: 'NewMarket' } | { __typename?: 'NewSpotMarket' } | { __typename?: 'NewTransfer' } | { __typename?: 'UpdateAsset' } | { __typename?: 'UpdateMarket' } | { __typename?: 'UpdateMarketState' } | { __typename?: 'UpdateNetworkParameter' } | { __typename?: 'UpdateReferralProgram' } | { __typename?: 'UpdateSpotMarket' } | { __typename?: 'UpdateVolumeDiscountProgram', endOfProgramTimestamp: any, windowLength: number, benefitTiers: Array<{ __typename?: 'VolumeBenefitTier', minimumRunningNotionalTakerVolume: string, volumeDiscountFactor: string }> } } }; + export type ProposalFieldsFragment = { __typename?: 'Proposal', id?: string | null, reference: string, state: Types.ProposalState, datetime: any, rejectionReason?: Types.ProposalRejectionReason | null, errorDetails?: string | null, rationale: { __typename?: 'ProposalRationale', title: string, description: string }, party: { __typename?: 'Party', id: 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, withdrawThreshold: string, lifetimeLimit: string } } | { __typename?: 'NewFreeform' } | { __typename?: 'NewMarket', instrument: { __typename?: 'InstrumentConfiguration', name: string, code: string, futureProduct?: { __typename?: 'FutureProduct', settlementAsset: { __typename?: 'Asset', symbol: string } } | null } } | { __typename?: 'NewSpotMarket' } | { __typename?: 'NewTransfer' } | { __typename?: 'UpdateAsset', quantum: string, assetId: string, source: { __typename?: 'UpdateERC20', lifetimeLimit: string, withdrawThreshold: string } } | { __typename?: 'UpdateMarket', marketId: string } | { __typename?: 'UpdateMarketState' } | { __typename?: 'UpdateNetworkParameter', networkParameter: { __typename?: 'NetworkParameter', key: string, value: string } } | { __typename?: 'UpdateReferralProgram' } | { __typename?: 'UpdateSpotMarket' } | { __typename?: 'UpdateVolumeDiscountProgram' } }, votes: { __typename?: 'ProposalVotes', yes: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalEquityLikeShareWeight: string }, no: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalEquityLikeShareWeight: string } } }; export type ProposalsQueryVariables = Types.Exact<{ includeNewMarketProductFields: Types.Scalars['Boolean']; includeUpdateMarketStates: Types.Scalars['Boolean']; includeUpdateReferralPrograms: Types.Scalars['Boolean']; + includeUpdateVolumeDiscountPrograms: Types.Scalars['Boolean']; }>; -export type ProposalsQuery = { __typename?: 'Query', proposalsConnection?: { __typename?: 'ProposalsConnection', edges?: Array<{ __typename?: 'ProposalEdge', node: { __typename?: 'Proposal', id?: string | null, reference: string, state: Types.ProposalState, datetime: any, rejectionReason?: Types.ProposalRejectionReason | null, errorDetails?: string | null, rationale: { __typename?: 'ProposalRationale', title: string, description: string }, party: { __typename?: 'Party', id: 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, withdrawThreshold: string, lifetimeLimit: string } } | { __typename?: 'NewFreeform' } | { __typename?: 'NewMarket', instrument: { __typename?: 'InstrumentConfiguration', name: string, code: string, futureProduct?: { __typename?: 'FutureProduct', settlementAsset: { __typename?: 'Asset', symbol: string } } | null, product?: { __typename: 'FutureProduct' } | { __typename: 'PerpetualProduct' } | { __typename: 'SpotProduct' } | null } } | { __typename?: 'NewSpotMarket' } | { __typename?: 'NewTransfer' } | { __typename?: 'UpdateAsset', quantum: string, assetId: string, source: { __typename?: 'UpdateERC20', lifetimeLimit: string, withdrawThreshold: string } } | { __typename?: 'UpdateMarket', marketId: string } | { __typename?: 'UpdateMarketState', updateType: Types.MarketUpdateType, price?: string | null, market: { __typename?: 'Market', decimalPlaces: number, id: string, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', name: string, code: string, product: { __typename: 'Future', quoteName: string } | { __typename: 'Perpetual', quoteName: string } | { __typename: 'Spot' } } } } } | { __typename?: 'UpdateNetworkParameter', networkParameter: { __typename?: 'NetworkParameter', key: string, value: string } } | { __typename?: 'UpdateReferralProgram', changes: { __typename?: 'ReferralProgram', endOfProgramTimestamp: string, windowLength: number, benefitTiers: Array<{ __typename?: 'BenefitTier', minimumEpochs: number, minimumRunningNotionalTakerVolume: string, referralDiscountFactor: string, referralRewardFactor: string }>, stakingTiers: Array<{ __typename?: 'StakingTier', minimumStakedTokens: string, referralRewardMultiplier: string }> } } | { __typename?: 'UpdateSpotMarket' } | { __typename?: 'UpdateVolumeDiscountProgram' } }, votes: { __typename?: 'ProposalVotes', yes: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalEquityLikeShareWeight: string }, no: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalEquityLikeShareWeight: string } } } } | null> | null } | null }; +export type ProposalsQuery = { __typename?: 'Query', proposalsConnection?: { __typename?: 'ProposalsConnection', edges?: Array<{ __typename?: 'ProposalEdge', node: { __typename?: 'Proposal', id?: string | null, reference: string, state: Types.ProposalState, datetime: any, rejectionReason?: Types.ProposalRejectionReason | null, errorDetails?: string | null, rationale: { __typename?: 'ProposalRationale', title: string, description: string }, party: { __typename?: 'Party', id: 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, withdrawThreshold: string, lifetimeLimit: string } } | { __typename?: 'NewFreeform' } | { __typename?: 'NewMarket', instrument: { __typename?: 'InstrumentConfiguration', name: string, code: string, futureProduct?: { __typename?: 'FutureProduct', settlementAsset: { __typename?: 'Asset', symbol: string } } | null, product?: { __typename: 'FutureProduct' } | { __typename: 'PerpetualProduct' } | { __typename: 'SpotProduct' } | null } } | { __typename?: 'NewSpotMarket' } | { __typename?: 'NewTransfer' } | { __typename?: 'UpdateAsset', quantum: string, assetId: string, source: { __typename?: 'UpdateERC20', lifetimeLimit: string, withdrawThreshold: string } } | { __typename?: 'UpdateMarket', marketId: string } | { __typename?: 'UpdateMarketState', updateType: Types.MarketUpdateType, price?: string | null, market: { __typename?: 'Market', decimalPlaces: number, id: string, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', name: string, code: string, product: { __typename: 'Future', quoteName: string } | { __typename: 'Perpetual', quoteName: string } | { __typename: 'Spot' } } } } } | { __typename?: 'UpdateNetworkParameter', networkParameter: { __typename?: 'NetworkParameter', key: string, value: string } } | { __typename?: 'UpdateReferralProgram', changes: { __typename?: 'ReferralProgram', endOfProgramTimestamp: string, windowLength: number, benefitTiers: Array<{ __typename?: 'BenefitTier', minimumEpochs: number, minimumRunningNotionalTakerVolume: string, referralDiscountFactor: string, referralRewardFactor: string }>, stakingTiers: Array<{ __typename?: 'StakingTier', minimumStakedTokens: string, referralRewardMultiplier: string }> } } | { __typename?: 'UpdateSpotMarket' } | { __typename?: 'UpdateVolumeDiscountProgram', endOfProgramTimestamp: any, windowLength: number, benefitTiers: Array<{ __typename?: 'VolumeBenefitTier', minimumRunningNotionalTakerVolume: string, volumeDiscountFactor: string }> } }, votes: { __typename?: 'ProposalVotes', yes: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalEquityLikeShareWeight: string }, no: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalEquityLikeShareWeight: string } } } } | null> | null } | null }; export const NewMarketProductFieldsFragmentDoc = gql` fragment NewMarketProductFields on Proposal { @@ -91,6 +94,22 @@ export const UpdateReferralProgramsFragmentDoc = gql` } } `; +export const UpdateVolumeDiscountProgramsFragmentDoc = gql` + fragment UpdateVolumeDiscountPrograms on Proposal { + terms { + change { + ... on UpdateVolumeDiscountProgram { + benefitTiers { + minimumRunningNotionalTakerVolume + volumeDiscountFactor + } + endOfProgramTimestamp + windowLength + } + } + } +} + `; export const ProposalFieldsFragmentDoc = gql` fragment ProposalFields on Proposal { id @@ -174,7 +193,7 @@ export const ProposalFieldsFragmentDoc = gql` } `; export const ProposalsDocument = gql` - query Proposals($includeNewMarketProductFields: Boolean!, $includeUpdateMarketStates: Boolean!, $includeUpdateReferralPrograms: Boolean!) { + query Proposals($includeNewMarketProductFields: Boolean!, $includeUpdateMarketStates: Boolean!, $includeUpdateReferralPrograms: Boolean!, $includeUpdateVolumeDiscountPrograms: Boolean!) { proposalsConnection { edges { node { @@ -182,6 +201,7 @@ export const ProposalsDocument = gql` ...NewMarketProductFields @include(if: $includeNewMarketProductFields) ...UpdateMarketStates @include(if: $includeUpdateMarketStates) ...UpdateReferralPrograms @include(if: $includeUpdateReferralPrograms) + ...UpdateVolumeDiscountPrograms @include(if: $includeUpdateVolumeDiscountPrograms) } } } @@ -189,7 +209,8 @@ export const ProposalsDocument = gql` ${ProposalFieldsFragmentDoc} ${NewMarketProductFieldsFragmentDoc} ${UpdateMarketStatesFragmentDoc} -${UpdateReferralProgramsFragmentDoc}`; +${UpdateReferralProgramsFragmentDoc} +${UpdateVolumeDiscountProgramsFragmentDoc}`; /** * __useProposalsQuery__ @@ -206,6 +227,7 @@ ${UpdateReferralProgramsFragmentDoc}`; * includeNewMarketProductFields: // value for 'includeNewMarketProductFields' * includeUpdateMarketStates: // value for 'includeUpdateMarketStates' * includeUpdateReferralPrograms: // value for 'includeUpdateReferralPrograms' + * includeUpdateVolumeDiscountPrograms: // value for 'includeUpdateVolumeDiscountPrograms' * }, * }); */ diff --git a/apps/governance/src/routes/proposals/proposals/proposals-container.tsx b/apps/governance/src/routes/proposals/proposals/proposals-container.tsx index 104897726a..187633f4b3 100644 --- a/apps/governance/src/routes/proposals/proposals/proposals-container.tsx +++ b/apps/governance/src/routes/proposals/proposals/proposals-container.tsx @@ -50,6 +50,7 @@ export const ProposalsContainer = () => { includeNewMarketProductFields: !!FLAGS.PRODUCT_PERPETUALS, includeUpdateMarketStates: !!FLAGS.UPDATE_MARKET_STATE, includeUpdateReferralPrograms: !!FLAGS.REFERRALS, + includeUpdateVolumeDiscountPrograms: !!FLAGS.VOLUME_DISCOUNTS, }, }); diff --git a/apps/governance/src/routes/proposals/rejected/rejected-proposals-container.tsx b/apps/governance/src/routes/proposals/rejected/rejected-proposals-container.tsx index 2e78b61d1f..f0d4a4fbbd 100644 --- a/apps/governance/src/routes/proposals/rejected/rejected-proposals-container.tsx +++ b/apps/governance/src/routes/proposals/rejected/rejected-proposals-container.tsx @@ -42,6 +42,7 @@ export const RejectedProposalsContainer = () => { includeNewMarketProductFields: !!FLAGS.PRODUCT_PERPETUALS, includeUpdateMarketStates: !!FLAGS.UPDATE_MARKET_STATE, includeUpdateReferralPrograms: !!FLAGS.REFERRALS, + includeUpdateVolumeDiscountPrograms: !!FLAGS.VOLUME_DISCOUNTS, }, }); diff --git a/libs/environment/src/hooks/use-environment.ts b/libs/environment/src/hooks/use-environment.ts index 2c1ccd4e46..b48c09aca8 100644 --- a/libs/environment/src/hooks/use-environment.ts +++ b/libs/environment/src/hooks/use-environment.ts @@ -429,6 +429,12 @@ function compileFeatureFlags(): FeatureFlags { process.env['NX_GOVERNANCE_TRANSFERS'] ) as string ), + VOLUME_DISCOUNTS: TRUTHY.includes( + windowOrDefault( + 'NX_VOLUME_DISCOUNTS', + process.env['NX_VOLUME_DISCOUNTS'] + ) as string + ), }; const EXPLORER_FLAGS = { EXPLORER_ASSETS: TRUTHY.includes( diff --git a/libs/environment/src/types.ts b/libs/environment/src/types.ts index 0f82262220..296adfe22b 100644 --- a/libs/environment/src/types.ts +++ b/libs/environment/src/types.ts @@ -26,6 +26,7 @@ export type CosmicElevatorFlags = Pick< | 'REFERRALS' | 'UPDATE_MARKET_STATE' | 'GOVERNANCE_TRANSFERS' + | 'VOLUME_DISCOUNTS' >; 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 609faba923..3f6a0af4b9 100644 --- a/libs/environment/src/utils/validate-environment.ts +++ b/libs/environment/src/utils/validate-environment.ts @@ -81,6 +81,7 @@ const COSMIC_ELEVATOR_FLAGS = { REFERRALS: z.optional(z.boolean()), UPDATE_MARKET_STATE: z.optional(z.boolean()), GOVERNANCE_TRANSFERS: z.optional(z.boolean()), + VOLUME_DISCOUNTS: z.optional(z.boolean()), }; const EXPLORER_FLAGS = { diff --git a/libs/network-parameters/src/use-network-params.ts b/libs/network-parameters/src/use-network-params.ts index a13e30b291..34ab03ba36 100644 --- a/libs/network-parameters/src/use-network-params.ts +++ b/libs/network-parameters/src/use-network-params.ts @@ -112,6 +112,22 @@ export const NetworkParams = { 'governance_proposal_referralProgram_requiredMajority', governance_proposal_referralProgram_requiredParticipation: 'governance_proposal_referralProgram_requiredParticipation', + governance_proposal_VolumeDiscountProgram_maxClose: + 'governance_proposal_VolumeDiscountProgram_maxClose', + governance_proposal_VolumeDiscountProgram_maxEnact: + 'governance_proposal_VolumeDiscountProgram_maxEnact', + governance_proposal_VolumeDiscountProgram_minClose: + 'governance_proposal_VolumeDiscountProgram_minClose', + governance_proposal_VolumeDiscountProgram_minEnact: + 'governance_proposal_VolumeDiscountProgram_minEnact', + governance_proposal_VolumeDiscountProgram_minProposerBalance: + 'governance_proposal_VolumeDiscountProgram_minProposerBalance', + governance_proposal_VolumeDiscountProgram_minVoterBalance: + 'governance_proposal_VolumeDiscountProgram_minVoterBalance', + governance_proposal_VolumeDiscountProgram_requiredMajority: + 'governance_proposal_VolumeDiscountProgram_requiredMajority', + governance_proposal_VolumeDiscountProgram_requiredParticipation: + 'governance_proposal_VolumeDiscountProgram_requiredParticipation', validators_delegation_minAmount: 'validators_delegation_minAmount', spam_protection_minimumWithdrawalQuantumMultiple: 'spam_protection_minimumWithdrawalQuantumMultiple',