Skip to content

Commit

Permalink
⚓ Integrate set era payout damping factor proposal (#4818)
Browse files Browse the repository at this point in the history
* Fix InputNumber maxAllowedValue

* Create set era payout damping factor proposals

* Preview set era payout damping factor proposals

* Fix typo

* Query `SetEraPayoutDampingFactorProposalDetails`

* Use the proper percentageMapper to preview the proposal

* Compare with current multiplier value

* Describe the proposal

* Validate that the multiplier is at most 100%

* Make the NumberOfBlocks renderer reusable

* Preview CRT constraints proposal

* Create CRT constraints proposal

* Query `UpdateTokenPalletTokenConstraintsProposalDetails`

* Schema and types

* Show current values in the form

* Show current values on the preview page

* Fix the vote on proposal modal

* Fix mismatched values

* Show current values on field inputs sub label instead of message
So the current values remain visible while input values are invalid

* Enable decimal values in `InputNumber`

* Use decimal percent to represent part per million

* Preview decimal percents

* Generate queries

* Patch `@joystream/types`

* Fix type issue
  • Loading branch information
thesan authored Apr 8, 2024
1 parent 7577166 commit 18ea9c5
Show file tree
Hide file tree
Showing 28 changed files with 9,515 additions and 3,762 deletions.
13,075 changes: 9,329 additions & 3,746 deletions .yarn/patches/@joystream-types-npm-4.3.0-542438a0b6.patch

Large diffs are not rendered by default.

32 changes: 32 additions & 0 deletions packages/ui/src/app/pages/Proposals/CurrentProposals.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1554,6 +1554,38 @@ export const SpecificParametersRuntimeUpgrade: Story = {
}),
}

export const SpecificParametersSetEraPayoutDampingFactor: Story = {
play: specificParametersTest('Set Era Payout Damping Factor', async ({ args, createProposal, modal, step }) => {
await createProposal(async () => {
const nextButton = getButtonByText(modal, 'Create proposal')
expect(nextButton).toBeDisabled()

// Valid
const factorField = await modal.findByLabelText('Validator reward multiplier')
await userEvent.type(factorField, '60')
await waitFor(() => expect(nextButton).toBeEnabled())

// Invalid
await userEvent.clear(factorField)
await userEvent.type(factorField, '200')
await modal.findByText('The value must be between 0 and 100%.')
await waitFor(() => expect(nextButton).toBeDisabled())

// Valid again
await userEvent.clear(factorField)
await userEvent.type(factorField, '60')
await waitFor(() => expect(nextButton).toBeEnabled())
})

await step('Transaction parameters', () => {
const [, , specificParameters] = args.onCreateProposal.mock.calls.at(-1)
expect(specificParameters.toJSON()).toEqual({
setEraPayoutDampingFactor: 60,
})
})
}),
}

export const SpecificParametersDecreaseCouncilBudget: Story = {
parameters: {
councilBudget: joy(500),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ export default {
council: {
budget: joy(1000),
councilorReward: joy(1),
eraPayoutDampingFactor: 70,
},
referendum: { stage: {} },
projectToken: {
Expand Down Expand Up @@ -304,6 +305,9 @@ export const UpdateChannelPayouts: Story = {
export const UpdatePalletFrozenStatus: Story = {
args: { type: 'UpdatePalletFrozenStatusProposalDetails' },
}
export const SetEraPayoutDampingFactor: Story = {
args: { type: 'SetEraPayoutDampingFactorProposalDetails' },
}
export const UpdateWorkingGroupBudget: Story = {
args: { type: 'UpdateWorkingGroupBudgetProposalDetails' },
}
Expand Down Expand Up @@ -683,7 +687,7 @@ export const TestCancelProposalHappy: Story = {
})

await step('Confirm', async () => {
expect(await modal.findByText('Your propsal has been cancelled.'))
expect(await modal.findByText('Your proposal has been cancelled.'))

expect(onCancel).toHaveBeenLastCalledWith(activeMember.controllerAccount, activeMember.id, PROPOSAL_DATA.id)
})
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions packages/ui/src/common/api/schemas/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -2593,6 +2593,7 @@ union ProposalDetails =
| UpdateGlobalNftLimitProposalDetails
| DecreaseCouncilBudgetProposalDetails
| UpdateTokenPalletTokenConstraintsProposalDetails
| SetEraPayoutDampingFactorProposalDetails

union ProposalStatus =
ProposalStatusDeciding
Expand Down Expand Up @@ -3338,6 +3339,13 @@ type SetCouncilorRewardProposalDetails {
newRewardPerBlock: BigInt!
}

type SetEraPayoutDampingFactorProposalDetails {
"""
Proposed validator payout damping factor
"""
dampingFactor: Int!
}

type SetInitialInvitationBalanceProposalDetails {
"""
The new (proposed) initial balance credited to controller account of an invitee (locked for transaction fee payments only)
Expand Down
6 changes: 3 additions & 3 deletions packages/ui/src/common/components/forms/InputNumber.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,19 @@ import { Input, InputProps } from './InputComponent'

interface BaseNumberInputProps extends Omit<InputProps, 'type' | 'defaultValue' | 'onChange'> {
decimalScale?: number
maxAllowedValue?: number
maxAllowedValue?: number // At most MAX_SAFE_INTEGER (otherwise InputNumber might not be the right component).
onChange?: (event: React.ChangeEvent<HTMLInputElement>, numberValue: number) => void
}

const BasedInputNumber = React.memo(
({ id, onChange, value = '', maxAllowedValue = 2 ** 32, decimalScale = 0, ...props }: BaseNumberInputProps) => {
({ id, onChange, value = '', maxAllowedValue = 2 ** 32 - 1, decimalScale = 0, ...props }: BaseNumberInputProps) => {
const onInputChange = useCallback(
({ floatValue = 0 }: NumberFormatValues, { event }: SourceInfo) => onChange?.(event, floatValue),
[onChange]
)

const isAllowed = useCallback(
({ floatValue = 0 }: NumberFormatValues) => floatValue < maxAllowedValue,
({ floatValue = 0 }: NumberFormatValues) => floatValue <= maxAllowedValue,
[maxAllowedValue]
)

Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions packages/ui/src/mocks/data/proposals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ const proposalDetails: Record<ProposalDetailsType, RecursivePartial<ProposalWith
UpdateWorkingGroupBudgetProposalDetails: { amount: joy(200), group: workingGroup },
VetoProposalDetails: { proposal: { __typename: 'Proposal', id: '0', title: random.words(4) } },
UpdateGlobalNftLimitProposalDetails: {},
SetEraPayoutDampingFactorProposalDetails: { dampingFactor: 60 },
DecreaseCouncilBudgetProposalDetails: { amount: joy(100) },
UpdateTokenPalletTokenConstraintsProposalDetails: {
maxYearlyRate: 0.4 * 10 ** 6,
Expand Down Expand Up @@ -257,6 +258,7 @@ export const proposalsPagesChain = (
budget: councilBudget,
councilorReward,
nextRewardPayments,
eraPayoutDampingFactor: 70,
},
referendum: { stage: {} },

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ const proposalDetailsToConstantKeyMap = new Map<ProposalDetailsType, keyof Api['
['UpdateWorkingGroupBudgetProposalDetails', 'updateWorkingGroupBudgetProposalParameters'],
['VetoProposalDetails', 'vetoProposalProposalParameters'],
['UpdatePalletFrozenStatusProposalDetails', 'setPalletFozenStatusProposalParameters'],
['SetEraPayoutDampingFactorProposalDetails', 'setEraPayoutDampingFactorProposalParameters'],
['DecreaseCouncilBudgetProposalDetails', 'decreaseCouncilBudgetProposalParameters'],
['UpdateTokenPalletTokenConstraintsProposalDetails', 'updateTokenPalletTokenConstraints'],
])
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,13 @@ const renderTypeMapper: Partial<Record<RenderType, ProposalDetailContent>> = {
export const ProposalDetails = ({ proposalDetails, gracePeriod, exactExecutionBlock, createdInBlock }: Props) => {
const { api } = useApi()
const { budget } = useCouncilStatistics()

const validatorRewardMultiplier = useFirstObservableValue(() => {
if (proposalDetails?.type === 'setEraPayoutDampingFactor') {
return api?.query.council.eraPayoutDampingFactor()
}
}, [api?.isConnected, proposalDetails?.type])

const { group } = useWorkingGroup({
name: (proposalDetails as UpdateGroupBudgetDetails)?.group?.id,
})
Expand Down Expand Up @@ -124,6 +131,17 @@ export const ProposalDetails = ({ proposalDetails, gracePeriod, exactExecutionBl
] as RenderNode[]
}

if (proposalDetails?.type === 'setEraPayoutDampingFactor') {
return [
{
renderType: 'Numeric',
units: '%',
label: 'Current multiplier',
value: validatorRewardMultiplier,
},
] as RenderNode[]
}

if (proposalDetails?.type === 'updateTokenPalletTokenConstraints') {
return [
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,16 @@ export type RenderType =
| 'Divider'
| 'ProposalLink'
| 'OpeningLink'
| 'Percentage'
| 'Hash'
| 'DestinationsPreview'
| 'BlockTimeDisplay'

export interface RenderNode {
label: string
value: any
renderType: RenderType
tooltip?: TooltipContentProp
units?: string
}

type Mapper<Detail, Key extends keyof Detail> = (
Expand Down Expand Up @@ -308,6 +309,9 @@ const mappers: Partial<Record<ProposalDetailsKeys, Mapper<any, any>>> = {
pallet: palletMapper,
freeze: palletStatusMapper,

// SetEraPayoutDampingFactor
multiplier: percentageMapper('Validator reward multiplier'),

// UpdateTokenPalletTokenConstraints
maxYearlyRate: percentageMapper('Proposed maximum yearly rate'),
minAmmSlope: amountMapper('Proposed minimum AMM slope'),
Expand Down
1 change: 1 addition & 0 deletions packages/ui/src/proposals/hooks/useProposalConstants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ const proposalTypeToConstantKey = new Map<ProposalType, keyof Api['consts']['pro
['terminateWorkingGroupLead', 'terminateWorkingGroupLeadProposalParameters'],
['updateWorkingGroupBudget', 'updateWorkingGroupBudgetProposalParameters'],
['veto', 'vetoProposalProposalParameters'],
['setEraPayoutDampingFactor', 'setEraPayoutDampingFactorProposalParameters'],
['decreaseCouncilBudget', 'decreaseCouncilBudgetProposalParameters'],
['updateTokenPalletTokenConstraints', 'updateTokenPalletTokenConstraints'],
])
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import React from 'react'

import { useApi } from '@/api/hooks/useApi'
import { InputComponent, InputNumber } from '@/common/components/forms'
import { Row } from '@/common/components/Modal'
import { RowGapBlock } from '@/common/components/page/PageContent'
import { TextMedium } from '@/common/components/typography'
import { useFirstObservableValue } from '@/common/hooks/useFirstObservableValue'

export const SetEraPayoutDampingFactor = () => {
const { api } = useApi()
const current = useFirstObservableValue(() => api?.query.council.eraPayoutDampingFactor(), [api?.isConnected])

return (
<RowGapBlock gap={24}>
<Row>
<RowGapBlock gap={8}>
<h4>Specific parameters</h4>
<TextMedium lighter>
Set the validator reward multiplier. {current && `The current value is ${current.toNumber()}%`}.
</TextMedium>
</RowGapBlock>
</Row>
<Row>
<RowGapBlock gap={20}>
<InputComponent
message={'Amount must be greater than zero'}
id="damping-factor-input"
name="setEraPayoutDampingFactor.dampingFactor"
label="Validator reward multiplier"
tight
units="%"
required
>
<InputNumber id="damping-factor-input" name="setEraPayoutDampingFactor.dampingFactor" placeholder="100" />
</InputComponent>
</RowGapBlock>
</Row>
</RowGapBlock>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ export const SetMembershipLeadInvitationQuota = () => {
id="amount-input"
name="setMembershipLeadInvitationQuota.count"
placeholder="0"
maxAllowedValue={Math.pow(2, 32) - 1}
disabled={isLoading || !group?.leadId}
/>
</InputComponent>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export const SetReferralCut = () => {
id="amount-input"
name="setReferralCut.referralCut"
placeholder="0"
maxAllowedValue={Math.pow(2, 8)}
maxAllowedValue={2 ** 8 - 1}
/>
</InputComponent>
</RowGapBlock>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import { FillWorkingGroupLeadOpening } from '@/proposals/modals/AddNewProposal/c
import { AddNewProposalMachineState } from '@/proposals/modals/AddNewProposal/machine'

import { DecreaseCouncilBudget } from './DecreaseCouncilBudget'
import { SetEraPayoutDampingFactor } from './SetEraPayoutDampingFactor'
import { SetInitialInvitationBalance } from './SetInitialInvitationBalance'
import { SetInitialInvitationCount } from './SetInitialInvitationCount'
import { UpdatePalletFrozenStatus } from './UpdatePalletFrozenStatus'
Expand Down Expand Up @@ -88,6 +89,9 @@ export const SpecificParametersStep = ({ matches }: SpecificParametersStepProps)
case matches('specificParameters.updatePalletFrozenStatus'): {
return <UpdatePalletFrozenStatus />
}
case matches('specificParameters.setEraPayoutDampingFactor'): {
return <SetEraPayoutDampingFactor />
}
case matches('specificParameters.decreaseCouncilBudget'): {
return <DecreaseCouncilBudget />
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,7 @@ export const TriggerAndDiscussionStep = () => {
name="triggerAndDiscussion.triggerBlock"
message={triggerBlock ? `≈ ${inBlocksDate(triggerBlock)}` : ''}
>
<InputNumber
id="triggerBlock"
placeholder="0"
name="triggerAndDiscussion.triggerBlock"
maxAllowedValue={Math.pow(2, 32)}
/>
<InputNumber id="triggerBlock" placeholder="0" name="triggerAndDiscussion.triggerBlock" />
</InputComponent>
)}
</RowGapBlock>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -189,9 +189,15 @@ export const getSpecificParameters = async (
}
case 'updatePalletFrozenStatus': {
return createType('PalletProposalsCodexProposalDetails', {
// NOTE: The "SetPalletFozenStatus" typo comes from the runtime so it should be fixed there first.
SetPalletFozenStatus: [!specifics.updatePalletFrozenStatus.enable, specifics.updatePalletFrozenStatus.pallet],
})
}
case 'setEraPayoutDampingFactor': {
return createType('PalletProposalsCodexProposalDetails', {
setEraPayoutDampingFactor: createType('Percent', specifics?.setEraPayoutDampingFactor?.dampingFactor ?? 100),
})
}
case 'decreaseCouncilBudget': {
return createType('PalletProposalsCodexProposalDetails', {
DecreaseCouncilBudget: specifics.decreaseCouncilBudget?.amount,
Expand Down
Loading

0 comments on commit 18ea9c5

Please sign in to comment.