feat(governance): volume discount proposals (#5022)

This commit is contained in:
Sam Keen 2023-10-11 19:51:52 +01:00 committed by GitHub
parent 6651ee52b4
commit b137e024c6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 413 additions and 14 deletions

View File

@ -24,6 +24,7 @@ NX_SUCCESSOR_MARKETS=true
NX_PRODUCT_PERPETUALS=true NX_PRODUCT_PERPETUALS=true
NX_REFERRALS=true NX_REFERRALS=true
NX_UPDATE_MARKET_STATE=true NX_UPDATE_MARKET_STATE=true
NX_VOLUME_DISCOUNTS=true
NX_VEGA_EXPLORER_URL=https://explorer.fairground.wtf NX_VEGA_EXPLORER_URL=https://explorer.fairground.wtf
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn

View File

@ -7,3 +7,4 @@ NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/m
NX_PRODUCT_PERPETUALS=true NX_PRODUCT_PERPETUALS=true
NX_UPDATE_MARKET_STATE=true NX_UPDATE_MARKET_STATE=true
NX_REFERRALS=true NX_REFERRALS=true
NX_VOLUME_DISCOUNTS=true

View File

@ -7,3 +7,4 @@ NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/m
NX_PRODUCT_PERPETUALS=false NX_PRODUCT_PERPETUALS=false
NX_UPDATE_MARKET_STATE=false NX_UPDATE_MARKET_STATE=false
NX_REFERRALS=false NX_REFERRALS=false
NX_VOLUME_DISCOUNTS=false

View File

@ -7,3 +7,4 @@ NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/m
NX_PRODUCT_PERPETUALS=true NX_PRODUCT_PERPETUALS=true
NX_UPDATE_MARKET_STATE=true NX_UPDATE_MARKET_STATE=true
NX_REFERRALS=true NX_REFERRALS=true
NX_VOLUME_DISCOUNTS=true

View File

@ -37,3 +37,4 @@ NX_PRODUCT_PERPETUALS=false
NX_UPDATE_MARKET_STATE=false NX_UPDATE_MARKET_STATE=false
NX_REFERRALS=false NX_REFERRALS=false
NX_GOVERNANCE_TRANSFERS=false NX_GOVERNANCE_TRANSFERS=false
NX_VOLUME_DISCOUNTS=false

View File

@ -36,3 +36,4 @@ NX_METAMASK_SNAPS=false
NX_PRODUCT_PERPETUALS=true NX_PRODUCT_PERPETUALS=true
NX_UPDATE_MARKET_STATE=true NX_UPDATE_MARKET_STATE=true
NX_REFERRALS=true NX_REFERRALS=true
NX_VOLUME_DISCOUNTS=true

View File

@ -28,3 +28,4 @@ NX_METAMASK_SNAPS=true
NX_PRODUCT_PERPETUALS=true NX_PRODUCT_PERPETUALS=true
NX_UPDATE_MARKET_STATE=true NX_UPDATE_MARKET_STATE=true
NX_REFERRALS=true NX_REFERRALS=true
NX_VOLUME_DISCOUNTS=true

View File

@ -27,3 +27,4 @@ NX_METAMASK_SNAPS=false
NX_PRODUCT_PERPETUALS=false NX_PRODUCT_PERPETUALS=false
NX_UPDATE_MARKET_STATE=false NX_UPDATE_MARKET_STATE=false
NX_REFERRALS=false NX_REFERRALS=false
NX_VOLUME_DISCOUNTS=false

View File

@ -26,3 +26,4 @@ NX_METAMASK_SNAPS=false
NX_PRODUCT_PERPETUALS=false NX_PRODUCT_PERPETUALS=false
NX_UPDATE_MARKET_STATE=false NX_UPDATE_MARKET_STATE=false
NX_REFERRALS=false NX_REFERRALS=false
NX_VOLUME_DISCOUNTS=false

View File

@ -25,3 +25,4 @@ NX_PRODUCT_PERPETUALS=true
NX_UPDATE_MARKET_STATE=true NX_UPDATE_MARKET_STATE=true
NX_REFERRALS=true NX_REFERRALS=true
NX_GOVERNANCE_TRANSFERS=true NX_GOVERNANCE_TRANSFERS=true
NX_VOLUME_DISCOUNTS=true

View File

@ -29,3 +29,4 @@ NX_METAMASK_SNAPS=true
NX_PRODUCT_PERPETUALS=true NX_PRODUCT_PERPETUALS=true
NX_UPDATE_MARKET_STATE=true NX_UPDATE_MARKET_STATE=true
NX_REFERRALS=true NX_REFERRALS=true
NX_VOLUME_DISCOUNTS=true

View File

@ -25,3 +25,4 @@ NX_METAMASK_SNAPS=false
NX_PRODUCT_PERPETUALS=false NX_PRODUCT_PERPETUALS=false
NX_UPDATE_MARKET_STATE=false NX_UPDATE_MARKET_STATE=false
NX_REFERRALS=false NX_REFERRALS=false
NX_VOLUME_DISCOUNTS=false

View File

@ -710,6 +710,7 @@
"UpdateMarketProposal": "Update market proposal", "UpdateMarketProposal": "Update market proposal",
"UpdateMarketStateProposal": "Update market state proposal", "UpdateMarketStateProposal": "Update market state proposal",
"UpdateReferralProgramProposal": "Update referral program proposal", "UpdateReferralProgramProposal": "Update referral program proposal",
"UpdateVolumeDiscountProgramProposal": "Update volume discount program proposal",
"MarketChange": "Market change", "MarketChange": "Market change",
"MarketStateChange": "Market state change", "MarketStateChange": "Market state change",
"MarketDetails": "Market details", "MarketDetails": "Market details",
@ -734,6 +735,7 @@
"UpdateMarket": "Update market", "UpdateMarket": "Update market",
"UpdateMarketState": "Update market state", "UpdateMarketState": "Update market state",
"UpdateReferralProgram": "Update referral program", "UpdateReferralProgram": "Update referral program",
"UpdateVolumeDiscountProgram": "Update volume discount program",
"NewAsset": "New asset", "NewAsset": "New asset",
"UpdateAsset": "Update asset", "UpdateAsset": "Update asset",
"AssetID": "Asset ID", "AssetID": "Asset ID",
@ -910,5 +912,7 @@
"WindowLength": "Window length", "WindowLength": "Window length",
"WindowLengthDescription": "Number of epochs over which to evaluate a referral set's running volume", "WindowLengthDescription": "Number of epochs over which to evaluate a referral set's running volume",
"EndOfProgramTimestamp": "End of program", "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"
} }

View File

@ -189,6 +189,7 @@ const GovernanceHome = ({ name }: RouteChildProps) => {
includeNewMarketProductFields: !!FLAGS.PRODUCT_PERPETUALS, includeNewMarketProductFields: !!FLAGS.PRODUCT_PERPETUALS,
includeUpdateMarketStates: !!FLAGS.UPDATE_MARKET_STATE, includeUpdateMarketStates: !!FLAGS.UPDATE_MARKET_STATE,
includeUpdateReferralPrograms: !!FLAGS.REFERRALS, includeUpdateReferralPrograms: !!FLAGS.REFERRALS,
includeUpdateVolumeDiscountPrograms: !!FLAGS.VOLUME_DISCOUNTS,
}, },
}); });

View File

@ -103,6 +103,11 @@ export const ProposalHeader = ({
fallbackTitle = t('UpdateReferralProgramProposal'); fallbackTitle = t('UpdateReferralProgramProposal');
break; break;
} }
case 'UpdateVolumeDiscountProgram': {
proposalType = 'UpdateVolumeDiscountProgram';
fallbackTitle = t('UpdateVolumeDiscountProgramProposal');
break;
}
case 'NewAsset': { case 'NewAsset': {
proposalType = 'NewAsset'; proposalType = 'NewAsset';
fallbackTitle = t('NewAssetProposal'); fallbackTitle = t('NewAssetProposal');

View File

@ -1,6 +1,5 @@
import { render, screen } from '@testing-library/react'; import { render, screen } from '@testing-library/react';
import { import {
formatEndOfProgramTimestamp,
formatMinimumRunningNotionalTakerVolume, formatMinimumRunningNotionalTakerVolume,
formatReferralDiscountFactor, formatReferralDiscountFactor,
formatReferralRewardFactor, formatReferralRewardFactor,
@ -18,13 +17,16 @@ jest.mock('../../../../contexts/app-state/app-state-context', () => ({
}), }),
})); }));
describe('ProposalReferralProgramDetails helper functions', () => { beforeEach(() => {
it('should format end of program timestamp correctly', () => { jest.useFakeTimers();
const input = '2023-01-01T12:00:00Z'; jest.setSystemTime(0);
const formatted = formatEndOfProgramTimestamp(input); });
expect(formatted).toBe('01 January 2023 12:00 (GMT)');
});
afterEach(() => {
jest.useRealTimers();
});
describe('ProposalReferralProgramDetails helper functions', () => {
it('should format minimum running notional taker volume correctly', () => { it('should format minimum running notional taker volume correctly', () => {
const input = '1000'; const input = '1000';
const formatted = formatMinimumRunningNotionalTakerVolume(input); const formatted = formatMinimumRunningNotionalTakerVolume(input);

View File

@ -0,0 +1 @@
export * from './proposal-volume-discount-program-details';

View File

@ -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(<ProposalVolumeDiscountProgramDetails proposal={null} />);
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(
<ProposalVolumeDiscountProgramDetails proposal={updateMarketProposal} />
);
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(
<ProposalVolumeDiscountProgramDetails proposal={incompleteProposal} />
);
expect(
screen.queryByTestId('proposal-volume-discount-program-details')
).toBeNull();
});
it('should render relevant fields if present', () => {
render(
<ProposalVolumeDiscountProgramDetails proposal={mockReferralProposal} />
);
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();
});
});

View File

@ -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 (
<div data-testid="proposal-volume-discount-program-details">
<RoundedWrapper paddingBottom={true}>
{windowLength && (
<div data-testid="proposal-volume-discount-program-window-length">
<KeyValueTable>
<KeyValueTableRow>
<Tooltip description={t('WindowLengthDescription')}>
<span>{t('WindowLength')}</span>
</Tooltip>
{windowLength}
</KeyValueTableRow>
</KeyValueTable>
</div>
)}
{endOfProgramTimestamp && (
<div
className="mb-6"
data-testid="proposal-volume-discount-program-end-of-program-timestamp"
>
<KeyValueTable>
<KeyValueTableRow>
<Tooltip description={t('EndOfProgramTimestampDescription')}>
<span>{t('EndOfProgramTimestamp')}</span>
</Tooltip>
{formatEndOfProgramTimestamp(endOfProgramTimestamp)}
</KeyValueTableRow>
</KeyValueTable>
</div>
)}
{benefitTiers && (
<div
className="mb-6"
data-testid="proposal-volume-discount-program-benefit-tiers"
>
<h3 className="mb-3 uppercase font-semibold text-lg">
{t('BenefitTiers')}
</h3>
<KeyValueTable>
{benefitTiers
.sort(
(a, b) =>
Number(a.minimumRunningNotionalTakerVolume) -
Number(b.minimumRunningNotionalTakerVolume)
)
.map((benefitTier, index) => (
<div className="mb-4" key={index}>
<h4 className="font-semibold uppercase">
Tier {index + 1}
</h4>
{benefitTier.minimumRunningNotionalTakerVolume && (
<KeyValueTableRow>
<Tooltip
description={t(
'BenefitTierMinimumRunningNotionalTakerVolumeDescription'
)}
>
<span>
{t('BenefitTierMinimumRunningNotionalTakerVolume')}
</span>
</Tooltip>
{formatMinimumRunningNotionalTakerVolume(
benefitTier.minimumRunningNotionalTakerVolume
)}
</KeyValueTableRow>
)}
{benefitTier.volumeDiscountFactor && (
<KeyValueTableRow>
<Tooltip
description={t(
'BenefitTierVolumeDiscountFactorDescription'
)}
>
<span>{t('BenefitTierVolumeDiscountFactor')}</span>
</Tooltip>
{formatVolumeDiscountFactor(
benefitTier.volumeDiscountFactor
)}
</KeyValueTableRow>
)}
</div>
))}
</KeyValueTable>
</div>
)}
</RoundedWrapper>
</div>
);
};

View File

@ -7,6 +7,7 @@ import { ProposalChangeTable } from '../proposal-change-table';
import { ProposalJson } from '../proposal-json'; import { ProposalJson } from '../proposal-json';
import { ProposalAssetDetails } from '../proposal-asset-details'; import { ProposalAssetDetails } from '../proposal-asset-details';
import { ProposalReferralProgramDetails } from '../proposal-referral-program-details'; import { ProposalReferralProgramDetails } from '../proposal-referral-program-details';
import { ProposalVolumeDiscountProgramDetails } from '../proposal-volume-discount-program-details';
import { UserVote } from '../vote-details'; import { UserVote } from '../vote-details';
import { ListAsset } from '../list-asset'; import { ListAsset } from '../list-asset';
import Routes from '../../../routes'; import Routes from '../../../routes';
@ -119,6 +120,10 @@ export const Proposal = ({
minVoterBalance = minVoterBalance =
networkParams.governance_proposal_referralProgram_minVoterBalance; networkParams.governance_proposal_referralProgram_minVoterBalance;
break; break;
case 'UpdateVolumeDiscountProgram':
minVoterBalance =
networkParams.governance_proposal_VolumeDiscountProgram_minVoterBalance;
break;
} }
} }
@ -232,6 +237,12 @@ export const Proposal = ({
</div> </div>
)} )}
{proposal.terms.change.__typename === 'UpdateVolumeDiscountProgram' && (
<div className="mb-4">
<ProposalVolumeDiscountProgramDetails proposal={proposal} />
</div>
)}
{governanceTransferDetails} {governanceTransferDetails}
<div className="mb-10"> <div className="mb-10">

View File

@ -28,6 +28,8 @@ export const useProposalNetworkParams = ({
NetworkParams.governance_proposal_updateNetParam_requiredParticipation, NetworkParams.governance_proposal_updateNetParam_requiredParticipation,
NetworkParams.governance_proposal_freeform_requiredMajority, NetworkParams.governance_proposal_freeform_requiredMajority,
NetworkParams.governance_proposal_freeform_requiredParticipation, NetworkParams.governance_proposal_freeform_requiredParticipation,
NetworkParams.governance_proposal_VolumeDiscountProgram_requiredMajority,
NetworkParams.governance_proposal_VolumeDiscountProgram_requiredParticipation,
]); ]);
const fallback = { const fallback = {
@ -101,6 +103,14 @@ export const useProposalNetworkParams = ({
params.governance_proposal_referralProgram_requiredParticipation params.governance_proposal_referralProgram_requiredParticipation
), ),
}; };
case 'UpdateVolumeDiscountProgram':
return {
requiredMajority:
params.governance_proposal_VolumeDiscountProgram_requiredMajority,
requiredParticipation: new BigNumber(
params.governance_proposal_VolumeDiscountProgram_requiredParticipation
),
};
default: default:
return fallback; return fallback;
} }

View File

@ -66,11 +66,27 @@ fragment UpdateReferralProgram on Proposal {
} }
} }
fragment UpdateVolumeDiscountProgram on Proposal {
terms {
change {
... on UpdateVolumeDiscountProgram {
benefitTiers {
minimumRunningNotionalTakerVolume
volumeDiscountFactor
}
endOfProgramTimestamp
windowLength
}
}
}
}
query Proposal( query Proposal(
$proposalId: ID! $proposalId: ID!
$includeNewMarketProductField: Boolean! $includeNewMarketProductField: Boolean!
$includeUpdateMarketState: Boolean! $includeUpdateMarketState: Boolean!
$includeUpdateReferralProgram: Boolean! $includeUpdateReferralProgram: Boolean!
$includeUpdateVolumeDiscountProgram: Boolean!
) { ) {
proposal(id: $proposalId) { proposal(id: $proposalId) {
id id
@ -89,6 +105,8 @@ query Proposal(
...NewMarketProductField @include(if: $includeNewMarketProductField) ...NewMarketProductField @include(if: $includeNewMarketProductField)
...UpdateMarketState @include(if: $includeUpdateMarketState) ...UpdateMarketState @include(if: $includeUpdateMarketState)
...UpdateReferralProgram @include(if: $includeUpdateReferralProgram) ...UpdateReferralProgram @include(if: $includeUpdateReferralProgram)
...UpdateVolumeDiscountProgram
@include(if: $includeUpdateVolumeDiscountProgram)
terms { terms {
closingDatetime closingDatetime
enactmentDatetime enactmentDatetime

File diff suppressed because one or more lines are too long

View File

@ -37,6 +37,7 @@ export const ProposalContainer = () => {
NetworkParams.governance_proposal_updateNetParam_minVoterBalance, NetworkParams.governance_proposal_updateNetParam_minVoterBalance,
NetworkParams.governance_proposal_freeform_minVoterBalance, NetworkParams.governance_proposal_freeform_minVoterBalance,
NetworkParams.governance_proposal_referralProgram_minVoterBalance, NetworkParams.governance_proposal_referralProgram_minVoterBalance,
NetworkParams.governance_proposal_VolumeDiscountProgram_minVoterBalance,
NetworkParams.spam_protection_voting_min_tokens, NetworkParams.spam_protection_voting_min_tokens,
NetworkParams.governance_proposal_market_requiredMajority, NetworkParams.governance_proposal_market_requiredMajority,
NetworkParams.governance_proposal_updateMarket_requiredMajority, NetworkParams.governance_proposal_updateMarket_requiredMajority,
@ -46,6 +47,7 @@ export const ProposalContainer = () => {
NetworkParams.governance_proposal_updateNetParam_requiredMajority, NetworkParams.governance_proposal_updateNetParam_requiredMajority,
NetworkParams.governance_proposal_freeform_requiredMajority, NetworkParams.governance_proposal_freeform_requiredMajority,
NetworkParams.governance_proposal_referralProgram_requiredMajority, NetworkParams.governance_proposal_referralProgram_requiredMajority,
NetworkParams.governance_proposal_VolumeDiscountProgram_requiredMajority,
]); ]);
const { const {
@ -60,6 +62,7 @@ export const ProposalContainer = () => {
includeNewMarketProductField: !!FLAGS.PRODUCT_PERPETUALS, includeNewMarketProductField: !!FLAGS.PRODUCT_PERPETUALS,
includeUpdateMarketState: !!FLAGS.UPDATE_MARKET_STATE, includeUpdateMarketState: !!FLAGS.UPDATE_MARKET_STATE,
includeUpdateReferralProgram: !!FLAGS.REFERRALS, includeUpdateReferralProgram: !!FLAGS.REFERRALS,
includeUpdateVolumeDiscountProgram: !!FLAGS.VOLUME_DISCOUNTS,
}, },
skip: !params.proposalId, skip: !params.proposalId,
}); });

View File

@ -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 { fragment ProposalFields on Proposal {
id id
rationale { rationale {
@ -151,6 +166,7 @@ query Proposals(
$includeNewMarketProductFields: Boolean! $includeNewMarketProductFields: Boolean!
$includeUpdateMarketStates: Boolean! $includeUpdateMarketStates: Boolean!
$includeUpdateReferralPrograms: Boolean! $includeUpdateReferralPrograms: Boolean!
$includeUpdateVolumeDiscountPrograms: Boolean!
) { ) {
proposalsConnection { proposalsConnection {
edges { edges {
@ -159,6 +175,8 @@ query Proposals(
...NewMarketProductFields @include(if: $includeNewMarketProductFields) ...NewMarketProductFields @include(if: $includeNewMarketProductFields)
...UpdateMarketStates @include(if: $includeUpdateMarketStates) ...UpdateMarketStates @include(if: $includeUpdateMarketStates)
...UpdateReferralPrograms @include(if: $includeUpdateReferralPrograms) ...UpdateReferralPrograms @include(if: $includeUpdateReferralPrograms)
...UpdateVolumeDiscountPrograms
@include(if: $includeUpdateVolumeDiscountPrograms)
} }
} }
} }

View File

@ -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 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 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<{ export type ProposalsQueryVariables = Types.Exact<{
includeNewMarketProductFields: Types.Scalars['Boolean']; includeNewMarketProductFields: Types.Scalars['Boolean'];
includeUpdateMarketStates: Types.Scalars['Boolean']; includeUpdateMarketStates: Types.Scalars['Boolean'];
includeUpdateReferralPrograms: 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` export const NewMarketProductFieldsFragmentDoc = gql`
fragment NewMarketProductFields on Proposal { 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` export const ProposalFieldsFragmentDoc = gql`
fragment ProposalFields on Proposal { fragment ProposalFields on Proposal {
id id
@ -174,7 +193,7 @@ export const ProposalFieldsFragmentDoc = gql`
} }
`; `;
export const ProposalsDocument = gql` export const ProposalsDocument = gql`
query Proposals($includeNewMarketProductFields: Boolean!, $includeUpdateMarketStates: Boolean!, $includeUpdateReferralPrograms: Boolean!) { query Proposals($includeNewMarketProductFields: Boolean!, $includeUpdateMarketStates: Boolean!, $includeUpdateReferralPrograms: Boolean!, $includeUpdateVolumeDiscountPrograms: Boolean!) {
proposalsConnection { proposalsConnection {
edges { edges {
node { node {
@ -182,6 +201,7 @@ export const ProposalsDocument = gql`
...NewMarketProductFields @include(if: $includeNewMarketProductFields) ...NewMarketProductFields @include(if: $includeNewMarketProductFields)
...UpdateMarketStates @include(if: $includeUpdateMarketStates) ...UpdateMarketStates @include(if: $includeUpdateMarketStates)
...UpdateReferralPrograms @include(if: $includeUpdateReferralPrograms) ...UpdateReferralPrograms @include(if: $includeUpdateReferralPrograms)
...UpdateVolumeDiscountPrograms @include(if: $includeUpdateVolumeDiscountPrograms)
} }
} }
} }
@ -189,7 +209,8 @@ export const ProposalsDocument = gql`
${ProposalFieldsFragmentDoc} ${ProposalFieldsFragmentDoc}
${NewMarketProductFieldsFragmentDoc} ${NewMarketProductFieldsFragmentDoc}
${UpdateMarketStatesFragmentDoc} ${UpdateMarketStatesFragmentDoc}
${UpdateReferralProgramsFragmentDoc}`; ${UpdateReferralProgramsFragmentDoc}
${UpdateVolumeDiscountProgramsFragmentDoc}`;
/** /**
* __useProposalsQuery__ * __useProposalsQuery__
@ -206,6 +227,7 @@ ${UpdateReferralProgramsFragmentDoc}`;
* includeNewMarketProductFields: // value for 'includeNewMarketProductFields' * includeNewMarketProductFields: // value for 'includeNewMarketProductFields'
* includeUpdateMarketStates: // value for 'includeUpdateMarketStates' * includeUpdateMarketStates: // value for 'includeUpdateMarketStates'
* includeUpdateReferralPrograms: // value for 'includeUpdateReferralPrograms' * includeUpdateReferralPrograms: // value for 'includeUpdateReferralPrograms'
* includeUpdateVolumeDiscountPrograms: // value for 'includeUpdateVolumeDiscountPrograms'
* }, * },
* }); * });
*/ */

View File

@ -50,6 +50,7 @@ export const ProposalsContainer = () => {
includeNewMarketProductFields: !!FLAGS.PRODUCT_PERPETUALS, includeNewMarketProductFields: !!FLAGS.PRODUCT_PERPETUALS,
includeUpdateMarketStates: !!FLAGS.UPDATE_MARKET_STATE, includeUpdateMarketStates: !!FLAGS.UPDATE_MARKET_STATE,
includeUpdateReferralPrograms: !!FLAGS.REFERRALS, includeUpdateReferralPrograms: !!FLAGS.REFERRALS,
includeUpdateVolumeDiscountPrograms: !!FLAGS.VOLUME_DISCOUNTS,
}, },
}); });

View File

@ -42,6 +42,7 @@ export const RejectedProposalsContainer = () => {
includeNewMarketProductFields: !!FLAGS.PRODUCT_PERPETUALS, includeNewMarketProductFields: !!FLAGS.PRODUCT_PERPETUALS,
includeUpdateMarketStates: !!FLAGS.UPDATE_MARKET_STATE, includeUpdateMarketStates: !!FLAGS.UPDATE_MARKET_STATE,
includeUpdateReferralPrograms: !!FLAGS.REFERRALS, includeUpdateReferralPrograms: !!FLAGS.REFERRALS,
includeUpdateVolumeDiscountPrograms: !!FLAGS.VOLUME_DISCOUNTS,
}, },
}); });

View File

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

View File

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

View File

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

View File

@ -112,6 +112,22 @@ export const NetworkParams = {
'governance_proposal_referralProgram_requiredMajority', 'governance_proposal_referralProgram_requiredMajority',
governance_proposal_referralProgram_requiredParticipation: governance_proposal_referralProgram_requiredParticipation:
'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', validators_delegation_minAmount: 'validators_delegation_minAmount',
spam_protection_minimumWithdrawalQuantumMultiple: spam_protection_minimumWithdrawalQuantumMultiple:
'spam_protection_minimumWithdrawalQuantumMultiple', 'spam_protection_minimumWithdrawalQuantumMultiple',