Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

💰 Fix update payout proposal creation #4095

Merged
merged 26 commits into from
Feb 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
996fc61
Add the `channelPayoutsComitmentFromPayload` helper function
thesan Jan 17, 2023
4d6081f
Upload the binary payload file instead of the json
thesan Jan 17, 2023
3029200
Do not validate the payload
thesan Jan 18, 2023
582c250
Update tsconfig for better web workers support
thesan Jan 20, 2023
04fd1ac
Geneate the payload hash correctly
thesan Jan 23, 2023
98dcdd5
Fix non web builds
thesan Jan 24, 2023
dfb578b
Add a story
thesan Jan 24, 2023
2aba6ad
Fix the `Utils: Crypto` tests
thesan Jan 24, 2023
ecee57a
Add more allowed mime types
thesan Jan 25, 2023
8d05f21
Remove mime type constraints
thesan Jan 25, 2023
ed3fbc9
Temp validation for min and max cashout
thesan Jan 25, 2023
29c1c0b
Fix failing tests
thesan Jan 26, 2023
aa00084
Fix the add new proposal tests
thesan Jan 26, 2023
5a0d28c
Rename the `merkleRootFromBinary` util
thesan Jan 26, 2023
06980b3
Update `@joystream/js`
thesan Jan 26, 2023
4125cff
Patch @joystream/js@1.2.0
thesan Jan 26, 2023
c6f1790
Fix the payload validation errors
thesan Jan 27, 2023
2a4eb15
Remove unnecessary changes
thesan Jan 27, 2023
8e759c8
Allow copy from payload size commitment and hash view fields
thesan Jan 27, 2023
f19daed
Make the payload non-required
thesan Jan 29, 2023
3262918
Fix the story
thesan Jan 29, 2023
3d33e67
Fix the min/max cashout validation
thesan Jan 30, 2023
6d2ea3e
Make the hardcoded `maxCashoutAllowed` more readable
thesan Jan 30, 2023
5e4fef9
Merge branch 'ephesus' of github.com:Joystream/pioneer into fix/payou…
thesan Jan 31, 2023
56440c2
Address CRs
thesan Feb 2, 2023
faf276f
Remove the `payload.uploaderAccount`
thesan Feb 2, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 0 additions & 39 deletions .yarn/patches/@joystream-js-npm-1.0.0-b6846bd440.patch

This file was deleted.

353 changes: 353 additions & 0 deletions .yarn/patches/@joystream-js-npm-1.2.0-a8795e7496.patch

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,10 @@
"@polkadot/util": "9.5.1",
"@polkadot/util-crypto": "9.5.1",
"bn.js": "^4.11.9",
"@joystream/js@^1.0.0": "patch:@joystream/js@npm%3A1.0.0#./.yarn/patches/@joystream-js-npm-1.0.0-b6846bd440.patch"
"@joystream/js@1.2.0": "patch:@joystream/js@npm%3A1.2.0#./.yarn/patches/@joystream-js-npm-1.2.0-a8795e7496.patch"
},
"engines": {
"node": ">=14.0.0",
"node": ">=14.18.0",
"yarn": "^1.22.0"
},
"lint-staged": {
Expand Down
13 changes: 12 additions & 1 deletion packages/ui/.storybook/main.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
const path = require('path')

const { merge } = require('lodash')

const shared = require('./../dev/webpack.shared')

module.exports = {
Expand All @@ -14,7 +18,14 @@ module.exports = {
}
})

config.resolve = shared.resolve
config.resolve = merge(
{
alias: {
'@/common/utils/crypto/worker$': path.resolve(__dirname, '../src/common/utils/crypto'),
},
},
shared.resolve
)
config.plugins.push(...shared.plugins)
config.module.rules.unshift(...shared.rules)

Expand Down
3 changes: 2 additions & 1 deletion packages/ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,13 @@
"dependencies": {
"@apollo/client": "3.5.7",
"@hcaptcha/react-hcaptcha": "^1.4.4",
"@joystream/js": "^1.0.0",
"@joystream/js": "1.2.0",
"@joystream/markdown-editor": "^0.1.0",
"@joystream/metadata-protobuf": "^2.0.0",
"@joystream/types": "0.20.5",
"@nivo/bar": "^0.79.1",
"@nivo/core": "^0.79.0",
"@noble/hashes": "^1.1.5",
"@polkadot/api": "8.9.1",
"@polkadot/extension-dapp": "0.44.2-4",
"@polkadot/keyring": "9.5.1",
Expand Down
Empty file.
11 changes: 9 additions & 2 deletions packages/ui/src/common/components/FileDropzone/FileDropzone.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,19 @@ interface DragResponseProps {
interface FileDropzoneProps extends Omit<DropzoneOptions, 'getFilesFromEvent'> {
title: string
subtitle: string
isRequired?: boolean
getFilesFromEvent: (file: File[]) => Promise<File[]>
}

const MEGABYTE = 1024 * 1024

export const FileDropzone = ({ title, subtitle, getFilesFromEvent, ...dropzoneOptions }: FileDropzoneProps) => {
export const FileDropzone = ({
title,
subtitle,
getFilesFromEvent,
isRequired,
...dropzoneOptions
}: FileDropzoneProps) => {
const { isDragActive, isDragAccept, isDragReject, getRootProps, getInputProps, acceptedFiles, fileRejections } =
useDropzone({
...dropzoneOptions,
Expand All @@ -34,7 +41,7 @@ export const FileDropzone = ({ title, subtitle, getFilesFromEvent, ...dropzoneOp
<RowGapBlock gap={4}>
<RowGapBlock gap={12}>
<RowGapBlock gap={4}>
<Label isRequired>{title}</Label>
<Label isRequired={isRequired}>{title}</Label>
<TextMedium lighter>{subtitle}</TextMedium>
</RowGapBlock>
<DropZone
Expand Down
3 changes: 3 additions & 0 deletions packages/ui/src/common/utils/bn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,6 @@ export const sumBN = (a: BN | undefined, b: BN | undefined): BN => new BN(a ?? 0
export const powerOf10 = (value: any) => BN_TEN.pow(asBN(value))

export const powerOf2 = (value: any) => BN_TWO.pow(asBN(value))

const ONE_JOY = powerOf10(10)
export const joy = (unit: number, decimal: number) => new BN(unit).mul(ONE_JOY).add(new BN(decimal))
17 changes: 17 additions & 0 deletions packages/ui/src/common/utils/crypto/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { generateCommitmentFromPayloadFile } from '@joystream/js/content'
import { blake3 } from '@noble/hashes/blake3'
import { encode as encodeHash, toB58String } from 'multihashes'

// FROM Atlas 5e5f2fed Klaudiusz Dembler (2022-01-11 11:09): Giza: update content extrinsics, enable uploads (#1882)
export const hashFile = async (file: Blob): Promise<string> => {
const fileBuffer = await file.arrayBuffer()
const digest = blake3(new Uint8Array(fileBuffer))
return toB58String(encodeHash(digest, 'blake3'))
}

export const merkleRootFromBinary = (file: Blob): Promise<string> => {
// It should be `end + 1` because the second parametter of `Blob.start() is the "the first byte that will *not* be included"
// (ref: https://developer.mozilla.org/en-US/docs/Web/API/Blob/slice#end)
const read = async (start: number, end: number) => new Uint8Array(await file.slice(start, end + 1).arrayBuffer())
return generateCommitmentFromPayloadFile(read)
}
29 changes: 29 additions & 0 deletions packages/ui/src/common/utils/crypto/worker/client.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { uniqueId } from 'lodash'
import { filter, firstValueFrom, fromEvent, Observable } from 'rxjs'

import { WorkerRequest, WorkerRequestType, WorkerResponse } from './utils'

let worker: Worker
let messages: Observable<MessageEvent<WorkerResponse>>

export const hashFile = computeInWorker('HASH_FILE')

export const merkleRootFromBinary = computeInWorker('MERKLE_ROOT')

function computeInWorker(type: WorkerRequestType) {
return async (file: Blob): Promise<string> => {
if (!worker) {
worker = new Worker(new URL('./worker', import.meta.url), { type: 'module' })
messages = fromEvent<MessageEvent<WorkerResponse>>(worker, 'message')
}

const request: WorkerRequest = { type, id: uniqueId(), file }
const resultObservable = messages.pipe(filter(({ data }) => data.id === request.id))

worker.postMessage(request)

const { data } = await firstValueFrom(resultObservable)
if (data.error) throw Error(data.value)
return data.value
}
}
1 change: 1 addition & 0 deletions packages/ui/src/common/utils/crypto/worker/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './client'
24 changes: 24 additions & 0 deletions packages/ui/src/common/utils/crypto/worker/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { merkleRootFromBinary, hashFile } from '..'

export type WorkerRequestType = 'HASH_FILE' | 'MERKLE_ROOT'
export type WorkerRequest = {
type: WorkerRequestType
id: string
file: Blob
}

export type WorkerResponse = {
type: WorkerRequestType
id: string
value: string
error?: boolean
}

export const compute = async (type: WorkerRequestType, file: Blob): Promise<string> => {
switch (type) {
case 'HASH_FILE':
return await hashFile(file)
case 'MERKLE_ROOT':
return await merkleRootFromBinary(file)
}
}
10 changes: 10 additions & 0 deletions packages/ui/src/common/utils/crypto/worker/worker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { compute, WorkerRequest, WorkerResponse } from './utils'

self.onmessage = async ({ data: { type, id, file } }: MessageEvent<WorkerRequest>) => {
try {
const response: WorkerResponse = { type, id, value: await compute(type, file) }
self.postMessage(response)
} catch (error) {
self.postMessage({ type, id, error: true, value: String(error) })
}
}
7 changes: 7 additions & 0 deletions packages/ui/src/common/utils/validation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@ import { formatJoyValue } from '@/common/model/formatters'

export const BNSchema = Yup.mixed()

export const whenDefined = (key: string, schema: Yup.AnySchema) =>
Yup.mixed().when(key, {
is: undefined,
then: Yup.mixed(),
otherwise: schema,
})

/*
* Both maxContext and minContext allow you to check whether value is bigger or
* smaller than context value that has been provided to yup.
Expand Down
2 changes: 1 addition & 1 deletion packages/ui/src/council/modals/VoteForCouncil/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export { VoteForCouncilModalCall } from './types'
export type { VoteForCouncilModalCall } from './types'
export * from './VoteForCouncilModal'
10 changes: 1 addition & 9 deletions packages/ui/src/memberships/components/Avatar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ import styled from 'styled-components'

import { LeadMemberIcon } from '@/common/components/icons'
import { Tooltip } from '@/common/components/Tooltip'
import { UserImage } from '@/common/components/UserImage/UserImage'

import { AvatarPlaceholder } from '../assets/images/AvatarPlaceholder'
import { Member } from '../types'

import { AvatarImg } from '.'
import { MemberPhoto, MemberPhotoContainer } from './components'
import { AvatarStarTooltipContainer, MemberInfo } from './MemberInfo'

Expand Down Expand Up @@ -83,14 +83,6 @@ export const MemberInfoAvatar = React.memo(
}
)

export const AvatarImg = styled(UserImage)<{ isLoading?: boolean }>`
height: 100%;
width: auto;
max-width: 100%;
object-fit: cover;
display: ${({ isLoading }) => isLoading && 'none'};
`

export const AvatarPlaceholderImage = styled(AvatarPlaceholder)`
width: 100%;
height: 100%;
Expand Down
10 changes: 9 additions & 1 deletion packages/ui/src/memberships/components/components.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import styled, { css } from 'styled-components'

import { TooltipContainer } from '@/common/components/Tooltip'
import { TextInlineSmall } from '@/common/components/typography'
import { UserImage } from '@/common/components/UserImage/UserImage'

import { BorderRad, Colors, Fonts, Transitions } from '../../common/constants'

import { AvatarImg } from './Avatar'
import { MemberRoleHelp, MemberRolesWrapper, MemberStatusTooltip } from './MemberRoles'
import { MemberInfoWrapProps } from './types'

Expand Down Expand Up @@ -52,6 +52,14 @@ interface MemberPhotoProps {
big?: boolean
}

export const AvatarImg = styled(UserImage)<{ isLoading?: boolean }>`
height: 100%;
width: auto;
max-width: 100%;
object-fit: cover;
display: ${({ isLoading }) => isLoading && 'none'};
`

export const MemberPhoto = styled.div<MemberPhotoProps>`
display: flex;
position: relative;
Expand Down
28 changes: 28 additions & 0 deletions packages/ui/src/proposals/components/StorybookTemplates.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import React, { FC } from 'react'

import { Modal, ModalHeader } from '@/common/components/Modal'
import { StepperStep } from '@/common/components/Stepper'
import { StepDescriptionColumn, Stepper, StepperBody, StepperModalBody } from '@/common/components/StepperModal'
import { StepperProposalWrapper } from '@/proposals/modals/AddNewProposal'

const steps: StepperStep[] = [
{ title: 'Proposal type', type: 'past' },
{ title: 'General parameters', type: 'past' },
{ title: 'Staking account', type: 'past', isBaby: true },
{ title: 'Proposal details', type: 'past', isBaby: true },
{ title: 'Trigger & Discussion', type: 'past', isBaby: true },
{ title: 'Specific parameters', type: 'active' },
]

export const AddNewProposalTemplate: FC<{ title: string }> = ({ title, children }) => (
<Modal onClose={() => undefined} modalSize="l" modalHeight="xl">
<ModalHeader onClick={() => undefined} title={`Creating new proposal: ${title}`} />
<StepperModalBody>
<StepperProposalWrapper>
<Stepper steps={steps} />
<StepDescriptionColumn></StepDescriptionColumn>
<StepperBody>{children}</StepperBody>
</StepperProposalWrapper>
</StepperModalBody>
</Modal>
)
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ import { useModal } from '@/common/hooks/useModal'
import { SignTransactionModal } from '@/common/modals/SignTransactionModal/SignTransactionModal'
import { isLastStepActive } from '@/common/modals/utils'
import { createType } from '@/common/model/createType'
// import { createType } from '@joystream/types'
import { getMaxBlock } from '@/common/model/getMaxBlock'
import { getSteps } from '@/common/model/machines/getSteps'
import { joy } from '@/common/utils'
import { useYupValidationResolver } from '@/common/utils/validation'
import { machineStateConverter } from '@/council/modals/AnnounceCandidacy/helpers'
import { useMyMemberships } from '@/memberships/hooks/useMyMemberships'
Expand Down Expand Up @@ -68,6 +68,8 @@ export const AddNewProposalModal = () => {
const { active: activeMember } = useMyMemberships()
const minimumValidatorCount = useMinimumValidatorCount()
const maximumReferralCut = api?.consts.members.referralCutMaximumPercent
const minCashoutAllowed = joy(166, 6_666_666_660)
const maxCashoutAllowed = joy(1_666_666, 6_666_600_000)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No way to extract these from API?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not yet but it should be available soon: Joystream/joystream#4585

const currentBlock = useCurrentBlockNumber()
const { hideModal, showModal } = useModal<AddNewProposalModalCall>()
const [state, send, service] = useMachine(addNewProposalMachine)
Expand All @@ -90,6 +92,8 @@ export const AddNewProposalModal = () => {
context: {
minimumValidatorCount,
maximumReferralCut,
minCashoutAllowed,
maxCashoutAllowed,
leaderOpeningStake: workingGroupConsts?.leaderOpeningStake,
minUnstakingPeriodLimit: workingGroupConsts?.minUnstakingPeriodLimit,
stakeLock: 'Proposals',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Meta, Story } from '@storybook/react'
import React from 'react'

import { AddNewProposalTemplate } from '@/proposals/components/StorybookTemplates'

import { ChannelIncentivesPayout } from './ChannelIncentivesPayout'

export default {
title: 'Proposals/AddNewProposalModal/ChannelIncentivesPayout',
component: ChannelIncentivesPayout,
} as Meta

const Template: Story = () => (
<AddNewProposalTemplate title="Channel Update Payout">
<ChannelIncentivesPayout />
</AddNewProposalTemplate>
)

export const Default = Template.bind({})
Loading