Skip to content

Commit

Permalink
Resolve /settings/nfts and /settings/cycle conflicting `reconfigu…
Browse files Browse the repository at this point in the history
…reFundingCyclesOf` tx (#4068)

Co-authored-by: Tom Quirk <[email protected]>
  • Loading branch information
johnnyd-eth and tomquirk authored Nov 7, 2023
1 parent 72e6d24 commit 073e53c
Show file tree
Hide file tree
Showing 8 changed files with 378 additions and 40 deletions.
13 changes: 4 additions & 9 deletions src/components/announcements/ExampleFeatureAnnouncement.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { Trans, t } from '@lingui/macro'
import { JuiceModalProps } from 'components/modals/JuiceModal'
import { NewFeatureAnnouncement } from './NewFeatureAnnouncement'

Expand All @@ -11,17 +10,13 @@ export const ExampleFeatureAnnouncement = (
return (
<NewFeatureAnnouncement
{...props}
title={t`Example Feature`}
title="Example Feature"
position="topRight"
okText={t`Got it`}
okText="Got it"
hideCancelButton
>
<p>
<Trans>This is an example feature announcement.</Trans>
</p>
<p>
<Trans>Use me as a base!</Trans>
</p>
<p>This is an example feature announcement.</p>
<p>Use me as a base!</p>
</NewFeatureAnnouncement>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import { t } from '@lingui/macro'
import { AmountInCurrency } from 'components/currency/AmountInCurrency'
import { timeSecondsToDateString } from 'components/v2v3/V2V3Project/ProjectDashboard/utils/timeSecondsToDateString'
import { BigNumber } from 'ethers'
import { V2V3CurrencyOption } from 'models/v2v3/currencyOption'
import { V2V3FundingCycle } from 'models/v2v3/fundingCycle'
import { useMemo } from 'react'
import { fromWad } from 'utils/format/formatNumber'
import { formatCurrencyAmount } from 'utils/formatCurrencyAmount'
import { getBallotStrategyByAddress } from 'utils/v2v3/ballotStrategies'
import { V2V3CurrencyName } from 'utils/v2v3/currency'
import { MAX_DISTRIBUTION_LIMIT } from 'utils/v2v3/math'
import { ConfigurationPanelDatum } from '../../components/ConfigurationPanel'
import { pairToDatum } from '../../utils/pairToDatum'

export const useFormatConfigurationCyclesSection = ({
fundingCycle,
upcomingFundingCycle,
distributionLimitAmountCurrency,
upcomingDistributionLimitAmountCurrency,
}: {
fundingCycle: V2V3FundingCycle | undefined
upcomingFundingCycle?: V2V3FundingCycle | null
distributionLimitAmountCurrency:
| {
distributionLimit: BigNumber | undefined
currency: BigNumber | undefined
}
| undefined
upcomingDistributionLimitAmountCurrency?: {
distributionLimit: BigNumber | undefined
currency: BigNumber | undefined
} | null
}) => {
const durationDatum: ConfigurationPanelDatum = useMemo(() => {
const formatDuration = (duration: BigNumber | undefined) => {
if (duration === undefined) return undefined
if (duration.eq(0)) return t`Not set`
return timeSecondsToDateString(duration.toNumber(), 'short', 'lower')
}
const currentDuration = formatDuration(fundingCycle?.duration)
if (upcomingFundingCycle === null) {
return pairToDatum(t`Duration`, currentDuration, null)
}
const upcomingDuration = formatDuration(upcomingFundingCycle?.duration)

return pairToDatum(t`Duration`, currentDuration, upcomingDuration)
}, [fundingCycle?.duration, upcomingFundingCycle])

const payoutsDatum: ConfigurationPanelDatum = useMemo(() => {
const formatCurrency = (currency: BigNumber | undefined) => {
if (currency === undefined) return undefined
return currency.toNumber() as V2V3CurrencyOption
}
const formatAmountWad = (
amountWad: BigNumber | undefined,
currency: V2V3CurrencyOption | undefined,
) => {
if (amountWad === undefined) return undefined
if (amountWad.eq(MAX_DISTRIBUTION_LIMIT)) return t`Unlimited`
if (amountWad.eq(0)) return t`Zero (no payouts)`
return formatCurrencyAmount({
amount: Number(fromWad(amountWad)),
currency,
})
}
const { distributionLimit, currency } =
distributionLimitAmountCurrency ?? {}
// const currentPayout = formatAmountWad(
// distributionLimit,
// formatCurrency(currency),
// )
const currentPayout = (
<AmountInCurrency
amount={distributionLimit}
currency={V2V3CurrencyName(formatCurrency(currency))}
/>
)

if (upcomingDistributionLimitAmountCurrency === null) {
return pairToDatum(t`Payouts`, currentPayout, null)
}

const upcomingDistributionLimit =
upcomingDistributionLimitAmountCurrency?.distributionLimit !== undefined
? upcomingDistributionLimitAmountCurrency.distributionLimit
: undefined
const upcomingDistributionLimitCurrency =
upcomingDistributionLimitAmountCurrency?.currency !== undefined
? upcomingDistributionLimitAmountCurrency.currency
: undefined
const upcomingPayout = formatAmountWad(
upcomingDistributionLimit,
formatCurrency(upcomingDistributionLimitCurrency),
)

return pairToDatum(t`Payouts`, currentPayout, upcomingPayout)
}, [distributionLimitAmountCurrency, upcomingDistributionLimitAmountCurrency])

const editDeadlineDatum: ConfigurationPanelDatum = useMemo(() => {
const currentBallotStrategy = fundingCycle?.ballot
? getBallotStrategyByAddress(fundingCycle.ballot)
: undefined
const current = currentBallotStrategy?.name
if (upcomingFundingCycle === null) {
return pairToDatum(t`Edit deadline`, current, null)
}

const upcomingBallotStrategy = upcomingFundingCycle?.ballot
? getBallotStrategyByAddress(upcomingFundingCycle.ballot)
: undefined
const upcoming = upcomingBallotStrategy?.name
return pairToDatum(t`Edit deadline`, current, upcoming)
}, [fundingCycle?.ballot, upcomingFundingCycle])

return useMemo(() => {
return {
duration: durationDatum,
payouts: payoutsDatum,
editDeadline: editDeadlineDatum,
}
}, [durationDatum, editDeadlineDatum, payoutsDatum])
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { NFT_FUNDING_CYCLE_METADATA_OVERRIDES } from 'utils/nftFundingCycleMetad
import { WEIGHT_UNCHANGED, WEIGHT_ZERO } from 'utils/v2v3/fundingCycle'
import { reloadWindow } from 'utils/windowUtils'
import { EditingFundingCycleConfig } from './useEditingFundingCycleConfig'
import { useResolveEditCycleConflicts } from './useResolveEditCycleConflicts'

/**
* Return the value of the `weight` argument to send in the transaction.
Expand All @@ -41,6 +42,13 @@ const getWeightArgument = ({
return newFundingCycleWeight
}

/**
* Return a function to initiate a transaction to reconfigure a project's funding cycle.
*
* @dev Used in two places:
* 1. Edit cycle form
* 2. NFT page - edit dataSource-related attributes of the cycle
*/
export const useReconfigureFundingCycle = ({
editingFundingCycleConfig,
memo,
Expand All @@ -65,6 +73,7 @@ export const useReconfigureFundingCycle = ({
const reconfigureV2V3FundingCycleTx = useReconfigureV2V3FundingCycleTx()
const reconfigureV2V3FundingCycleWithNftsTx =
useReconfigureV2V3FundingCycleWithNftsTx()
const resolveEditCycleConflicts = useResolveEditCycleConflicts()

// If given a latestEditingData, will use that. Else, will use redux store
const reconfigureFundingCycle = useCallback(
Expand Down Expand Up @@ -105,20 +114,22 @@ export const useReconfigureFundingCycle = ({
newFundingCycleWeight: editingFundingCycleData.weight,
})

const reconfigureFundingCycleData: ReconfigureFundingCycleTxParams = {
fundingCycleData: {
...editingFundingCycleData,
weight,
},
fundingCycleMetadata,
fundAccessConstraints: editingFundAccessConstraints,
groupedSplits: [
editingPayoutGroupedSplits,
editingReservedTokensGroupedSplits,
],
memo,
mustStartAtOrAfter: editingMustStartAtOrAfter,
}
const reconfigureFundingCycleData: ReconfigureFundingCycleTxParams =
resolveEditCycleConflicts({
fundingCycleData: {
...editingFundingCycleData,
weight,
},
fundingCycleMetadata,
fundAccessConstraints: editingFundAccessConstraints,
groupedSplits: [
editingPayoutGroupedSplits,
editingReservedTokensGroupedSplits,
],
memo,
mustStartAtOrAfter: editingMustStartAtOrAfter,
launchedNewNfts,
})

const txOpts = {
onDone() {
Expand Down Expand Up @@ -177,6 +188,7 @@ export const useReconfigureFundingCycle = ({
memo,
onComplete,
projectId,
resolveEditCycleConflicts,
],
)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { ProjectMetadataContext } from 'contexts/shared/ProjectMetadataContext'
import { V2V3ProjectContext } from 'contexts/v2v3/Project/V2V3ProjectContext'
import useProjectQueuedFundingCycle from 'hooks/v2v3/contractReader/useProjectQueuedFundingCycle'
import { ReconfigureFundingCycleTxParams } from 'hooks/v2v3/transactor/useReconfigureV2V3FundingCycleTx'
import {
V2V3FundingCycleData,
V2V3FundingCycleMetadata,
} from 'models/v2v3/fundingCycle'
import { useContext } from 'react'

/**
* Determines if a "Launch NFTs" (NftDeployer.reconfigureFundingCycleOf) has been called
* in the same cycle as an "Edit cycle" (reconfigureFundingCycleOf) tx.
*
* If so, we need to pass the new delegate and other NFT-related data into the subsequent
* "Edit cycle" tx so they are not overriden and lost.
*
* @param {V2V3FundingCycleMetadata} currentFcMetadata - The current funding cycle metadata.
* @param {V2V3FundingCycleMetadata} queuedFcMetadata - The queued funding cycle metadata.
* @returns {boolean} - Whether a conflict exists or not.
*/
function hasNftConflict(
currentFcMetadata: V2V3FundingCycleMetadata,
queuedFcMetadata: V2V3FundingCycleMetadata,
): boolean {
// if the queued cycle's NFT data has any changes to the current cycle, return true.
const useDataSourceForPayHasDiff =
queuedFcMetadata.useDataSourceForPay !==
currentFcMetadata.useDataSourceForPay
const useDataSourceForRedeemHasDiff =
queuedFcMetadata.useDataSourceForRedeem !==
currentFcMetadata.useDataSourceForRedeem
const useDataSourceHasDiff =
queuedFcMetadata.dataSource !== currentFcMetadata.dataSource

return (
useDataSourceForPayHasDiff ||
useDataSourceForRedeemHasDiff ||
useDataSourceHasDiff
)
}

/**
*
* @returns
*/
export const useResolveEditCycleConflicts = () => {
const { projectId } = useContext(ProjectMetadataContext)
const { fundingCycleMetadata } = useContext(V2V3ProjectContext)

const { data: queuedCycle } = useProjectQueuedFundingCycle({ projectId })

// If no queued cycle, no resolving needs to be done. Return current data
if (!queuedCycle || !fundingCycleMetadata) {
return (data: ReconfigureFundingCycleTxParams) => data
}

const queuedFcData: V2V3FundingCycleData = queuedCycle[0]
const queuedFcMetadata: V2V3FundingCycleMetadata = queuedCycle[1]

return (
data: ReconfigureFundingCycleTxParams & { launchedNewNfts?: boolean },
) => {
// Calling from "Edit cycle" and an NFT tx has be called same cycle: pass that previously queued NFT data into this "Edit cycle" tx
if (hasNftConflict(fundingCycleMetadata, queuedFcMetadata)) {
return {
...data,
fundingCycleMetadata: {
...data.fundingCycleMetadata,
useDataSourceForPay: queuedFcMetadata.useDataSourceForPay,
useDataSourceForRedeem: queuedFcMetadata.useDataSourceForRedeem,
dataSource: queuedFcMetadata.dataSource,
},
}
}
// Calling from "Launch NFTs" and an "Edit cycle" tx has be called same cycle: pass that previously queued data into this "Launch NFTs" tx
if (data.launchedNewNfts) {
return {
...data,
fundingCycleMetadata: {
...data.fundingCycleMetadata,
...queuedFcMetadata,
},
fundingCycleData: {
...data.fundingCycleData,
...queuedFcData,
},
}
// Calling "Edit cycle" when a previously "Edit cycle" tx exists: ignore the old tx data, return the new one
} else return data
}
}
Loading

1 comment on commit 073e53c

@vercel
Copy link

@vercel vercel bot commented on 073e53c Nov 7, 2023

Choose a reason for hiding this comment

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

Please sign in to comment.