Skip to content

Commit

Permalink
feat: Vesting & updates for enacted bids (#1630)
Browse files Browse the repository at this point in the history
* bid vestings

* feat: use isProject function and update notification texts

* chore: update notification body text

---------

Co-authored-by: Andy Espagnolo <[email protected]>
  • Loading branch information
ncomerci and andyesp authored Feb 5, 2024
1 parent 20939c4 commit 64089bb
Show file tree
Hide file tree
Showing 8 changed files with 34 additions and 27 deletions.
11 changes: 6 additions & 5 deletions src/back/routes/proposal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ import {
isAlreadyACatalyst,
isAlreadyBannedName,
isAlreadyPointOfInterest,
isProjectProposal,
isValidName,
isValidPointOfInterest,
isValidUpdateProposalStatus,
Expand Down Expand Up @@ -538,15 +539,15 @@ export async function updateProposalStatus(req: WithAuth<Request<{ proposal: str
updated_at: new Date(),
}

const isProject = isProjectProposal(proposal.type)
const isEnactedStatus = update.status === ProposalStatus.Enacted
const isGrantProposal = proposal.type === ProposalType.Grant
if (isEnactedStatus) {
update.enacted = true
update.enacted_by = user
if (isGrantProposal) {
if (isProject) {
const { vesting_addresses } = configuration
if (!vesting_addresses || vesting_addresses.length === 0) {
throw new RequestError('Vesting addresses are required for grant proposals', RequestError.BadRequest)
throw new RequestError('Vesting addresses are required for grant or bid proposals', RequestError.BadRequest)
}
if (vesting_addresses.some((address) => !isEthereumAddress(address))) {
throw new RequestError('Some vesting address is invalid', RequestError.BadRequest)
Expand All @@ -571,8 +572,8 @@ export async function updateProposalStatus(req: WithAuth<Request<{ proposal: str
}

await ProposalModel.update<ProposalAttributes>(update, { id })
if (isEnactedStatus && isGrantProposal) {
NotificationService.grantProposalEnacted(proposal)
if (isEnactedStatus && isProject) {
NotificationService.projectProposalEnacted(proposal)
}

const updatedProposal = await ProposalModel.findOne<ProposalAttributes>({
Expand Down
6 changes: 3 additions & 3 deletions src/back/services/notification.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ export class NotificationService {
}
}

static grantProposalEnacted(proposal: ProposalAttributes) {
static projectProposalEnacted(proposal: ProposalAttributes) {
inBackground(async () => {
try {
const coauthors = await CoauthorService.getAllFromProposalId(proposal.id)
Expand All @@ -134,8 +134,8 @@ export class NotificationService {
throw new Error('Invalid addresses')
}

const title = Notifications.GrantEnacted.title
const body = Notifications.GrantEnacted.body
const title = Notifications.ProjectEnacted.title
const body = Notifications.ProjectEnacted.body

const validatedUsers = await UserModel.getActiveDiscordIds(addresses)
for (const user of validatedUsers) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ import Icon from 'semantic-ui-react/dist/commonjs/elements/Icon'
import isEthereumAddress from 'validator/lib/isEthereumAddress'

import { Governance } from '../../../clients/Governance'
import { ProposalAttributes, ProposalStatus, ProposalType } from '../../../entities/Proposal/types'
import { ProposalAttributes, ProposalStatus } from '../../../entities/Proposal/types'
import { isProjectProposal } from '../../../entities/Proposal/utils'
import { validateUniqueAddresses } from '../../../entities/Transparency/utils'
import useFormatMessage from '../../../hooks/useFormatMessage'
import Label from '../../Common/Typography/Label'
Expand Down Expand Up @@ -86,8 +87,8 @@ export function UpdateProposalStatusModal({

const values = useWatch({ control })

const isGrantProposal = proposal?.type === ProposalType.Grant
const showAddButton = isGrantProposal && !!vesting_addresses && vesting_addresses.length > 0
const isProject = isProjectProposal(proposal?.type)
const showAddButton = isProject && !!vesting_addresses && vesting_addresses.length > 0
const hasValues = values.vestingAddresses && values.vestingAddresses.filter((value) => value.length > 0).length > 0

useEffect(() => {
Expand Down Expand Up @@ -136,7 +137,7 @@ export function UpdateProposalStatusModal({
const updateProposal = await Governance.get().updateProposalStatus(
proposal.id,
status,
isGrantProposal ? filteredVestingContracts : undefined
isProject ? filteredVestingContracts : undefined
)
onClose()
return updateProposal
Expand Down Expand Up @@ -182,7 +183,7 @@ export function UpdateProposalStatusModal({
<Markdown size="lg">{t('modal.update_status_proposal.description', { status }) || ''}</Markdown>
</div>

{proposal && isGrantProposal && (
{proposal && isProject && (
<div className="ProposalModal__GrantTransaction">
<Label>{t('modal.update_status_proposal.grant_vesting_addresses')}</Label>
{values.vestingAddresses?.map((address, idx) => (
Expand Down Expand Up @@ -226,7 +227,7 @@ export function UpdateProposalStatusModal({
type="submit"
primary
fluid
disabled={isSubmitting || (isGrantProposal && !hasValues)}
disabled={isSubmitting || (isProject && !hasValues)}
loading={loading && isSubmitting}
>
{t(getPrimaryButtonTextKey(status, showAddButton))}
Expand Down
5 changes: 3 additions & 2 deletions src/components/Proposal/ProposalSidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import useAuthContext from 'decentraland-gatsby/dist/context/Auth/useAuthContext
import useTrackContext from 'decentraland-gatsby/dist/context/Track/useTrackContext'

import { SegmentEvent } from '../../entities/Events/types'
import { ProposalAttributes, ProposalStatus, ProposalType } from '../../entities/Proposal/types'
import { ProposalAttributes, ProposalStatus } from '../../entities/Proposal/types'
import { isProjectProposal } from '../../entities/Proposal/utils'
import { SubscriptionAttributes } from '../../entities/Subscription/types'
import { Survey } from '../../entities/SurveyTopic/types'
import { UpdateAttributes } from '../../entities/Updates/types'
Expand Down Expand Up @@ -112,7 +113,7 @@ export default function ProposalSidebar({
const showProposalUpdatesActions =
proposal &&
isProposalStatusWithUpdates(proposal?.status) &&
proposal?.type === ProposalType.Grant &&
isProjectProposal(proposal?.type) &&
(isOwner || isCoauthor)
const showProposalThresholdsSummary = !!(
proposal &&
Expand Down
8 changes: 2 additions & 6 deletions src/components/Proposal/Update/ProposalUpdates.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { ProposalAttributes, ProposalType } from '../../../entities/Proposal/types'
import { ProposalAttributes } from '../../../entities/Proposal/types'
import { UpdateAttributes } from '../../../entities/Updates/types'
import { isProposalStatusWithUpdates } from '../../../entities/Updates/utils'
import useFormatMessage from '../../../hooks/useFormatMessage'
import Empty from '../../Common/Empty'
import Megaphone from '../../Icon/Megaphone'
Expand All @@ -19,10 +18,7 @@ interface Props {
export default function ProposalUpdates({ proposal, updates, isCoauthor, onUpdateDeleted }: Props) {
const t = useFormatMessage()

const showProposalUpdates =
updates && isProposalStatusWithUpdates(proposal?.status) && proposal?.type === ProposalType.Grant

if (!updates || !proposal || !showProposalUpdates) {
if (!updates || !proposal) {
return null
}

Expand Down
4 changes: 4 additions & 0 deletions src/entities/Proposal/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -312,3 +312,7 @@ export function getDisplayedPriorityProposals(
return showTheProposal
})
}

export function isProjectProposal(proposalType?: ProposalType) {
return proposalType === ProposalType.Grant || proposalType === ProposalType.Bid
}
8 changes: 6 additions & 2 deletions src/pages/proposal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,11 @@ import ProposalImagesPreview from '../components/Proposal/View/ProposalImagesPre
import ProposalMarkdown from '../components/Proposal/View/ProposalMarkdown'
import { OldGrantCategory } from '../entities/Grant/types'
import { ProposalAttributes, ProposalStatus, ProposalType } from '../entities/Proposal/types'
import { isBiddingAndTenderingProposal, isGovernanceProcessProposal } from '../entities/Proposal/utils'
import {
isBiddingAndTenderingProposal,
isGovernanceProcessProposal,
isProjectProposal,
} from '../entities/Proposal/utils'
import { Survey } from '../entities/SurveyTopic/types'
import { SurveyEncoder } from '../entities/SurveyTopic/utils'
import { isProposalStatusWithUpdates } from '../entities/Updates/utils'
Expand Down Expand Up @@ -165,7 +169,7 @@ export default function ProposalPage() {

const { publicUpdates, pendingUpdates, nextUpdate, currentUpdate, refetchUpdates } = useProposalUpdates(proposal?.id)
const showProposalUpdates =
publicUpdates && isProposalStatusWithUpdates(proposal?.status) && proposal?.type === ProposalType.Grant
publicUpdates && isProposalStatusWithUpdates(proposal?.status) && isProjectProposal(proposal?.type)

const { surveyTopics, isLoadingSurveyTopics, voteWithSurvey, showSurveyResults } = useSurvey(
proposal,
Expand Down
6 changes: 3 additions & 3 deletions src/utils/notifications.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ export const Notifications = {
title: 'Co-author Request Received',
body: "You've been invited to collaborate as a co-author on a published proposal. Accept it or reject it here",
},
GrantEnacted: {
title: 'Grant Proposal Enacted',
body: 'Congratulations! Your Grant Proposal has been successfully enacted and a Vesting Contract was added',
ProjectEnacted: {
title: 'Your Project has been funded',
body: 'Congratulations! Your Project has been successfully enacted and a funding Vesting Contract was created',
},
}

0 comments on commit 64089bb

Please sign in to comment.